Skip to content

feat(fs-http): timeout option with 30000ms default (Doctrine #8 library-author extension)#60

Merged
jasperboerhof merged 5 commits intomainfrom
armorer/fs-http-timeout-surface
May 1, 2026
Merged

feat(fs-http): timeout option with 30000ms default (Doctrine #8 library-author extension)#60
jasperboerhof merged 5 commits intomainfrom
armorer/fs-http-timeout-surface

Conversation

@Goosterhof
Copy link
Copy Markdown
Contributor

@Goosterhof Goosterhof commented Apr 30, 2026

Summary

Equips @script-development/fs-http with a compliant timeout surface per the war-room Doctrine #8 library-author extension (CLAUDE.md, 2026-04-22). Closes the 3-spy convergent finding 2026-04-30 (Quartermaster M3 F-1 / Sapper M2 M3 / Liaison M2) that fs-http exposes no timeout option, allowing axios's no-timeout default to silently propagate to all 5 consumer territories (0 of 26 import sites configure AxiosRequestConfig.timeout).

  • Adds timeout?: number to HttpServiceOptions. Default 30000ms (30s) applies when unset.
  • Override service-wide via createHttpService(url, {timeout: 5_000}). Per-call override remains via existing AxiosRequestConfig.timeout parameter on each method.
  • Pass timeout: 0 to disable (consumer explicitly accepts Doctrine feat: add @script-development/fs-adapter-store package #8 responsibility, e.g. AI streaming endpoints with their own timeout discipline).
  • Exports DEFAULT_TIMEOUT_MS constant from the package barrel for consumer reference.
  • Bumps fs-http 0.1.3 → 0.2.0 (minor — non-breaking type addition, but observable behavior change on previously-unset timeouts).

Consumer-package coordination (2026-04-30)

The 0.1.3 → 0.2.0 bump crosses the pre-1.0 minor boundary. Two intra-workspace consumers had "@script-development/fs-http": "^0.1.0" declared in dev + peer deps, which doesn't satisfy 0.2.0:

  • packages/adapter-store/package.json — bumped 0.1.4 → 0.1.5, peer range widened to ^0.1.0 || ^0.2.0.
  • packages/loading/package.json — bumped 0.1.0 → 0.1.1, peer range widened to ^0.1.0 || ^0.2.0.

Without this widening, npm ci would still pass but the workspace would be silently incoherent: npm would pull published fs-http@0.1.3 into nested node_modules/ for adapter-store and loading, so those packages would test/build against the old fs-http while the workspace fs-http was at 0.2.0. The widening keeps all three fs-http references resolving to the workspace copy.

Doctrine reference

CLAUDE.md, Architectural Principle #8 (library-author extension, 2026-04-22):

Shared HTTP factory packages (e.g., @script-development/fs-http) must expose a compliant timeout surface: a default, a required option, or a documented contract plus consumer-level enforcement. Inheriting framework defaults at the library layer silently propagates the violation to every consumer territory.

Commander disposition (2026-04-30): default with options-param override — lowest-friction option that brings 5 consumer territories into doctrine compliance immediately.

Mutation discipline

The ??|| operator-swap mutation on options?.timeout ?? DEFAULT_TIMEOUT_MS is killed by the explicit timeout: 0 test (a 0 left of ?? is honored, but a 0 left of || would coerce back to the default). fs-http mutation score after change: 95.74% (90 killed, 4 pre-existing survivors unrelated to timeout).

CI gates

All 8 gates green locally:

  • audit (0 vulnerabilities)
  • format:check (oxfmt clean)
  • lint (oxlint, 0 errors)
  • build (tsdown ESM+CJS)
  • typecheck (tsc --noEmit)
  • lint:pkg (publint+attw, 0 problems)
  • test:coverage (430/430 tests, per-package thresholds met)
  • test:mutation (95.74% ≥ 90% threshold — verified pre-coordination commit)

Out of scope

  • Consumer territory upgrades. This PR ships fs-http only. Consumer territories receive the new behavior on next dependency upgrade; roll-out is a separate post-publish campaign.
  • Per-call timeout aliases. axios's AxiosRequestConfig.timeout already works through existing method signatures.
  • Other Doctrine feat: add @script-development/fs-adapter-store package #8 surfaces. fs-router and other packages are not in scope.

Test plan

  • Default test: factory with no options → axios defaults.timeout === 30000
  • Override test: {timeout: 5_000}defaults.timeout === 5000
  • Disable test: {timeout: 0}defaults.timeout === 0 (no coercion to default — kills ??|| mutation)
  • All 8 CI gates pass
  • Workspace lock resolves fs-http @ 0.2.0 for all three consuming packages (no nested registry copies)
  • Trusted Publishing OIDC pipeline publishes 0.2.0 (fs-http), 0.1.5 (adapter-store), 0.1.1 (loading) on merge

Refs

  • Campaign: campaigns/fs-packages/2026-04-30-multi-spy-refresh-wave.md
  • Convergent field reports: reports/fs-packages/field/2026-04-30-{quartermaster-m3,sapper-m2,liaison-m2}-staleness-refresh.md
  • Deployment order: orders/fs-packages/fs-http-timeout-surface-armorer-deployment.md
  • Doctrine: CLAUDE.md Architectural Principles feat: add @script-development/fs-adapter-store package #8 (library-author extension, 2026-04-22)

Goosterhof and others added 2 commits April 30, 2026 13:32
…ibrary-author extension)

Closes 3-spy convergent finding 2026-04-30 on fs-http timeout surface absence.
Default 30000ms applies when unset; override via options.timeout or per-call
AxiosRequestConfig. Pass timeout: 0 to disable (consumer accepts Doctrine #8).

Bumps fs-http to 0.2.0 (minor — observable behavior change on previously-unset
timeouts).

Refs: campaigns/fs-packages/2026-04-30-multi-spy-refresh-wave.md
Completes the fs-http 0.1.3 → 0.2.0 bump from 8f764bd. Both consumer
packages declared `"@script-development/fs-http": "^0.1.0"` in dev +
peer deps; pre-1.0 caret only matches same-minor, so 0.2.0 fell
outside the workspace constraint and npm pulled published 0.1.3 into
nested node_modules. Result: `npm ci` could pass while adapter-store
and loading were silently testing against the old fs-http.

Widens both ranges to `^0.1.0 || ^0.2.0` (peer-range widening is
monotonically backward-compatible). Bumps adapter-store 0.1.4 → 0.1.5
and loading 0.1.0 → 0.1.1 for observable metadata change.

Lock now resolves all three fs-http references to the workspace
copy at 0.2.0; nested registry copies removed.

Verified locally: build / typecheck / format / lint / lint:pkg /
coverage (430/430) all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 30, 2026

Deploying fs-packages with  Cloudflare Pages  Cloudflare Pages

Latest commit: e7bf591
Status: ✅  Deploy successful!
Preview URL: https://2aecf2ce.fs-packages.pages.dev
Branch Preview URL: https://armorer-fs-http-timeout-surf.fs-packages.pages.dev

View logs

jasperboerhof
jasperboerhof previously approved these changes Apr 30, 2026
Copy link
Copy Markdown
Contributor

@jasperboerhof jasperboerhof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Goosterhof nog ff merge conflicts oplossen :)

@jasperboerhof jasperboerhof merged commit 8c457ee into main May 1, 2026
2 checks passed
@jasperboerhof jasperboerhof deleted the armorer/fs-http-timeout-surface branch May 1, 2026 08:56
Goosterhof added a commit that referenced this pull request May 4, 2026
Closes #59. fs-http should be HTTP transport, not browser download
UX. The previous downloadRequest/previewRequest methods constructed
Blobs, created `<a>` elements, dispatched clicks, and managed object
URL lifecycles — coupling that bled into every consumer's tests as
fragile global stubs (vi.stubGlobal of `Blob`/`document`/`window.URL`),
exposed to vitest 4's class-mock requirement and oxfmt's arrow-function
collapse. Three territories independently rediscovered the mitigation.

Reshape both methods to pure transport: `(endpoint, options?) →
Promise<AxiosResponse<Blob>>`. The DOM-side download dance moves to
`@script-development/fs-helpers` ≥ 0.1.2 as `triggerDownload(blob,
filename)`. Object-URL lifecycle for previews is now the consumer's
concern (one `URL.createObjectURL` line, plus revocation on cleanup).

BREAKING CHANGES (fs-http 0.2.0 → 0.3.0):

- downloadRequest(endpoint, documentName, type?) → AxiosResponse
  becomes downloadRequest(endpoint, options?) → AxiosResponse<Blob>.
- previewRequest(endpoint) → string (object URL)
  becomes previewRequest(endpoint, options?) → AxiosResponse<Blob>.
- Removed HEADERS_TO_TYPE map (one-entry OOXML lookup, did not earn
  its place in transport-layer code; consumers can supply their own
  if needed).

Cascade workspace bumps to keep the lock coherent:

- fs-helpers 0.1.1 → 0.1.2 (additive: triggerDownload export +
  happy-dom devDep).
- fs-adapter-store 0.1.5 → 0.1.6 (peer range widened to include
  ^0.3.0 — not a behavior change; doesn't depend on download/preview).
- fs-loading 0.1.1 → 0.1.2 (same widening; same rationale).

Out of scope (separate dispositions):

- streamRequest's `document.cookie` XSRF read remains. It's the only
  remaining DOM touch in fs-http; cleanup would require either a
  required `xsrfHeader` option or a cookie-reader middleware shipped
  alongside, both bigger migrations than #59 covers.
- Consumer territory upgrades. Land this PR after #60, then run a
  post-publish migration campaign across kendo, ublgenie, emmie,
  entreezuil, and BIO (5 download/preview call sites in production
  code; ~50 in test mocks).

Verified locally:

- 19 test files / 430 tests pass (was 18/430; +2 fs-helpers, -2 net
  fs-http after dropping obsolete DOM-orchestration tests).
- 100% coverage maintained on fs-http and fs-helpers.
- mutation: fs-http 97.30% (up from 95.74%; less surface to mutate),
  fs-helpers 100% (including the new dom-download.ts).
- All 8 CI gates locally green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Goosterhof added a commit that referenced this pull request May 4, 2026
Closes #59. fs-http should be HTTP transport, not browser download
UX. The previous downloadRequest/previewRequest methods constructed
Blobs, created `<a>` elements, dispatched clicks, and managed object
URL lifecycles — coupling that bled into every consumer's tests as
fragile global stubs (vi.stubGlobal of `Blob`/`document`/`window.URL`),
exposed to vitest 4's class-mock requirement and oxfmt's arrow-function
collapse. Three territories independently rediscovered the mitigation.

Reshape both methods to pure transport: `(endpoint, options?) →
Promise<AxiosResponse<Blob>>`. The DOM-side download dance moves to
`@script-development/fs-helpers` ≥ 0.1.2 as `triggerDownload(blob,
filename)`. Object-URL lifecycle for previews is now the consumer's
concern (one `URL.createObjectURL` line, plus revocation on cleanup).

BREAKING CHANGES (fs-http 0.2.0 → 0.3.0):

- downloadRequest(endpoint, documentName, type?) → AxiosResponse
  becomes downloadRequest(endpoint, options?) → AxiosResponse<Blob>.
- previewRequest(endpoint) → string (object URL)
  becomes previewRequest(endpoint, options?) → AxiosResponse<Blob>.
- Removed HEADERS_TO_TYPE map (one-entry OOXML lookup, did not earn
  its place in transport-layer code; consumers can supply their own
  if needed).

Cascade workspace bumps to keep the lock coherent:

- fs-helpers 0.1.1 → 0.1.2 (additive: triggerDownload export +
  happy-dom devDep).
- fs-adapter-store 0.1.5 → 0.1.6 (peer range widened to include
  ^0.3.0 — not a behavior change; doesn't depend on download/preview).
- fs-loading 0.1.1 → 0.1.2 (same widening; same rationale).

Out of scope (separate dispositions):

- streamRequest's `document.cookie` XSRF read remains. It's the only
  remaining DOM touch in fs-http; cleanup would require either a
  required `xsrfHeader` option or a cookie-reader middleware shipped
  alongside, both bigger migrations than #59 covers.
- Consumer territory upgrades. Land this PR after #60, then run a
  post-publish migration campaign across kendo, ublgenie, emmie,
  entreezuil, and BIO (5 download/preview call sites in production
  code; ~50 in test mocks).

Verified locally:

- 19 test files / 430 tests pass (was 18/430; +2 fs-helpers, -2 net
  fs-http after dropping obsolete DOM-orchestration tests).
- 100% coverage maintained on fs-http and fs-helpers.
- mutation: fs-http 97.30% (up from 95.74%; less surface to mutate),
  fs-helpers 100% (including the new dom-download.ts).
- All 8 CI gates locally green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants