git:20261003.1465ec2 to git:20261004.2702e5d

43 added, 44 removed. Audit A to A.

---
name: fetcher-v6-migration
description: >
- Upgrade a project from Fetcher 5.x to 6.0. Use when asked to upgrade `@ahoo-wang/fetcher*` to 6; when Wow query hooks, data-monitor hooks or other hooks/options (`setQuery`, `propagateError`, `onBeforeExecute`) are missing from `@ahoo-wang/fetcher-react`; or when `fetcher-wow`, `fetcher-generator` or `fetcher-viewer` stop resolving. Decides stay-on-5.x versus upgrade, then detects and rewrites usages. Not for writing new Fetcher code.
+ Upgrade a project from Fetcher 5.x to 6.0. Use when asked to upgrade `@ahoo-wang/fetcher*` to 6; when hooks or options (Wow query hooks, data-monitor hooks, `setQuery`, `propagateError`) are missing from `@ahoo-wang/fetcher-react`; when deprecated `fetcher-wow`, `fetcher-generator` or `fetcher-viewer` must go; or when behavior changed after the upgrade. Decides stay-on-5.x versus upgrade, then rewrites usages. Not for new Fetcher code.
---
# fetcher-v6-migration
- 6.0 does two things. (1) The Wow-coupled packages moved to the Wow repository (`typescript/` there) and are released with Wow. (2) `@ahoo-wang/fetcher-react` is **redesigned around cancellation** — breaking: hooks and options are removed, queries are controlled, `execute` never rejects. `@ahoo-wang/fetcher`, `-decorator`, `-eventbus`, `-eventstream`, `-openai`, `-openapi`, `-storage` and `-cosec` have no breaking API change; their behavior corrections are under **Changed** in the 6.0 release notes and in the behavior checks of `references/api.md` (query serialization, timeouts with a `signal`, default `Content-Type`, `HttpStatusValidationError` no longer behind `ExchangeError.cause`, decorator array arguments, OpenAPI required fields, `EventStreamIncompleteError`, CoSec `isTrusted: sameOriginTrust`, storage `destroy()`, `RouteGuard` `onUnauthorized` after commit, `useKeyStorage` during SSR, `useLatest` after commit).
-
- ## 1. Check what is published — never assume
+ Fetcher **6.0.0 is published** (`latest` on npm). It does two breaking things: (1) the Wow-coupled packages left fetcher — `@ahoo-wang/fetcher-wow`, `@ahoo-wang/fetcher-generator` and `@ahoo-wang/fetcher-viewer` are **deprecated on npm** and replaced by Wow's `@ahoo-wang/wow-*` packages; (2) `@ahoo-wang/fetcher-react` is **redesigned around cancellation** (hooks and options removed, queries controlled, `execute` never rejects). `@ahoo-wang/fetcher`, `-decorator`, `-eventbus`, `-eventstream`, `-openai`, `-openapi`, `-storage` and `-cosec` keep their API but **change behavior** in ways that pass `tsc` (section 4). Source of truth: the fetcher repository's `docs/releases/v6.0.0.md`.
- The replacements (`@ahoo-wang/wow-client`, `@ahoo-wang/wow-react`, `@ahoo-wang/wow-generator`) are **not on npm yet**; they ship with Wow's first stable release, and fetcher 6.0 ships after that. Before recommending any install, run:
+ ## 1. Check npm — never assume
```sh
- npm view @ahoo-wang/fetcher dist-tags
- npm view @ahoo-wang/wow-client version # a 404 means: not published yet
+ npm view @ahoo-wang/fetcher dist-tags # latest: 6.x; release-5 only once a 5.x patch ships after 6.0
+ npm view @ahoo-wang/wow-client version peerDependencies # 9.2.1 or later: fetcher peer ^5.1.5 || ^6.0.0
+ npm view @ahoo-wang/fetcher-wow deprecated # the deprecation message names the replacement
```
- Never write `pnpm add @ahoo-wang/wow-*` into a plan, script or manifest until that command returns a version, and never invent one.
+ Wow 9.2.0 was the first stable release of `@ahoo-wang/wow-client`, `@ahoo-wang/wow-react`, `@ahoo-wang/wow-generator` and `@ahoo-wang/wow-view-engine`, but it required fetcher `^5.1.5`; **9.2.1 is the first to accept fetcher 6**. Install the version `npm view` prints (never below 9.2.1, never invented), and keep `wow-client` and `wow-react` on the **same** version (`wow-react` peers `wow-client` with a `~` range). Wow's migration guide: https://wow.ahoo.me/guide/typescript/migration. To stay on 5.x pin `^5.1.5`; use the `release-5` dist-tag only if `dist-tags` lists it.
- ## 2. Detect usages
+ ## 2. Detect and decide
- Run the checklist in `references/api.md` (grep patterns for manifests, imports, scripts, generated code and the fetcher-react hook API). Classify the project:
+ Run the grep checklist in `references/api.md`, then classify:
- | Found | Decision |
- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
- | `@ahoo-wang/fetcher-viewer`, or any data-monitor hook | **Stay on 5.x** (`^5.1.3`). No 6.x replacement exists. |
- | `@ahoo-wang/fetcher-wow`, `@ahoo-wang/fetcher-generator`, Wow query hooks | Replacements not on npm yet → **stay on 5.x**, move to 5.1.3 and prepare. Once published → switch to the Wow packages on 5.1.3 first, then upgrade fetcher. |
- | `@ahoo-wang/fetcher-react` hooks (any of them) | Upgrade, and **rewrite the hook calls** (section 3b) — they no longer type-check or behave the same. |
- | none of the above | **Upgrade** every `@ahoo-wang/fetcher*` to `^6.0.0` once 6.0.0 is on npm; no code changes beyond the **Changed** checks in `references/api.md`. |
+ | Found | Decision |
+ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+ | `@ahoo-wang/fetcher-viewer`, or any data-monitor hook | **Stay on 5.x** (`^5.1.5`, every `@ahoo-wang/fetcher*` package: the viewer peers `^5.0.0`). `@ahoo-wang/wow-view-engine` replaces the viewer with a different model and API; the data-monitor hooks have no replacement. |
+ | `@ahoo-wang/fetcher-wow`, `@ahoo-wang/fetcher-generator`, Wow query hooks | Upgrade in the order of section 3: Wow packages first (on 5.x), then fetcher 6. |
+ | `@ahoo-wang/fetcher-react` hooks | Upgrade, then **rewrite the hook calls** (section 3, step 5). |
+ | none of the above | Bump every `@ahoo-wang/fetcher*` to `^6.0.0` together, then run the behavior checks (section 4). |
Removed from `@ahoo-wang/fetcher-react` in 6.0:
- - Wow query hooks, moved to `@ahoo-wang/wow-react` under the same names: `useSingleQuery`, `useListQuery`, `usePagedQuery`, `useCountQuery`, `useListStreamQuery` with `UseSingleQueryOptions`/`UseSingleQueryReturn`, `UseListQueryOptions`/`UseListQueryReturn`, `UsePagedQueryOptions`/`UsePagedQueryReturn`, `UseCountQueryOptions`/`UseCountQueryReturn`, `UseListStreamQueryOptions`/`UseListStreamQueryReturn`; and `useFetcherSingleQuery`, `useFetcherListQuery`, `useFetcherPagedQuery`, `useFetcherCountQuery`, `useFetcherListStreamQuery` with `UseFetcherSingleQueryOptions`/`UseFetcherSingleQueryReturn`, `UseFetcherListQueryOptions`/`UseFetcherListQueryReturn`, `UseFetcherPagedQueryOptions`/`UseFetcherPagedQueryReturn`, `UseFetcherCountQueryOptions`/`UseFetcherCountQueryReturn`, `UseFetcherListStreamQueryOptions`/`UseFetcherListStreamQueryReturn`. `@ahoo-wang/wow-react` has its own request state and does not depend on `@ahoo-wang/fetcher-react`; its hooks keep their own API, so the redesign below does not apply to them.
+ - Wow query hooks, moved **unchanged** to `@ahoo-wang/wow-react`: `useSingleQuery`, `useListQuery`, `usePagedQuery`, `useCountQuery`, `useListStreamQuery` with `UseSingleQueryOptions`/`UseSingleQueryReturn`, `UseListQueryOptions`/`UseListQueryReturn`, `UsePagedQueryOptions`/`UsePagedQueryReturn`, `UseCountQueryOptions`/`UseCountQueryReturn`, `UseListStreamQueryOptions`/`UseListStreamQueryReturn`; and `useFetcherSingleQuery`, `useFetcherListQuery`, `useFetcherPagedQuery`, `useFetcherCountQuery`, `useFetcherListStreamQuery` with `UseFetcherSingleQueryOptions`/`UseFetcherSingleQueryReturn`, `UseFetcherListQueryOptions`/`UseFetcherListQueryReturn`, `UseFetcherPagedQueryOptions`/`UseFetcherPagedQueryReturn`, `UseFetcherCountQueryOptions`/`UseFetcherCountQueryReturn`, `UseFetcherListStreamQueryOptions`/`UseFetcherListStreamQueryReturn`. `@ahoo-wang/wow-react` has its own request state and does not depend on `@ahoo-wang/fetcher-react`, so the redesign below does not apply to them (their `setQuery` stays).
- Data-monitor hooks, **no replacement**: `useDataMonitor`, `UseDataMonitorOptions`, `UseDataMonitorReturn`, `DataMonitorService`, `dataMonitorService`, `DataMonitorNotificationConfig`, `useDataMonitorEventBus`, `UseDataMonitorEventBusReturn`, `DataChangedEvent`, `dataMonitorEventBus`.
- - Redesign, **no replacement** (write your own, or use a library such as ahooks): `useFullscreen`, `UseFullscreenOptions`, `UseFullscreenReturn`, `FullscreenProvider`, `FullscreenProviderProps`, `FullscreenContext`, `FullscreenContextValue`, `useFullscreenContext`, `getFullscreenElement`, `isFullscreen`, `enterFullscreen`, `exitFullscreen`, `addFullscreenChangeListener`, `removeFullscreenChangeListener`, `useRefs`, `UseRefsReturn`, `useForceUpdate`, `useMounted`, `useRequestId`, `UseRequestIdReturn`.
+ - Redesign, **no replacement** (write your own or use a library such as ahooks): `useFullscreen`, `UseFullscreenOptions`, `UseFullscreenReturn`, `FullscreenProvider`, `FullscreenProviderProps`, `FullscreenContext`, `FullscreenContextValue`, `useFullscreenContext`, `getFullscreenElement`, `isFullscreen`, `enterFullscreen`, `exitFullscreen`, `addFullscreenChangeListener`, `removeFullscreenChangeListener`, `useRefs`, `UseRefsReturn`, `useForceUpdate`, `useMounted`, `useRequestId`, `UseRequestIdReturn`.
- Redesign, **replaced by a controlled `query`**: `useQueryState`, `useCancellableQueryState`, `UseQueryStateOptions`, `UseQueryStateReturn`, `isValidateQuery`; the `initialQuery` option and the returned `setQuery`/`getQuery` on `useQuery`, `useFetcherQuery`, the query API hooks and the debounced query hooks.
- - Redesign, other: the `propagateError` option (read the state `execute` resolves to); `onBeforeExecute` and `OnBeforeExecuteCallback` on generated API hooks (prepare arguments before `execute`); `PromiseStateCallbacks` and `usePromiseState`'s `onSuccess`/`onError` (use `useExecutePromise`'s); `run`/`cancel`/`isPending` on `useDebouncedQuery`/`useDebouncedFetcherQuery` (now `pending`, `flush()`, `execute()`).
-
- `useFetcher`, `useFetcherQuery`, `useQuery`, `useExecutePromise`, `usePromiseState`, the debounced hooks, `createExecuteApiHooks`/`createQueryApiHooks`, CoSec, storage and event hooks stay — with the changed API in section 3b. Do not confuse `useFetcherQuery` (kept) with `useFetcherPagedQuery` (moved).
-
- ## 3. Rewrite, in this order
+ - Redesign, other: `propagateError` (read the state `execute` resolves to); `onBeforeExecute` and `OnBeforeExecuteCallback` (prepare arguments before `execute`); `PromiseStateCallbacks` and `usePromiseState`'s `onSuccess`/`onError` (use `useExecutePromise`'s); `run`/`cancel`/`isPending` on `useDebouncedQuery`/`useDebouncedFetcherQuery` (now `pending`, `flush()`, `execute()`).
- 1. Pin `@ahoo-wang/fetcher-react@5.1.3` — the first version whose `@ahoo-wang/fetcher-wow` peer is optional, and the first with the `/fetcher` subpath.
- 2. Only when the Wow packages are published: remove `@ahoo-wang/fetcher-wow` and `@ahoo-wang/fetcher-generator`, add the Wow packages at the version `npm view` reported, and move imports (`references/api.md` has the diffs). Their fetcher peer range covers the latest 5.x, so the app keeps working on 5.x; check that it also covers `^6` before step 3 with `npm view @ahoo-wang/wow-client peerDependencies`.
- 3. Replace the `fetcher-generator` command with `wow-generator` (`fetcher-generator` stays an alias until Wow v10), then **regenerate** clients: code generated earlier imports `@ahoo-wang/fetcher-wow`; the new generator emits `@ahoo-wang/wow-client`. If regeneration is impossible, rewrite that import.
- 4. Upgrade the remaining `@ahoo-wang/fetcher*` packages to `^6.0.0` together, and rewrite fetcher-react hook calls (3b).
- 5. Verify: type-check, run tests, and re-run the grep checklist until it finds nothing.
+ `useFetcher`, `useFetcherQuery`, `useQuery`, `useExecutePromise`, `usePromiseState`, the debounced hooks, `createExecuteApiHooks`/`createQueryApiHooks`, the CoSec components, storage and event hooks stay. Do not confuse `useFetcherQuery` (kept) with `useFetcherPagedQuery` (moved).
- ### 3b. fetcher-react hook calls
+ ## 3. Upgrade, in this order
- Rewrite with `references/api.md` (removed/changed table and diffs):
+ 1. **Latest 5.x first**: every `@ahoo-wang/fetcher*` package to `^5.1.5` (the Wow packages peer `^5.1.5`; `fetcher-react`'s `fetcher-wow` peer is optional from 5.1.3).
+ 2. **Swap the moved packages while still on 5.x**: remove `@ahoo-wang/fetcher-wow` and `@ahoo-wang/fetcher-generator`; add `@ahoo-wang/wow-client` and `@ahoo-wang/wow-react` (and `-D @ahoo-wang/wow-generator`) at the version `npm view` printed; move the imports (diffs in `references/api.md`). The app keeps working on 5.x.
+ 3. **Generator**: replace the `fetcher-generator` command with `wow-generator` (the old name stays an alias until Wow v10) and **regenerate** — code generated earlier imports `@ahoo-wang/fetcher-wow`; `wow-generator` emits `@ahoo-wang/wow-client`. If you cannot regenerate, rewrite that import.
+ 4. **fetcher 6**: bump every remaining `@ahoo-wang/fetcher*` package to `^6.0.0` in one change (their sibling peers are `^6.0.0`; mixing 5 and 6 breaks peer resolution). `fetcher-react` 6 needs `react` `^19.0.0` and no longer peers `fetcher-eventstream` or `react-dom`.
+ 5. **fetcher-react hooks**: run `tsc` and fix each error with the redesign table in `references/api.md`:
+ - Query in your own state: `const [query, setQuery] = useState(initial)`, pass `query`; drop `initialQuery` and the hook's `setQuery`/`getQuery`. `useQuery`'s `execute` option is `(query, abortController)` (no `attributes`). Generated query hooks take `{ query, attributes }`.
+ - `execute` never rejects: replace `propagateError: true` + `try/catch` with `const { status, result, error } = await execute(…)` and branch on `status` (`'idle'` when cancelled).
+ - Debounced query hooks: `pending` and `flush()` replace `run`/`cancel`/`isPending()`; they auto-execute by default and run the first query at once.
+ - Generated execute hooks: drop `onBeforeExecute`; `UseApiMethodExecuteOptions` lost its first type parameter; `appendAbortController: true` forwards the controller.
+ - Still compiles, behaves differently: `reset()` cancels the in-flight request; `onAbort` is synchronous; `useFetcher` no longer writes `request.abortController`; a failed `useFetcher` exposes the failed `exchange`; an auto-executing query renders `loading` on its first render; `RouteGuard` calls `onUnauthorized` after commit; `useKeyStorage`/`useSecurity` render the default during SSR and hydration.
+ 6. **Behavior checks** (section 4) for every package you use.
+ 7. **Verify**: type-check, run the tests, re-run the grep checklist until only intended hits remain.
- - **Query in your own state**: `const [query, setQuery] = useState(initial)` and pass `query`; drop `initialQuery`, and `setQuery`/`getQuery` from the hook's return. `useQuery`'s `execute` option loses `attributes`: `(query, abortController)`. Generated query hooks take `{ query, attributes }`.
- - **`execute` never rejects**: replace `propagateError: true` + `try/catch` with `const { status, result, error } = await execute(…)` and branch on `status`. A cancelled execution resolves to `status: 'idle'`.
- - **Debounced query hooks**: pass the state's `query`; use `pending` and `flush()` instead of `run`/`cancel`/`isPending()`. They now auto-execute by default, and the first query runs at once.
- - **Generated execute hooks**: drop `onBeforeExecute`; `UseApiMethodExecuteOptions` has lost its first type parameter. Pass `appendAbortController: true` to forward the controller.
- - **Behavior that still compiles but changed**: `reset()` now cancels the in-flight request first; `onAbort` is called synchronously; `useFetcher` no longer writes `request.abortController` (cancel with `abort()`/`reset()` or pass `request.signal`); on failure `useFetcher`'s `exchange` is the failed request's exchange instead of `undefined`; an auto-executing query renders `loading` on its first render (was `idle`); `useEventSubscription` no longer resubscribes when only the handler object changes.
- - **Upgrade check**: run `tsc`; then search for `propagateError`, `reset(`, `request.abortController`, and tests asserting `idle` on the first render of an auto query.
+ ## 4. Behavior changes that type-check
- Gotchas:
+ The ones that most often break an app (full list with grep checks in `references/api.md`):
- - `@ahoo-wang/wow-react` is ESM only; the `fetcher-react` UMD bundle no longer carries the Wow hooks.
- - `@ahoo-wang/fetcher-react/core` (promise state, `useQuery`, debounce hooks, `useLatest`, `useStableValue`) and `@ahoo-wang/fetcher-react/fetcher` (`useFetcher`, `useFetcherQuery` and their debounced variants) are ESM-only subpaths without security, storage or event-bus integration. The root entry exports everything that remained.
- - `@ahoo-wang/fetcher-react` 6 needs `react` `^19.0.0`.
- - A project on `@ahoo-wang/fetcher-viewer` must keep **all** fetcher packages on 5.x: its peers are `^5.0.0`.
- - After 6.0, 5.x patches publish under the dist-tag `release-5` (`@ahoo-wang/fetcher@release-5`); `latest` moves to 6.x.
+ - **Errors** (`@ahoo-wang/fetcher`): a status failure rejects with the `HttpStatusValidationError` itself — test `error instanceof HttpStatusValidationError` (before `ExchangeError`, its superclass), not `error.cause`. An **error interceptor that throws** (including a CoSec `onUnauthorized`/`onForbidden` callback) no longer escapes raw: the call rejects with an `ExchangeError` whose `cause` is the thrown value, and later error interceptors do not run — a `catch` that tested for your own error class must read `error.cause`.
+ - **Requests** (`@ahoo-wang/fetcher`): no default `Content-Type` (only plain-object and string bodies get `application/json`); `undefined`/`null` query values are omitted, arrays repeat (`ids=1&ids=2`, was `ids=1%2C2`), a `Date` is ISO 8601; a missing or `null` path parameter throws `Missing required path parameter`; a timeout now also applies when a `signal` is passed.
+ - **CoSec** (`@ahoo-wang/fetcher-cosec`): a refresh that fails on a network error, timeout, abort or **5xx keeps the session** and rejects with `RefreshUnavailableError` (inside the call's `ExchangeError`, at `cause`), **without** `onUnauthorized` — only a 4xx or a malformed refresh response signs out with `RefreshTokenError`. A custom `TokenRefresher` must reject with an error carrying `exchange.response.status` for a rejection to sign out. Add `isTrusted: sameOriginTrust` unless every absolute URL the client requests is yours. Set a `timeout` on the refresh client: browser tabs now refresh one at a time under a Web Lock.
+ - **Streams**: a stream that ends before its terminating event (OpenAI `data: [DONE]`) rejects with `EventStreamIncompleteError`; `ChatResponse.usage` is optional.
+ - **Decorator**: an array `@query('ids')` is sent as `ids=1&ids=2` (was `0=1&1=2`); a named `@attribute('x')` object is stored whole; an undecorated subclass override runs as written.
+ - **CDN/UMD**: `dist/index.umd.js` → `dist/index.umd.cjs` in `fetcher-cosec`, `-eventbus`, `-openai`, `-openapi`, `-storage`, `-react`.
## References
- - `references/api.md`: package mapping, the removed-export tables with replacements, the fetcher-react redesign (removed/changed API, migration diffs, behavior changes), subpath contents, the detection checklist, and version facts. Load it for the detection step and for any rewrite.
+ - `references/api.md`: npm facts, package mapping, the removed-export tables, the fetcher-react redesign (table, diffs, behavior), subpaths, **behavior changes by package**, the detection checklist, rewrites, generated clients and staying on 5.x. Load it for detection and for any rewrite.
## Related Skills
- $fetcher-react-hooks: the 6.x API of `@ahoo-wang/fetcher-react`, for writing the rewritten hook calls.
- - $fetcher-openapi-types: the OpenAPI type layer, which stays in fetcher.
+ - $fetcher-cosec-auth: the 6.x CoSec refresh, trust and 401/403 semantics.
+ - $fetcher-integration: the 6.x error classes and interceptor behavior of `@ahoo-wang/fetcher`.