git:20260924.71dc2fb to git:20260924.b36c6c8

45 added, 14 removed. Audit A to A.

---
name: fetcher-integration
description: >
- Set up the core `@ahoo-wang/fetcher` HTTP client with `Fetcher` or `NamedFetcher`, interceptors, URL parameters, timeouts, result extractors, cancellation, status validation, and the named registry. Use for direct Fetcher calls or request-lifecycle behavior.
+ Set up and call the core `@ahoo-wang/fetcher` HTTP client: `Fetcher`/`NamedFetcher`, baseURL, timeouts, path and query params, request/response/error interceptors, status validation, result extractors, cancellation and the named registry. Use for "Axios-like" client setup or request-pipeline bugs. Not for service classes with `@api`/`@get`/`@post` decorators (fetcher-decorator-service) or React state (fetcher-react-hooks).
---
# fetcher-integration
- ## Workflow
+ ## Decisions
- 1. Start with `@ahoo-wang/fetcher` before reaching for higher-level packages.
- 2. Prefer `NamedFetcher` when the client will be reused by decorators, generated clients, or app services.
- 3. Mutate `FetchExchange` inside interceptors; do not return a replacement exchange from `intercept()`.
- 4. Use `ResultExtractors` when callers need typed values instead of raw `Response` objects.
- 5. Load `references/api.md` for exact method signatures, lifecycle order, or full examples.
+ - **`NamedFetcher` vs `Fetcher`**: use `NamedFetcher('name', options)` whenever decorators, CoSec or hooks will look the client up by name; it registers itself in `fetcherRegistrar` (a reused name silently replaces the earlier one). `fetcherRegistrar.default` throws if nothing is named `'default'`.
+ - **What a call returns**: `get/post/put/patch/delete/fetch` default to `ResultExtractors.Response`; `request()` defaults to `ResultExtractors.Exchange`. Pass `{ resultExtractor: ResultExtractors.Json }` as the **third** argument for typed data.
+ - **Where cross-cutting logic goes**: an interceptor, not a wrapper function — auth, tracing, retries and error recovery all belong in `fetcher.interceptors.request/response/error`.
- ## Key Practices
+ ## Gotchas a capable model gets wrong
- - Keep interceptors small and phase-specific: request shaping, response validation, and error recovery are separate responsibilities.
- - Use `urlParams.path` for template variables and `urlParams.query` for query strings.
- - Use `validateStatus` or `IGNORE_VALIDATE_STATUS` intentionally when non-2xx responses are part of the domain flow.
+ - `intercept(exchange)` **mutates** the exchange and returns nothing; `use()` returns `false` and ignores an interceptor whose `name` is already registered.
+ - `FetchRequest.headers` is optional: write headers with `setHeader(exchange.ensureRequestHeaders(), 'Authorization', value)`, not `exchange.request.headers.X = …`.
+ - Built-in order: `REQUEST_BODY_INTERCEPTOR_ORDER` (body → JSON) runs near `Number.MIN_SAFE_INTEGER`, so an object body assigned by a user interceptor (default order 0) is **not** serialized. `URL_RESOLVE_INTERCEPTOR_ORDER` and `FETCH_INTERCEPTOR_ORDER` come last; response-side `VALIDATE_STATUS_INTERCEPTOR_ORDER` runs near `Number.MAX_SAFE_INTEGER`.
+ - Non-2xx responses reject with `ExchangeError` (cause `HttpStatusValidationError`); timeouts with `FetchTimeoutError`. An error interceptor that clears `exchange.error` recovers, but the response phase is not re-run. Skip validation per call with `{ attributes: new Map([[IGNORE_VALIDATE_STATUS, true]]) }` in the third argument, or per client with `validateStatus`.
+ - `timeout` defaults to none (`0` also means none). Passing a `signal` disables the timeout for that request; pass an `abortController` to keep both.
+ - `options.headers` **replaces** the default `Content-Type: application/json` instead of merging.
+ - `urlParams.path` fills `{id}` / `:id` templates (missing values throw); `urlParams.query` goes through `URLSearchParams`, so `undefined` becomes the string `"undefined"` — drop it first.
+ ## Minimal example
+
+ ```ts
+ import { NamedFetcher, ResultExtractors, setHeader } from '@ahoo-wang/fetcher';
+
+ export const api = new NamedFetcher('api', {
+ baseURL: 'https://api.example.com',
+ timeout: 5000,
+ });
+ api.interceptors.request.use({
+ name: 'auth',
+ order: 100,
+ intercept(exchange) {
+ setHeader(
+ exchange.ensureRequestHeaders(),
+ 'Authorization',
+ `Bearer ${token()}`,
+ );
+ },
+ });
+ const user = await api.get<User>(
+ '/users/{id}',
+ { urlParams: { path: { id: 1 }, query: { include: 'profile' } } },
+ { resultExtractor: ResultExtractors.Json },
+ );
+ ```
+
## References
- - `references/api.md`: Detailed package API, examples, and edge-case guidance. Load it only when the task needs method signatures, interceptor ordering, URL parameter examples, error hierarchy, and complete service setup examples.
+ - `references/api.md`: constructor options, every method signature, interceptor order constants, URL params, error classes and full examples. Load it for exact signatures or lifecycle details.
## Related Skills
- - $fetcher-decorator-service: Use when services should be declared with TypeScript decorators.
- - $fetcher-cosec-auth: Use when interceptors need CoSec authentication behavior.
+ - $fetcher-decorator-service: stable endpoints declared as a class on top of a named fetcher.
+ - $fetcher-cosec-auth: JWT, refresh and 401/403 handling as interceptors.
+ - $fetcher-llm-streaming: SSE and token streams from a Fetcher response.
+ - $fetcher-react-hooks: request state in React components.