Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Consumer territories must apply per-call timeouts at instantiation OR rely on th
- **Test environment:** Browser-dependent tests use `// @vitest-environment happy-dom` file-level comments.
- **Identical build config:** All packages share the same `tsdown.config.ts` structure.
- **No direct axios imports in dependent packages.** Route `AxiosResponse` / `AxiosRequestConfig` / sibling types through `fs-http`'s re-exports (e.g. `Parameters<ResponseMiddlewareFunc>[0]` for response types). Direct `import type {AxiosResponse} from 'axios'` breaks rolldown's `d.cts` emission on dual-bundle packages — caught during `fs-cached-adapter-store` scaffold 2026-05-13.
- **No top-level side effects.** Every published package declares `"sideEffects": false` in its `package.json` so bundlers can tree-shake under deep imports. The factory + barrel pattern ensures this is structurally true — every package's `src/index.ts` is either a pure re-export or a file whose top-level statements are imports, type declarations, and `const`/`function` factory declarations. The manifest entry makes it explicit and bundler-actionable. Closes enforcement queue #70 (publint 0.3.21 Suggestion, fatal-promoted by `scripts/lint-pkg.mjs`).
- **Transport-surface discipline.** Every `fs-http` transport method must inherit option-honoring from the `axios.create()` instance. Adding a new transport path that uses native `fetch` (or any non-axios transport) requires a deliberate audit against the full `HttpServiceOptions` matrix — `headers`, `withCredentials`, `withXSRFToken`, `smartCredentials`, `timeout`, plus the per-call `AxiosRequestConfig` override surface. The Library-Config-Honor Surface Audit (Sapper M3 + Surveyor M3, 2026-05-15) is the standing checklist. The pre-1.0 `streamRequest` function violated this rule on four axes (queue #22 streamRequest portion + queue #64 XSRF + Surveyor M3 F-1 headers + F-2 timeout) and was removed in 0.4.0 with zero realized consumer impact. If a future streaming use case emerges, the right design is either axios's `responseType: 'stream'` mode via the standard methods (inherits all options for free) or a deliberate `createStreamHttpService` factory designed against the option-honoring matrix from the start — not a re-add of an axios-bypassing transport.

### Internal Dependency Coordination
Expand Down
1 change: 1 addition & 0 deletions packages/adapter-store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/cached-adapter-store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/dialog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/http/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/loading/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/storage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/toast/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
1 change: 1 addition & 0 deletions packages/translation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dist"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
Expand Down
45 changes: 38 additions & 7 deletions scripts/lint-pkg.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@
// 1. publint + attw — treats publint suggestions/warnings/errors as fatal.
// publint 0.3.18 CLI does not expose a flag to fail on suggestions
// (--strict only promotes warnings → errors). This wrapper fills that gap:
// it runs publint per workspace, captures stdout, and fails the gate if any
// package emits a "Suggestions:", "Warnings:", or "Errors:" block.
// attw --pack runs after publint per package and preserves its own exit code.
// it runs publint per workspace, captures stdout, strips ANSI SGR codes,
// and fails the gate if any package emits a "Suggestions:", "Warnings:", or
// "Errors:" block. attw --pack runs after publint per package and preserves
// its own exit code.
// Motivated by enforcement queue #33 and the PR #35 regression: publint
// suggestions about the "git+" URL prefix silently re-drifted across 10
// packages because the gate tolerated them.
//
// ANSI invariance (enforcement queue #63): publint colors its block headers
// when it detects a color-capable environment (TTY or FORCE_COLOR). In CI,
// publint emitted ANSI-wrapped headers (e.g. "\x1b[1m\x1b[34mSuggestions:\x1b[39m\x1b[22m"),
// so PUBLINT_BLOCK_RE — anchored on a bare "Suggestions:" line — never matched
// and the gate silently no-op'd (false-NEGATIVE: a real Warning/Error block
// would have sailed through CI undetected). Verified against raw CI logs:
// the gate had been a no-op in CI since publint 0.3.21 landed 2026-05-11,
// while locally (plain-text, non-TTY) the regex matched and the gate fired
// correctly. Fix: spawn publint with NO_COLOR=1 AND strip residual ANSI from
// captured stdout before the regex match — belt-and-suspenders so the verdict
// is identical in every color environment (plain, TTY, FORCE_COLOR=1).
//
// 2. engines.node presence — closes enforcement queue #31 (drift-prevention
// gate, deployed 2026-05-12). Every workspace package.json AND the root
// package.json must declare a non-empty `engines.node` string. Value is NOT
Expand All @@ -27,6 +40,15 @@ import {join} from 'node:path';
const PACKAGES_DIR = 'packages';
const ROOT_MANIFEST = 'package.json';
const PUBLINT_BLOCK_RE = /^(Suggestions|Warnings|Errors):$/m;
// SGR / ANSI escape sequences (CSI ... final-byte). publint wraps its block
// headers in these when color is enabled (CI default, FORCE_COLOR), which
// otherwise defeats PUBLINT_BLOCK_RE's bare-line anchors. See header note,
// enforcement queue #63.
const ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*[a-zA-Z]`, 'g');

function stripAnsi(text) {
return text.replace(ANSI_RE, '');
}

function listPackageDirs() {
return readdirSync(PACKAGES_DIR)
Expand Down Expand Up @@ -61,8 +83,14 @@ function checkEnginesNode(manifestPath, label) {
return null;
}

function runCaptured(cmd, args, cwd) {
const result = spawnSync(cmd, args, {cwd, stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8', shell: false});
function runCaptured(cmd, args, cwd, extraEnv) {
const result = spawnSync(cmd, args, {
cwd,
stdio: ['ignore', 'pipe', 'pipe'],
encoding: 'utf8',
shell: false,
env: extraEnv ? {...process.env, ...extraEnv} : process.env,
});
const stdout = result.stdout ?? '';
const stderr = result.stderr ?? '';
process.stdout.write(stdout);
Expand Down Expand Up @@ -95,8 +123,11 @@ function main() {
process.stderr.write(` ${enginesFailure}\n`);
}

const publint = runCaptured('npx', ['publint', 'run'], dir);
const publintBlock = PUBLINT_BLOCK_RE.exec(publint.stdout);
// NO_COLOR=1 keeps publint's output plain regardless of runner color
// settings; stripAnsi defends against any residual SGR codes so the
// PUBLINT_BLOCK_RE verdict is identical in every environment (queue #63).
const publint = runCaptured('npx', ['publint', 'run'], dir, {NO_COLOR: '1'});
const publintBlock = PUBLINT_BLOCK_RE.exec(stripAnsi(publint.stdout));
if (publint.status !== 0) {
failures.push(`${name}: publint exited ${publint.status}`);
} else if (publintBlock) {
Expand Down