@vielzeug/courier 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +1 -1
  2. package/dist/courier.cjs +3 -3
  3. package/dist/courier.cjs.map +1 -1
  4. package/dist/courier.d.ts +2 -2
  5. package/dist/courier.d.ts.map +1 -1
  6. package/dist/courier.iife.js +3 -3
  7. package/dist/courier.iife.js.map +1 -1
  8. package/dist/courier.js +3 -3
  9. package/dist/courier.js.map +1 -1
  10. package/dist/mutation.cjs +1 -1
  11. package/dist/mutation.cjs.map +1 -1
  12. package/dist/mutation.d.ts.map +1 -1
  13. package/dist/mutation.js +1 -1
  14. package/dist/mutation.js.map +1 -1
  15. package/dist/query-observe.cjs +1 -1
  16. package/dist/query-observe.cjs.map +1 -1
  17. package/dist/query-observe.d.ts.map +1 -1
  18. package/dist/query-observe.js +1 -1
  19. package/dist/query-observe.js.map +1 -1
  20. package/dist/query.cjs.map +1 -1
  21. package/dist/query.d.ts +0 -6
  22. package/dist/query.d.ts.map +1 -1
  23. package/dist/query.js.map +1 -1
  24. package/dist/stream-readable.cjs +3 -0
  25. package/dist/stream-readable.cjs.map +1 -0
  26. package/dist/stream-readable.d.ts +46 -0
  27. package/dist/stream-readable.d.ts.map +1 -0
  28. package/dist/stream-readable.js +3 -0
  29. package/dist/stream-readable.js.map +1 -0
  30. package/dist/stream-shared.cjs +2 -0
  31. package/dist/stream-shared.cjs.map +1 -0
  32. package/dist/stream-shared.d.ts +41 -0
  33. package/dist/stream-shared.d.ts.map +1 -0
  34. package/dist/stream-shared.js +2 -0
  35. package/dist/stream-shared.js.map +1 -0
  36. package/dist/stream-sse.cjs +6 -0
  37. package/dist/stream-sse.cjs.map +1 -0
  38. package/dist/stream-sse.d.ts +59 -0
  39. package/dist/stream-sse.d.ts.map +1 -0
  40. package/dist/stream-sse.js +6 -0
  41. package/dist/stream-sse.js.map +1 -0
  42. package/dist/stream.cjs +1 -6
  43. package/dist/stream.cjs.map +1 -1
  44. package/dist/stream.d.ts +4 -76
  45. package/dist/stream.d.ts.map +1 -1
  46. package/dist/stream.js +1 -6
  47. package/dist/stream.js.map +1 -1
  48. package/package.json +3 -3
package/dist/mutation.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./transport.cjs"),n=require("./retry.cjs");let r=require("@vielzeug/arsenal");var i=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!1,status:`loading`,updatedAt:void 0});function a(a,o){let s=i,c=0,l=null,u=new Set,d=!1,f=null;function p(){u.forEach(e=>e())}async function m(e){try{await e?.()}catch(e){if(o?.onCallbackError)try{o.onCallbackError(e instanceof Error?e:Error(String(e)))}catch{}}}return{async cancel(){let e=l;e&&(e.controller.abort(),await e.promise.catch(()=>{}))},dispose(){d||(d=!0,l?.controller.abort(),l=null,u.clear())},get disposed(){return d},async mutate(u,f){if(d)throw new e.CourierDisposedError(`Mutation`);let h=new AbortController,g=f?.signal?t.anySignal(f.signal,h.signal):h.signal,_=++c;s={data:void 0,error:null,isFetching:!0,isLoading:!0,status:`loading`,updatedAt:void 0},p();let v={controller:h,promise:null};l=v;let y=(async()=>{try{let e=await(0,r.retry)(()=>a(u,g),{delay:e=>n.resolveRetryDelay(e,o?.delay),shouldRetry:o?.shouldRetry,signal:g,times:o?.times??1});return _===c&&(s={data:e,error:null,isFetching:!1,isLoading:!1,status:`success`,updatedAt:Date.now()},p()),await m(()=>o?.onSuccess?.(e,u)),await m(()=>o?.onFinally?.(u)),await m(()=>o?.onSettled?.({data:e,status:`success`,variables:u})),e}catch(e){let t=e instanceof Error?e:Error(String(e)),n=g.aborted||(0,r.isAbortError)(t);throw _===c&&(s=n?i:{data:void 0,error:t,isFetching:!1,isLoading:!1,status:`error`,updatedAt:Date.now()},p()),n||await m(()=>o?.onError?.(t,u)),await m(()=>o?.onFinally?.(u)),await m(()=>o?.onSettled?.(n?{status:`aborted`,variables:u}:{error:t,status:`error`,variables:u})),t}finally{l?.controller===h&&(l=null)}})();return v.promise=y,y},peek(){return s},reset(){s=i,p()},get store(){return f??={peek:()=>s,subscribe:e=>(u.add(e),()=>u.delete(e))}},subscribe(e){return u.add(e),()=>u.delete(e)},[Symbol.dispose](){this.dispose()}}}exports.createMutation=a;
1
+ const e=require("./errors.cjs"),t=require("./transport.cjs"),n=require("./retry.cjs");let r=require("@vielzeug/arsenal");var i=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!0,status:`loading`,updatedAt:void 0});function a(a,o){let s=i,c=0,l=null,u=new Set,d=!1,f=null;function p(){u.forEach(e=>e())}async function m(e){try{await e?.()}catch(e){if(o?.onCallbackError)try{o.onCallbackError(e instanceof Error?e:Error(String(e)))}catch{}}}return{async cancel(){let e=l;e&&(e.controller.abort(),await e.promise.catch(()=>{}))},dispose(){d||(d=!0,l?.controller.abort(),l=null,u.clear())},get disposed(){return d},async mutate(u,f){if(d)throw new e.CourierDisposedError(`Mutation`);let h=new AbortController,g=f?.signal?t.anySignal(f.signal,h.signal):h.signal,_=++c;s={data:void 0,error:null,isFetching:!0,isLoading:!0,status:`loading`,updatedAt:void 0},p();let v={controller:h,promise:null};l=v;let y=(async()=>{try{let e=await(0,r.retry)(()=>a(u,g),{delay:e=>n.resolveRetryDelay(e,o?.delay),shouldRetry:o?.shouldRetry,signal:g,times:o?.times??1});return _===c&&(s={data:e,error:null,isFetching:!1,isLoading:!1,status:`success`,updatedAt:Date.now()},p()),await m(()=>o?.onSuccess?.(e,u)),await m(()=>o?.onFinally?.(u)),await m(()=>o?.onSettled?.({data:e,status:`success`,variables:u})),e}catch(e){let t=e instanceof Error?e:Error(String(e)),n=g.aborted||(0,r.isAbortError)(t);throw _===c&&(s=n?i:{data:void 0,error:t,isFetching:!1,isLoading:!1,status:`error`,updatedAt:Date.now()},p()),n||await m(()=>o?.onError?.(t,u)),await m(()=>o?.onFinally?.(u)),await m(()=>o?.onSettled?.(n?{status:`aborted`,variables:u}:{error:t,status:`error`,variables:u})),t}finally{l?.controller===h&&(l=null)}})();return v.promise=y,y},peek(){return s},reset(){s=i,p()},get store(){return f??={peek:()=>s,subscribe:e=>d?()=>{}:(u.add(e),()=>u.delete(e))}},subscribe(e){return d?()=>{}:(u.add(e),()=>u.delete(e))},[Symbol.dispose](){this.dispose()}}}exports.createMutation=a;
2
2
  //# sourceMappingURL=mutation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mutation.cjs","names":[],"sources":["../src/mutation.ts"],"sourcesContent":["import { isAbortError, retry } from '@vielzeug/arsenal';\n\nimport type { RetryOptions } from './retry';\nimport type { MutationState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport { resolveRetryDelay } from './retry';\nimport { anySignal } from './transport';\n\nconst IDLE_STATE: MutationState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: false,\n status: 'loading',\n updatedAt: undefined,\n});\n\n/**\n * Discriminated union result passed to `onSettled`.\n * Switch on `result.status` to handle each outcome exhaustively.\n */\nexport type SettledResult<TData, TVariables> =\n | { readonly data: TData; readonly status: 'success'; readonly variables: TVariables }\n | { readonly error: Error; readonly status: 'error'; readonly variables: TVariables }\n | { readonly status: 'aborted'; readonly variables: TVariables };\n\nexport type MutationOptions<TData = unknown, TVariables = void> = RetryOptions & {\n /**\n * Called when a lifecycle callback (`onSuccess`, `onError`, `onSettled`, or `onFinally`) throws.\n * Does not affect the `mutate()` result or re-throw. Optional — for development/debugging.\n */\n onCallbackError?: (error: Error) => void;\n /**\n * Called after a failed run, before `mutate()` rejects.\n * Not called when the mutation was aborted (use `onSettled` if you need abort awareness).\n */\n onError?: (error: Error, variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Use for cleanup patterns that do not need to inspect the result.\n * Runs after `onSuccess` / `onError` and before `onSettled`.\n */\n onFinally?: (variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Switch on `result.status` to handle `'success'`, `'error'`, or `'aborted'` exhaustively.\n */\n onSettled?: (result: SettledResult<TData, TVariables>) => void | Promise<void>;\n /** Called after a successful run, before `mutate()` resolves. */\n onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;\n};\n\nexport type MutationFn<TData, TVariables = void> = (input: TVariables, signal: AbortSignal) => Promise<TData>;\n\n/**\n * Wraps an async function (typically a write operation — create/update/delete) with\n * loading/success/error state tracking, retries, cancellation, and lifecycle callbacks.\n *\n * @example\n * ```ts\n * const createUser = createMutation(\n * (input: NewUser, signal) => api.post<User>('/users', { body: input, signal }),\n * { onSuccess: (user) => console.log('created', user.id) },\n * );\n *\n * const unsub = createUser.subscribe(() => console.log(createUser.peek()));\n * const user = await createUser.mutate({ name: 'Ada' });\n *\n * // later:\n * createUser.dispose();\n * ```\n */\nexport function createMutation<TData, TVariables = void>(\n mutationFn: MutationFn<TData, TVariables>,\n mutOpts?: MutationOptions<TData, TVariables>,\n): Mutation<TData, TVariables> {\n let snap: MutationState<TData> = IDLE_STATE as MutationState<TData>;\n let currentRun = 0;\n let activeRun: { controller: AbortController; promise: Promise<unknown> } | null = null;\n const observers = new Set<() => void>();\n let disposed = false;\n let cachedStore: SyncStore<MutationState<TData>> | null = null;\n\n function notifyObservers(): void {\n observers.forEach((l) => l());\n }\n\n async function safeCall(fn: (() => void | Promise<void>) | undefined): Promise<void> {\n try {\n await fn?.();\n } catch (err) {\n if (mutOpts?.onCallbackError) {\n try {\n mutOpts.onCallbackError(err instanceof Error ? err : new Error(String(err)));\n } catch {\n // Silently ignore errors in error handler itself.\n }\n }\n }\n }\n\n return {\n async cancel(): Promise<void> {\n const run = activeRun;\n\n if (!run) return;\n\n run.controller.abort();\n await run.promise.catch(() => {});\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n activeRun?.controller.abort();\n activeRun = null;\n observers.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n /**\n * Execute the mutation with the given variables.\n *\n * Concurrent calls are allowed. State updates (`isFetching`, `data`, `error`) always\n * reflect the **latest** `mutate()` call. Lifecycle callbacks (`onSuccess`, `onError`,\n * `onSettled`) fire for **every** call independently — not just the last one.\n * Cancel earlier calls via `mutation.cancel()` if you need only-last-wins callback semantics.\n *\n * @throws {CourierDisposedError} If called after `dispose()`.\n */\n async mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData> {\n if (disposed) throw new CourierDisposedError('Mutation');\n\n const localController = new AbortController();\n\n const signal = callOpts?.signal ? anySignal(callOpts.signal, localController.signal)! : localController.signal;\n const run = ++currentRun;\n\n snap = {\n data: undefined,\n error: null,\n isFetching: true,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n };\n notifyObservers();\n\n const pendingRun = { controller: localController, promise: null as unknown as Promise<TData> };\n\n activeRun = pendingRun;\n\n const operation = (async () => {\n try {\n const data = await retry(() => mutationFn(variables, signal), {\n delay: (attempt) => resolveRetryDelay(attempt, mutOpts?.delay),\n shouldRetry: mutOpts?.shouldRetry,\n signal,\n times: mutOpts?.times ?? 1,\n });\n\n if (run === currentRun) {\n snap = { data, error: null, isFetching: false, isLoading: false, status: 'success', updatedAt: Date.now() };\n notifyObservers();\n }\n\n await safeCall(() => mutOpts?.onSuccess?.(data, variables));\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() => mutOpts?.onSettled?.({ data, status: 'success', variables }));\n\n return data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const isAborted = signal.aborted || isAbortError(error);\n\n if (run === currentRun) {\n snap = isAborted\n ? (IDLE_STATE as MutationState<TData>)\n : { data: undefined, error, isFetching: false, isLoading: false, status: 'error', updatedAt: Date.now() };\n notifyObservers();\n }\n\n if (!isAborted) {\n await safeCall(() => mutOpts?.onError?.(error, variables));\n }\n\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() =>\n mutOpts?.onSettled?.(isAborted ? { status: 'aborted', variables } : { error, status: 'error', variables }),\n );\n\n throw error;\n } finally {\n if (activeRun?.controller === localController) {\n activeRun = null;\n }\n }\n })();\n\n pendingRun.promise = operation;\n\n return operation;\n },\n\n /**\n * Read the current mutation state snapshot.\n * For framework integrations prefer `peek()` + `subscribe()` directly.\n */\n peek(): MutationState<TData> {\n return snap;\n },\n\n reset(): void {\n snap = IDLE_STATE as MutationState<TData>;\n notifyObservers();\n },\n\n /**\n * A `SyncStore` view of the mutation state — identical to `{ peek, subscribe }` on this object.\n * Provided for framework integrations that accept a `SyncStore` directly:\n * - React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n * - Svelte: `readable(mutation.peek(), mutation.subscribe)`\n */\n get store(): SyncStore<MutationState<TData>> {\n return (cachedStore ??= {\n peek: () => snap,\n subscribe: (cb) => {\n observers.add(cb);\n\n return () => observers.delete(cb);\n },\n });\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n observers.add(onStoreChange);\n\n return () => observers.delete(onStoreChange);\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport interface Mutation<TData, TVariables = void> {\n [Symbol.dispose](): void;\n cancel(): Promise<void>;\n dispose(): void;\n get disposed(): boolean;\n mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData>;\n peek(): MutationState<TData>;\n reset(): void;\n /**\n * `SyncStore` adapter — equivalent to `{ peek, subscribe }` on this object.\n * For React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n */\n get store(): SyncStore<MutationState<TData>>;\n subscribe(onStoreChange: () => void): Unsubscribe;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mutation.cjs","names":[],"sources":["../src/mutation.ts"],"sourcesContent":["import { isAbortError, retry } from '@vielzeug/arsenal';\n\nimport type { RetryOptions } from './retry';\nimport type { MutationState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport { resolveRetryDelay } from './retry';\nimport { anySignal } from './transport';\n\nconst IDLE_STATE: MutationState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n});\n\n/**\n * Discriminated union result passed to `onSettled`.\n * Switch on `result.status` to handle each outcome exhaustively.\n */\nexport type SettledResult<TData, TVariables> =\n | { readonly data: TData; readonly status: 'success'; readonly variables: TVariables }\n | { readonly error: Error; readonly status: 'error'; readonly variables: TVariables }\n | { readonly status: 'aborted'; readonly variables: TVariables };\n\nexport type MutationOptions<TData = unknown, TVariables = void> = RetryOptions & {\n /**\n * Called when a lifecycle callback (`onSuccess`, `onError`, `onSettled`, or `onFinally`) throws.\n * Does not affect the `mutate()` result or re-throw. Optional — for development/debugging.\n */\n onCallbackError?: (error: Error) => void;\n /**\n * Called after a failed run, before `mutate()` rejects.\n * Not called when the mutation was aborted (use `onSettled` if you need abort awareness).\n */\n onError?: (error: Error, variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Use for cleanup patterns that do not need to inspect the result.\n * Runs after `onSuccess` / `onError` and before `onSettled`.\n */\n onFinally?: (variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Switch on `result.status` to handle `'success'`, `'error'`, or `'aborted'` exhaustively.\n */\n onSettled?: (result: SettledResult<TData, TVariables>) => void | Promise<void>;\n /** Called after a successful run, before `mutate()` resolves. */\n onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;\n};\n\nexport type MutationFn<TData, TVariables = void> = (input: TVariables, signal: AbortSignal) => Promise<TData>;\n\n/**\n * Wraps an async function (typically a write operation — create/update/delete) with\n * loading/success/error state tracking, retries, cancellation, and lifecycle callbacks.\n *\n * @example\n * ```ts\n * const createUser = createMutation(\n * (input: NewUser, signal) => api.post<User>('/users', { body: input, signal }),\n * { onSuccess: (user) => console.log('created', user.id) },\n * );\n *\n * const unsub = createUser.subscribe(() => console.log(createUser.peek()));\n * const user = await createUser.mutate({ name: 'Ada' });\n *\n * // later:\n * createUser.dispose();\n * ```\n */\nexport function createMutation<TData, TVariables = void>(\n mutationFn: MutationFn<TData, TVariables>,\n mutOpts?: MutationOptions<TData, TVariables>,\n): Mutation<TData, TVariables> {\n let snap: MutationState<TData> = IDLE_STATE as MutationState<TData>;\n let currentRun = 0;\n let activeRun: { controller: AbortController; promise: Promise<unknown> } | null = null;\n const observers = new Set<() => void>();\n let disposed = false;\n let cachedStore: SyncStore<MutationState<TData>> | null = null;\n\n function notifyObservers(): void {\n observers.forEach((l) => l());\n }\n\n async function safeCall(fn: (() => void | Promise<void>) | undefined): Promise<void> {\n try {\n await fn?.();\n } catch (err) {\n if (mutOpts?.onCallbackError) {\n try {\n mutOpts.onCallbackError(err instanceof Error ? err : new Error(String(err)));\n } catch {\n // Silently ignore errors in error handler itself.\n }\n }\n }\n }\n\n return {\n async cancel(): Promise<void> {\n const run = activeRun;\n\n if (!run) return;\n\n run.controller.abort();\n await run.promise.catch(() => {});\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n activeRun?.controller.abort();\n activeRun = null;\n observers.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n /**\n * Execute the mutation with the given variables.\n *\n * Concurrent calls are allowed. State updates (`isFetching`, `data`, `error`) always\n * reflect the **latest** `mutate()` call. Lifecycle callbacks (`onSuccess`, `onError`,\n * `onSettled`) fire for **every** call independently — not just the last one.\n * Cancel earlier calls via `mutation.cancel()` if you need only-last-wins callback semantics.\n *\n * @throws {CourierDisposedError} If called after `dispose()`.\n */\n async mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData> {\n if (disposed) throw new CourierDisposedError('Mutation');\n\n const localController = new AbortController();\n\n const signal = callOpts?.signal ? anySignal(callOpts.signal, localController.signal)! : localController.signal;\n const run = ++currentRun;\n\n snap = {\n data: undefined,\n error: null,\n isFetching: true,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n };\n notifyObservers();\n\n const pendingRun = { controller: localController, promise: null as unknown as Promise<TData> };\n\n activeRun = pendingRun;\n\n const operation = (async () => {\n try {\n const data = await retry(() => mutationFn(variables, signal), {\n delay: (attempt) => resolveRetryDelay(attempt, mutOpts?.delay),\n shouldRetry: mutOpts?.shouldRetry,\n signal,\n times: mutOpts?.times ?? 1,\n });\n\n if (run === currentRun) {\n snap = { data, error: null, isFetching: false, isLoading: false, status: 'success', updatedAt: Date.now() };\n notifyObservers();\n }\n\n await safeCall(() => mutOpts?.onSuccess?.(data, variables));\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() => mutOpts?.onSettled?.({ data, status: 'success', variables }));\n\n return data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const isAborted = signal.aborted || isAbortError(error);\n\n if (run === currentRun) {\n snap = isAborted\n ? (IDLE_STATE as MutationState<TData>)\n : { data: undefined, error, isFetching: false, isLoading: false, status: 'error', updatedAt: Date.now() };\n notifyObservers();\n }\n\n if (!isAborted) {\n await safeCall(() => mutOpts?.onError?.(error, variables));\n }\n\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() =>\n mutOpts?.onSettled?.(isAborted ? { status: 'aborted', variables } : { error, status: 'error', variables }),\n );\n\n throw error;\n } finally {\n if (activeRun?.controller === localController) {\n activeRun = null;\n }\n }\n })();\n\n pendingRun.promise = operation;\n\n return operation;\n },\n\n /**\n * Read the current mutation state snapshot.\n * For framework integrations prefer `peek()` + `subscribe()` directly.\n */\n peek(): MutationState<TData> {\n return snap;\n },\n\n reset(): void {\n snap = IDLE_STATE as MutationState<TData>;\n notifyObservers();\n },\n\n /**\n * A `SyncStore` view of the mutation state — identical to `{ peek, subscribe }` on this object.\n * Provided for framework integrations that accept a `SyncStore` directly:\n * - React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n * - Svelte: `readable(mutation.peek(), mutation.subscribe)`\n */\n get store(): SyncStore<MutationState<TData>> {\n return (cachedStore ??= {\n peek: () => snap,\n subscribe: (cb) => {\n if (disposed) return () => {};\n\n observers.add(cb);\n\n return () => observers.delete(cb);\n },\n });\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n if (disposed) return () => {};\n\n observers.add(onStoreChange);\n\n return () => observers.delete(onStoreChange);\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport interface Mutation<TData, TVariables = void> {\n [Symbol.dispose](): void;\n cancel(): Promise<void>;\n dispose(): void;\n get disposed(): boolean;\n mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData>;\n peek(): MutationState<TData>;\n reset(): void;\n /**\n * `SyncStore` adapter — equivalent to `{ peek, subscribe }` on this object.\n * For React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n */\n get store(): SyncStore<MutationState<TData>>;\n subscribe(onStoreChange: () => void): Unsubscribe;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"mutation.d.ts","sourceRoot":"","sources":["../src/mutation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAerE;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,EAAE,UAAU,IACvC;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,GACpF;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,GACnF;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,eAAe,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,IAAI,IAAI,YAAY,GAAG;IAC/E;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,iEAAiE;IACjE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;AAE9G;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,EACrD,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,EACzC,OAAO,CAAC,EAAE,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,GAC3C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CA6K7B;AAED,MAAM,WAAW,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAChD,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,IAAI,CAAC;IAChB,IAAI,QAAQ,IAAI,OAAO,CAAC;IACxB,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACnF,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd;;;OAGG;IACH,IAAI,KAAK,IAAI,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7C,SAAS,CAAC,aAAa,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;CACnD"}
1
+ {"version":3,"file":"mutation.d.ts","sourceRoot":"","sources":["../src/mutation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAerE;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,EAAE,UAAU,IACvC;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,GACpF;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,GACnF;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,eAAe,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,IAAI,IAAI,YAAY,GAAG;IAC/E;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,iEAAiE;IACjE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;AAE9G;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,EACrD,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,EACzC,OAAO,CAAC,EAAE,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,GAC3C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAiL7B;AAED,MAAM,WAAW,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAChD,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,IAAI,CAAC;IAChB,IAAI,QAAQ,IAAI,OAAO,CAAC;IACxB,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACnF,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd;;;OAGG;IACH,IAAI,KAAK,IAAI,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7C,SAAS,CAAC,aAAa,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;CACnD"}
package/dist/mutation.js CHANGED
@@ -1,2 +1,2 @@
1
- import{CourierDisposedError as e}from"./errors.js";import{anySignal as t}from"./transport.js";import{resolveRetryDelay as n}from"./retry.js";import{isAbortError as r,retry as i}from"@vielzeug/arsenal";var a=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!1,status:`loading`,updatedAt:void 0});function o(o,s){let c=a,l=0,u=null,d=new Set,f=!1,p=null;function m(){d.forEach(e=>e())}async function h(e){try{await e?.()}catch(e){if(s?.onCallbackError)try{s.onCallbackError(e instanceof Error?e:Error(String(e)))}catch{}}}return{async cancel(){let e=u;e&&(e.controller.abort(),await e.promise.catch(()=>{}))},dispose(){f||(f=!0,u?.controller.abort(),u=null,d.clear())},get disposed(){return f},async mutate(d,p){if(f)throw new e(`Mutation`);let g=new AbortController,_=p?.signal?t(p.signal,g.signal):g.signal,v=++l;c={data:void 0,error:null,isFetching:!0,isLoading:!0,status:`loading`,updatedAt:void 0},m();let y={controller:g,promise:null};u=y;let b=(async()=>{try{let e=await i(()=>o(d,_),{delay:e=>n(e,s?.delay),shouldRetry:s?.shouldRetry,signal:_,times:s?.times??1});return v===l&&(c={data:e,error:null,isFetching:!1,isLoading:!1,status:`success`,updatedAt:Date.now()},m()),await h(()=>s?.onSuccess?.(e,d)),await h(()=>s?.onFinally?.(d)),await h(()=>s?.onSettled?.({data:e,status:`success`,variables:d})),e}catch(e){let t=e instanceof Error?e:Error(String(e)),n=_.aborted||r(t);throw v===l&&(c=n?a:{data:void 0,error:t,isFetching:!1,isLoading:!1,status:`error`,updatedAt:Date.now()},m()),n||await h(()=>s?.onError?.(t,d)),await h(()=>s?.onFinally?.(d)),await h(()=>s?.onSettled?.(n?{status:`aborted`,variables:d}:{error:t,status:`error`,variables:d})),t}finally{u?.controller===g&&(u=null)}})();return y.promise=b,b},peek(){return c},reset(){c=a,m()},get store(){return p??={peek:()=>c,subscribe:e=>(d.add(e),()=>d.delete(e))}},subscribe(e){return d.add(e),()=>d.delete(e)},[Symbol.dispose](){this.dispose()}}}export{o as createMutation};
1
+ import{CourierDisposedError as e}from"./errors.js";import{anySignal as t}from"./transport.js";import{resolveRetryDelay as n}from"./retry.js";import{isAbortError as r,retry as i}from"@vielzeug/arsenal";var a=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!0,status:`loading`,updatedAt:void 0});function o(o,s){let c=a,l=0,u=null,d=new Set,f=!1,p=null;function m(){d.forEach(e=>e())}async function h(e){try{await e?.()}catch(e){if(s?.onCallbackError)try{s.onCallbackError(e instanceof Error?e:Error(String(e)))}catch{}}}return{async cancel(){let e=u;e&&(e.controller.abort(),await e.promise.catch(()=>{}))},dispose(){f||(f=!0,u?.controller.abort(),u=null,d.clear())},get disposed(){return f},async mutate(d,p){if(f)throw new e(`Mutation`);let g=new AbortController,_=p?.signal?t(p.signal,g.signal):g.signal,v=++l;c={data:void 0,error:null,isFetching:!0,isLoading:!0,status:`loading`,updatedAt:void 0},m();let y={controller:g,promise:null};u=y;let b=(async()=>{try{let e=await i(()=>o(d,_),{delay:e=>n(e,s?.delay),shouldRetry:s?.shouldRetry,signal:_,times:s?.times??1});return v===l&&(c={data:e,error:null,isFetching:!1,isLoading:!1,status:`success`,updatedAt:Date.now()},m()),await h(()=>s?.onSuccess?.(e,d)),await h(()=>s?.onFinally?.(d)),await h(()=>s?.onSettled?.({data:e,status:`success`,variables:d})),e}catch(e){let t=e instanceof Error?e:Error(String(e)),n=_.aborted||r(t);throw v===l&&(c=n?a:{data:void 0,error:t,isFetching:!1,isLoading:!1,status:`error`,updatedAt:Date.now()},m()),n||await h(()=>s?.onError?.(t,d)),await h(()=>s?.onFinally?.(d)),await h(()=>s?.onSettled?.(n?{status:`aborted`,variables:d}:{error:t,status:`error`,variables:d})),t}finally{u?.controller===g&&(u=null)}})();return y.promise=b,b},peek(){return c},reset(){c=a,m()},get store(){return p??={peek:()=>c,subscribe:e=>f?()=>{}:(d.add(e),()=>d.delete(e))}},subscribe(e){return f?()=>{}:(d.add(e),()=>d.delete(e))},[Symbol.dispose](){this.dispose()}}}export{o as createMutation};
2
2
  //# sourceMappingURL=mutation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mutation.js","names":[],"sources":["../src/mutation.ts"],"sourcesContent":["import { isAbortError, retry } from '@vielzeug/arsenal';\n\nimport type { RetryOptions } from './retry';\nimport type { MutationState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport { resolveRetryDelay } from './retry';\nimport { anySignal } from './transport';\n\nconst IDLE_STATE: MutationState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: false,\n status: 'loading',\n updatedAt: undefined,\n});\n\n/**\n * Discriminated union result passed to `onSettled`.\n * Switch on `result.status` to handle each outcome exhaustively.\n */\nexport type SettledResult<TData, TVariables> =\n | { readonly data: TData; readonly status: 'success'; readonly variables: TVariables }\n | { readonly error: Error; readonly status: 'error'; readonly variables: TVariables }\n | { readonly status: 'aborted'; readonly variables: TVariables };\n\nexport type MutationOptions<TData = unknown, TVariables = void> = RetryOptions & {\n /**\n * Called when a lifecycle callback (`onSuccess`, `onError`, `onSettled`, or `onFinally`) throws.\n * Does not affect the `mutate()` result or re-throw. Optional — for development/debugging.\n */\n onCallbackError?: (error: Error) => void;\n /**\n * Called after a failed run, before `mutate()` rejects.\n * Not called when the mutation was aborted (use `onSettled` if you need abort awareness).\n */\n onError?: (error: Error, variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Use for cleanup patterns that do not need to inspect the result.\n * Runs after `onSuccess` / `onError` and before `onSettled`.\n */\n onFinally?: (variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Switch on `result.status` to handle `'success'`, `'error'`, or `'aborted'` exhaustively.\n */\n onSettled?: (result: SettledResult<TData, TVariables>) => void | Promise<void>;\n /** Called after a successful run, before `mutate()` resolves. */\n onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;\n};\n\nexport type MutationFn<TData, TVariables = void> = (input: TVariables, signal: AbortSignal) => Promise<TData>;\n\n/**\n * Wraps an async function (typically a write operation — create/update/delete) with\n * loading/success/error state tracking, retries, cancellation, and lifecycle callbacks.\n *\n * @example\n * ```ts\n * const createUser = createMutation(\n * (input: NewUser, signal) => api.post<User>('/users', { body: input, signal }),\n * { onSuccess: (user) => console.log('created', user.id) },\n * );\n *\n * const unsub = createUser.subscribe(() => console.log(createUser.peek()));\n * const user = await createUser.mutate({ name: 'Ada' });\n *\n * // later:\n * createUser.dispose();\n * ```\n */\nexport function createMutation<TData, TVariables = void>(\n mutationFn: MutationFn<TData, TVariables>,\n mutOpts?: MutationOptions<TData, TVariables>,\n): Mutation<TData, TVariables> {\n let snap: MutationState<TData> = IDLE_STATE as MutationState<TData>;\n let currentRun = 0;\n let activeRun: { controller: AbortController; promise: Promise<unknown> } | null = null;\n const observers = new Set<() => void>();\n let disposed = false;\n let cachedStore: SyncStore<MutationState<TData>> | null = null;\n\n function notifyObservers(): void {\n observers.forEach((l) => l());\n }\n\n async function safeCall(fn: (() => void | Promise<void>) | undefined): Promise<void> {\n try {\n await fn?.();\n } catch (err) {\n if (mutOpts?.onCallbackError) {\n try {\n mutOpts.onCallbackError(err instanceof Error ? err : new Error(String(err)));\n } catch {\n // Silently ignore errors in error handler itself.\n }\n }\n }\n }\n\n return {\n async cancel(): Promise<void> {\n const run = activeRun;\n\n if (!run) return;\n\n run.controller.abort();\n await run.promise.catch(() => {});\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n activeRun?.controller.abort();\n activeRun = null;\n observers.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n /**\n * Execute the mutation with the given variables.\n *\n * Concurrent calls are allowed. State updates (`isFetching`, `data`, `error`) always\n * reflect the **latest** `mutate()` call. Lifecycle callbacks (`onSuccess`, `onError`,\n * `onSettled`) fire for **every** call independently — not just the last one.\n * Cancel earlier calls via `mutation.cancel()` if you need only-last-wins callback semantics.\n *\n * @throws {CourierDisposedError} If called after `dispose()`.\n */\n async mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData> {\n if (disposed) throw new CourierDisposedError('Mutation');\n\n const localController = new AbortController();\n\n const signal = callOpts?.signal ? anySignal(callOpts.signal, localController.signal)! : localController.signal;\n const run = ++currentRun;\n\n snap = {\n data: undefined,\n error: null,\n isFetching: true,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n };\n notifyObservers();\n\n const pendingRun = { controller: localController, promise: null as unknown as Promise<TData> };\n\n activeRun = pendingRun;\n\n const operation = (async () => {\n try {\n const data = await retry(() => mutationFn(variables, signal), {\n delay: (attempt) => resolveRetryDelay(attempt, mutOpts?.delay),\n shouldRetry: mutOpts?.shouldRetry,\n signal,\n times: mutOpts?.times ?? 1,\n });\n\n if (run === currentRun) {\n snap = { data, error: null, isFetching: false, isLoading: false, status: 'success', updatedAt: Date.now() };\n notifyObservers();\n }\n\n await safeCall(() => mutOpts?.onSuccess?.(data, variables));\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() => mutOpts?.onSettled?.({ data, status: 'success', variables }));\n\n return data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const isAborted = signal.aborted || isAbortError(error);\n\n if (run === currentRun) {\n snap = isAborted\n ? (IDLE_STATE as MutationState<TData>)\n : { data: undefined, error, isFetching: false, isLoading: false, status: 'error', updatedAt: Date.now() };\n notifyObservers();\n }\n\n if (!isAborted) {\n await safeCall(() => mutOpts?.onError?.(error, variables));\n }\n\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() =>\n mutOpts?.onSettled?.(isAborted ? { status: 'aborted', variables } : { error, status: 'error', variables }),\n );\n\n throw error;\n } finally {\n if (activeRun?.controller === localController) {\n activeRun = null;\n }\n }\n })();\n\n pendingRun.promise = operation;\n\n return operation;\n },\n\n /**\n * Read the current mutation state snapshot.\n * For framework integrations prefer `peek()` + `subscribe()` directly.\n */\n peek(): MutationState<TData> {\n return snap;\n },\n\n reset(): void {\n snap = IDLE_STATE as MutationState<TData>;\n notifyObservers();\n },\n\n /**\n * A `SyncStore` view of the mutation state — identical to `{ peek, subscribe }` on this object.\n * Provided for framework integrations that accept a `SyncStore` directly:\n * - React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n * - Svelte: `readable(mutation.peek(), mutation.subscribe)`\n */\n get store(): SyncStore<MutationState<TData>> {\n return (cachedStore ??= {\n peek: () => snap,\n subscribe: (cb) => {\n observers.add(cb);\n\n return () => observers.delete(cb);\n },\n });\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n observers.add(onStoreChange);\n\n return () => observers.delete(onStoreChange);\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport interface Mutation<TData, TVariables = void> {\n [Symbol.dispose](): void;\n cancel(): Promise<void>;\n dispose(): void;\n get disposed(): boolean;\n mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData>;\n peek(): MutationState<TData>;\n reset(): void;\n /**\n * `SyncStore` adapter — equivalent to `{ peek, subscribe }` on this object.\n * For React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n */\n get store(): SyncStore<MutationState<TData>>;\n subscribe(onStoreChange: () => void): Unsubscribe;\n}\n"],"mappings":"yMASA,IAAM,EAAqC,OAAO,OAAO,CACvD,KAAM,IAAA,GACN,MAAO,KACP,WAAY,GACZ,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,CAAC,EAyDD,SAAgB,EACd,EACA,EAC6B,CAC7B,IAAI,EAA6B,EAC7B,EAAa,EACb,EAA+E,KAC7E,EAAY,IAAI,IAClB,EAAW,GACX,EAAsD,KAE1D,SAAS,GAAwB,CAC/B,EAAU,QAAS,GAAM,EAAE,CAAC,CAC9B,CAEA,eAAe,EAAS,EAA6D,CACnF,GAAI,CACF,MAAM,IAAK,CACb,OAAS,EAAK,CACZ,GAAI,GAAS,gBACX,GAAI,CACF,EAAQ,gBAAgB,aAAe,MAAQ,EAAU,MAAM,OAAO,CAAG,CAAC,CAAC,CAC7E,MAAQ,CAER,CAEJ,CACF,CAEA,MAAO,CACL,MAAM,QAAwB,CAC5B,IAAM,EAAM,EAEP,IAEL,EAAI,WAAW,MAAM,EACrB,MAAM,EAAI,QAAQ,UAAY,CAAC,CAAC,EAClC,EAEA,SAAgB,CACV,IAEJ,EAAW,GACX,GAAW,WAAW,MAAM,EAC5B,EAAY,KACZ,EAAU,MAAM,EAClB,EAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAYA,MAAM,OAAO,EAAuB,EAAqD,CACvF,GAAI,EAAU,MAAM,IAAI,EAAqB,UAAU,EAEvD,IAAM,EAAkB,IAAI,gBAEtB,EAAS,GAAU,OAAS,EAAU,EAAS,OAAQ,EAAgB,MAAM,EAAK,EAAgB,OAClG,EAAM,EAAE,EAEd,EAAO,CACL,KAAM,IAAA,GACN,MAAO,KACP,WAAY,GACZ,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,EACA,EAAgB,EAEhB,IAAM,EAAa,CAAE,WAAY,EAAiB,QAAS,IAAkC,EAE7F,EAAY,EAEZ,IAAM,GAAa,SAAY,CAC7B,GAAI,CACF,IAAM,EAAO,MAAM,MAAY,EAAW,EAAW,CAAM,EAAG,CAC5D,MAAQ,GAAY,EAAkB,EAAS,GAAS,KAAK,EAC7D,YAAa,GAAS,YACtB,SACA,MAAO,GAAS,OAAS,CAC3B,CAAC,EAWD,OATI,IAAQ,IACV,EAAO,CAAE,OAAM,MAAO,KAAM,WAAY,GAAO,UAAW,GAAO,OAAQ,UAAW,UAAW,KAAK,IAAI,CAAE,EAC1G,EAAgB,GAGlB,MAAM,MAAe,GAAS,YAAY,EAAM,CAAS,CAAC,EAC1D,MAAM,MAAe,GAAS,YAAY,CAAS,CAAC,EACpD,MAAM,MAAe,GAAS,YAAY,CAAE,OAAM,OAAQ,UAAW,WAAU,CAAC,CAAC,EAE1E,CACT,OAAS,EAAK,CACZ,IAAM,EAAQ,aAAe,MAAQ,EAAU,MAAM,OAAO,CAAG,CAAC,EAC1D,EAAY,EAAO,SAAW,EAAa,CAAK,EAkBtD,MAhBI,IAAQ,IACV,EAAO,EACF,EACD,CAAE,KAAM,IAAA,GAAW,QAAO,WAAY,GAAO,UAAW,GAAO,OAAQ,QAAS,UAAW,KAAK,IAAI,CAAE,EAC1G,EAAgB,GAGb,GACH,MAAM,MAAe,GAAS,UAAU,EAAO,CAAS,CAAC,EAG3D,MAAM,MAAe,GAAS,YAAY,CAAS,CAAC,EACpD,MAAM,MACJ,GAAS,YAAY,EAAY,CAAE,OAAQ,UAAW,WAAU,EAAI,CAAE,QAAO,OAAQ,QAAS,WAAU,CAAC,CAC3G,EAEM,CACR,QAAU,CACJ,GAAW,aAAe,IAC5B,EAAY,KAEhB,CACF,EAAA,CAAG,EAIH,MAFA,GAAW,QAAU,EAEd,CACT,EAMA,MAA6B,CAC3B,OAAO,CACT,EAEA,OAAc,CACZ,EAAO,EACP,EAAgB,CAClB,EAQA,IAAI,OAAyC,CAC3C,MAAQ,KAAgB,CACtB,SAAY,EACZ,UAAY,IACV,EAAU,IAAI,CAAE,MAEH,EAAU,OAAO,CAAE,EAEpC,CACF,EAEA,UAAU,EAAwC,CAGhD,OAFA,EAAU,IAAI,CAAa,MAEd,EAAU,OAAO,CAAa,CAC7C,EAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,CACF,CACF"}
1
+ {"version":3,"file":"mutation.js","names":[],"sources":["../src/mutation.ts"],"sourcesContent":["import { isAbortError, retry } from '@vielzeug/arsenal';\n\nimport type { RetryOptions } from './retry';\nimport type { MutationState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport { resolveRetryDelay } from './retry';\nimport { anySignal } from './transport';\n\nconst IDLE_STATE: MutationState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n});\n\n/**\n * Discriminated union result passed to `onSettled`.\n * Switch on `result.status` to handle each outcome exhaustively.\n */\nexport type SettledResult<TData, TVariables> =\n | { readonly data: TData; readonly status: 'success'; readonly variables: TVariables }\n | { readonly error: Error; readonly status: 'error'; readonly variables: TVariables }\n | { readonly status: 'aborted'; readonly variables: TVariables };\n\nexport type MutationOptions<TData = unknown, TVariables = void> = RetryOptions & {\n /**\n * Called when a lifecycle callback (`onSuccess`, `onError`, `onSettled`, or `onFinally`) throws.\n * Does not affect the `mutate()` result or re-throw. Optional — for development/debugging.\n */\n onCallbackError?: (error: Error) => void;\n /**\n * Called after a failed run, before `mutate()` rejects.\n * Not called when the mutation was aborted (use `onSettled` if you need abort awareness).\n */\n onError?: (error: Error, variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Use for cleanup patterns that do not need to inspect the result.\n * Runs after `onSuccess` / `onError` and before `onSettled`.\n */\n onFinally?: (variables: TVariables) => void | Promise<void>;\n /**\n * Called after every run regardless of outcome, including aborts.\n * Switch on `result.status` to handle `'success'`, `'error'`, or `'aborted'` exhaustively.\n */\n onSettled?: (result: SettledResult<TData, TVariables>) => void | Promise<void>;\n /** Called after a successful run, before `mutate()` resolves. */\n onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;\n};\n\nexport type MutationFn<TData, TVariables = void> = (input: TVariables, signal: AbortSignal) => Promise<TData>;\n\n/**\n * Wraps an async function (typically a write operation — create/update/delete) with\n * loading/success/error state tracking, retries, cancellation, and lifecycle callbacks.\n *\n * @example\n * ```ts\n * const createUser = createMutation(\n * (input: NewUser, signal) => api.post<User>('/users', { body: input, signal }),\n * { onSuccess: (user) => console.log('created', user.id) },\n * );\n *\n * const unsub = createUser.subscribe(() => console.log(createUser.peek()));\n * const user = await createUser.mutate({ name: 'Ada' });\n *\n * // later:\n * createUser.dispose();\n * ```\n */\nexport function createMutation<TData, TVariables = void>(\n mutationFn: MutationFn<TData, TVariables>,\n mutOpts?: MutationOptions<TData, TVariables>,\n): Mutation<TData, TVariables> {\n let snap: MutationState<TData> = IDLE_STATE as MutationState<TData>;\n let currentRun = 0;\n let activeRun: { controller: AbortController; promise: Promise<unknown> } | null = null;\n const observers = new Set<() => void>();\n let disposed = false;\n let cachedStore: SyncStore<MutationState<TData>> | null = null;\n\n function notifyObservers(): void {\n observers.forEach((l) => l());\n }\n\n async function safeCall(fn: (() => void | Promise<void>) | undefined): Promise<void> {\n try {\n await fn?.();\n } catch (err) {\n if (mutOpts?.onCallbackError) {\n try {\n mutOpts.onCallbackError(err instanceof Error ? err : new Error(String(err)));\n } catch {\n // Silently ignore errors in error handler itself.\n }\n }\n }\n }\n\n return {\n async cancel(): Promise<void> {\n const run = activeRun;\n\n if (!run) return;\n\n run.controller.abort();\n await run.promise.catch(() => {});\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n activeRun?.controller.abort();\n activeRun = null;\n observers.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n /**\n * Execute the mutation with the given variables.\n *\n * Concurrent calls are allowed. State updates (`isFetching`, `data`, `error`) always\n * reflect the **latest** `mutate()` call. Lifecycle callbacks (`onSuccess`, `onError`,\n * `onSettled`) fire for **every** call independently — not just the last one.\n * Cancel earlier calls via `mutation.cancel()` if you need only-last-wins callback semantics.\n *\n * @throws {CourierDisposedError} If called after `dispose()`.\n */\n async mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData> {\n if (disposed) throw new CourierDisposedError('Mutation');\n\n const localController = new AbortController();\n\n const signal = callOpts?.signal ? anySignal(callOpts.signal, localController.signal)! : localController.signal;\n const run = ++currentRun;\n\n snap = {\n data: undefined,\n error: null,\n isFetching: true,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n };\n notifyObservers();\n\n const pendingRun = { controller: localController, promise: null as unknown as Promise<TData> };\n\n activeRun = pendingRun;\n\n const operation = (async () => {\n try {\n const data = await retry(() => mutationFn(variables, signal), {\n delay: (attempt) => resolveRetryDelay(attempt, mutOpts?.delay),\n shouldRetry: mutOpts?.shouldRetry,\n signal,\n times: mutOpts?.times ?? 1,\n });\n\n if (run === currentRun) {\n snap = { data, error: null, isFetching: false, isLoading: false, status: 'success', updatedAt: Date.now() };\n notifyObservers();\n }\n\n await safeCall(() => mutOpts?.onSuccess?.(data, variables));\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() => mutOpts?.onSettled?.({ data, status: 'success', variables }));\n\n return data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const isAborted = signal.aborted || isAbortError(error);\n\n if (run === currentRun) {\n snap = isAborted\n ? (IDLE_STATE as MutationState<TData>)\n : { data: undefined, error, isFetching: false, isLoading: false, status: 'error', updatedAt: Date.now() };\n notifyObservers();\n }\n\n if (!isAborted) {\n await safeCall(() => mutOpts?.onError?.(error, variables));\n }\n\n await safeCall(() => mutOpts?.onFinally?.(variables));\n await safeCall(() =>\n mutOpts?.onSettled?.(isAborted ? { status: 'aborted', variables } : { error, status: 'error', variables }),\n );\n\n throw error;\n } finally {\n if (activeRun?.controller === localController) {\n activeRun = null;\n }\n }\n })();\n\n pendingRun.promise = operation;\n\n return operation;\n },\n\n /**\n * Read the current mutation state snapshot.\n * For framework integrations prefer `peek()` + `subscribe()` directly.\n */\n peek(): MutationState<TData> {\n return snap;\n },\n\n reset(): void {\n snap = IDLE_STATE as MutationState<TData>;\n notifyObservers();\n },\n\n /**\n * A `SyncStore` view of the mutation state — identical to `{ peek, subscribe }` on this object.\n * Provided for framework integrations that accept a `SyncStore` directly:\n * - React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n * - Svelte: `readable(mutation.peek(), mutation.subscribe)`\n */\n get store(): SyncStore<MutationState<TData>> {\n return (cachedStore ??= {\n peek: () => snap,\n subscribe: (cb) => {\n if (disposed) return () => {};\n\n observers.add(cb);\n\n return () => observers.delete(cb);\n },\n });\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n if (disposed) return () => {};\n\n observers.add(onStoreChange);\n\n return () => observers.delete(onStoreChange);\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport interface Mutation<TData, TVariables = void> {\n [Symbol.dispose](): void;\n cancel(): Promise<void>;\n dispose(): void;\n get disposed(): boolean;\n mutate(variables: TVariables, callOpts?: { signal?: AbortSignal }): Promise<TData>;\n peek(): MutationState<TData>;\n reset(): void;\n /**\n * `SyncStore` adapter — equivalent to `{ peek, subscribe }` on this object.\n * For React: `useSyncExternalStore(mutation.subscribe, mutation.peek)`\n */\n get store(): SyncStore<MutationState<TData>>;\n subscribe(onStoreChange: () => void): Unsubscribe;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("./query-cache.cjs");var t=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!0,status:`loading`,updatedAt:void 0});function n(e){return e.status===`loading`?{data:void 0,error:null,isFetching:e.isFetching,isLoading:!0,status:`loading`,updatedAt:void 0}:e.status===`success`?{data:e.data,error:null,isFetching:e.isFetching,isLoading:!1,status:`success`,updatedAt:e.updatedAt}:{data:e.data,error:e.error,isFetching:e.isFetching,isLoading:!1,status:`error`,updatedAt:e.updatedAt}}function r(t,r){let i=n(t);if(i.status===`loading`){let t=e.resolveValue(r.placeholderData);return{...i,data:t}}let a=r.select?r.select(i.data):i.data;return{...i,data:a}}function i(e){for(let t of e.observers){let n=t,i=r(e,n),a=n.previous,o=Object.is(a?.data,i.data),s=!!a&&a.status===i.status&&a.isFetching===i.isFetching&&Object.is(a.error,i.error);(n.select?s&&o:s&&a?.updatedAt===i.updatedAt&&o)||(n.previous=i,n.listener())}}function a(n,i,a){let o={listener:()=>{},...a};return{peek(){let a=n.entries.get(e.hashKey(i));return a?r(a,o):t},subscribe(t){let s=e.ensureEntry(n,i);e.cancelGc(n,s.hash);let c={...a,listener:t,previous:r(s,o)};return s.observers.add(c),()=>{s.observers.delete(c),s.observers.size===0&&e.scheduleGc(n,s,s.lastConfig?.gcTime??n.gcTimeDefault)}}}}exports.notify=i,exports.toBaseState=n,exports.toObserverState=r,exports.watchInternal=a;
1
+ const e=require("./query-cache.cjs");var t=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!0,status:`loading`,updatedAt:void 0});function n(e){return e.status===`loading`?{data:void 0,error:null,isFetching:e.isFetching,isLoading:!0,status:`loading`,updatedAt:void 0}:e.status===`success`?{data:e.data,error:null,isFetching:e.isFetching,isLoading:!1,status:`success`,updatedAt:e.updatedAt}:{data:e.data,error:e.error,isFetching:e.isFetching,isLoading:!1,status:`error`,updatedAt:e.updatedAt}}function r(t,r){let i=n(t);if(i.status===`loading`){let t=e.resolveValue(r.placeholderData);return{...i,data:t}}let a=r.select?r.select(i.data):i.data;return{...i,data:a}}function i(e){for(let t of e.observers){let n=t,i=r(e,n),a=n.previous,o=Object.is(a?.data,i.data),s=!!a&&a.status===i.status&&a.isFetching===i.isFetching&&Object.is(a.error,i.error);(n.select?s&&o:s&&a?.updatedAt===i.updatedAt&&o)||(n.previous=i,n.listener())}}function a(n,i,a){let o={listener:()=>{},...a};return{peek(){let a=n.entries.get(e.hashKey(i));return a?r(a,o):t},subscribe(t){if(n.disposed)return()=>{};let s=e.ensureEntry(n,i);e.cancelGc(n,s.hash);let c={...a,listener:t,previous:r(s,o)};return s.observers.add(c),()=>{s.observers.delete(c),s.observers.size===0&&e.scheduleGc(n,s,s.lastConfig?.gcTime??n.gcTimeDefault)}}}}exports.notify=i,exports.toBaseState=n,exports.toObserverState=r,exports.watchInternal=a;
2
2
  //# sourceMappingURL=query-observe.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-observe.cjs","names":[],"sources":["../src/query-observe.ts"],"sourcesContent":["import type { CacheContext, CacheEntry } from './query-cache';\nimport type { AsyncState, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { cancelGc, ensureEntry, hashKey, resolveValue, scheduleGc } from './query-cache';\n\nexport type QueryObserver<T, S> = {\n listener: () => void;\n placeholderData?: S | (() => S | undefined);\n previous?: QueryState<S>;\n select?: (data: T | undefined) => S | undefined;\n};\n\nconst LOADING_STATE: AsyncState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n});\n\nexport function toBaseState<T>(entry: CacheEntry<T>): QueryState<T> {\n if (entry.status === 'loading') {\n return {\n data: undefined,\n error: null,\n isFetching: entry.isFetching,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n } as QueryState<T>;\n }\n\n if (entry.status === 'success') {\n return {\n data: entry.data as T,\n error: null,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'success',\n updatedAt: entry.updatedAt as number,\n };\n }\n\n return {\n data: entry.data,\n error: entry.error as Error,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'error',\n updatedAt: entry.updatedAt as number,\n };\n}\n\nexport function toObserverState<T, S>(entry: CacheEntry<T>, observer: QueryObserver<T, S>): QueryState<S> {\n const base = toBaseState(entry);\n\n if (base.status === 'loading') {\n const placeholder = resolveValue(observer.placeholderData as S | (() => S | undefined) | undefined);\n\n return { ...base, data: placeholder } as QueryState<S>;\n }\n\n const selected = observer.select ? observer.select(base.data as T | undefined) : (base.data as S | undefined);\n\n return { ...base, data: selected } as QueryState<S>;\n}\n\nexport function notify<T>(entry: CacheEntry<T>): void {\n for (const observer of entry.observers) {\n const typed = observer as QueryObserver<T, unknown>;\n const next = toObserverState(entry, typed);\n const prev = typed.previous;\n\n const dataUnchanged = Object.is(prev?.data, next.data);\n const metaUnchanged =\n !!prev && prev.status === next.status && prev.isFetching === next.isFetching && Object.is(prev.error, next.error);\n\n const shouldSkip = typed.select\n ? metaUnchanged && dataUnchanged\n : metaUnchanged && prev?.updatedAt === next.updatedAt && dataUnchanged;\n\n if (shouldSkip) continue;\n\n typed.previous = next;\n typed.listener();\n }\n}\n\nexport function watchInternal<T = unknown, S = T>(\n ctx: CacheContext,\n key: Parameters<typeof ensureEntry>[1],\n opts?: { placeholderData?: S | (() => S | undefined); select?: (data: T | undefined) => S | undefined },\n): SyncStore<QueryState<S>> {\n const peekObserver: QueryObserver<T, S> = { listener: () => {}, ...opts };\n\n return {\n peek(): QueryState<S> {\n const entry = ctx.entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return LOADING_STATE as QueryState<S>;\n\n return toObserverState(entry, peekObserver);\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n const entry = ensureEntry<T>(ctx, key);\n\n cancelGc(ctx, entry.hash);\n\n const observer: QueryObserver<T, S> = {\n ...opts,\n listener: onStoreChange,\n previous: toObserverState(entry, peekObserver),\n };\n\n entry.observers.add(observer as QueryObserver<unknown, unknown>);\n\n return () => {\n entry.observers.delete(observer as QueryObserver<unknown, unknown>);\n\n if (entry.observers.size === 0) {\n scheduleGc(ctx, entry, entry.lastConfig?.gcTime ?? ctx.gcTimeDefault);\n }\n };\n },\n };\n}\n"],"mappings":"qCAYA,IAAM,EAAqC,OAAO,OAAO,CACvD,KAAM,IAAA,GACN,MAAO,KACP,WAAY,GACZ,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,CAAC,EAED,SAAgB,EAAe,EAAqC,CAuBlE,OAtBI,EAAM,SAAW,UACZ,CACL,KAAM,IAAA,GACN,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,EAGE,EAAM,SAAW,UACZ,CACL,KAAM,EAAM,KACZ,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,EAAM,SACnB,EAGK,CACL,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,QACR,UAAW,EAAM,SACnB,CACF,CAEA,SAAgB,EAAsB,EAAsB,EAA8C,CACxG,IAAM,EAAO,EAAY,CAAK,EAE9B,GAAI,EAAK,SAAW,UAAW,CAC7B,IAAM,EAAc,EAAA,aAAa,EAAS,eAAwD,EAElG,MAAO,CAAE,GAAG,EAAM,KAAM,CAAY,CACtC,CAEA,IAAM,EAAW,EAAS,OAAS,EAAS,OAAO,EAAK,IAAqB,EAAK,EAAK,KAEvF,MAAO,CAAE,GAAG,EAAM,KAAM,CAAS,CACnC,CAEA,SAAgB,EAAU,EAA4B,CACpD,IAAK,IAAM,KAAY,EAAM,UAAW,CACtC,IAAM,EAAQ,EACR,EAAO,EAAgB,EAAO,CAAK,EACnC,EAAO,EAAM,SAEb,EAAgB,OAAO,GAAG,GAAM,KAAM,EAAK,IAAI,EAC/C,EACJ,CAAC,CAAC,GAAQ,EAAK,SAAW,EAAK,QAAU,EAAK,aAAe,EAAK,YAAc,OAAO,GAAG,EAAK,MAAO,EAAK,KAAK,GAE/F,EAAM,OACrB,GAAiB,EACjB,GAAiB,GAAM,YAAc,EAAK,WAAa,KAI3D,EAAM,SAAW,EACjB,EAAM,SAAS,EACjB,CACF,CAEA,SAAgB,EACd,EACA,EACA,EAC0B,CAC1B,IAAM,EAAoC,CAAE,aAAgB,CAAC,EAAG,GAAG,CAAK,EAExE,MAAO,CACL,MAAsB,CACpB,IAAM,EAAQ,EAAI,QAAQ,IAAI,EAAA,QAAQ,CAAG,CAAC,EAI1C,OAFK,EAEE,EAAgB,EAAO,CAAY,EAFvB,CAGrB,EAEA,UAAU,EAAwC,CAChD,IAAM,EAAQ,EAAA,YAAe,EAAK,CAAG,EAErC,EAAA,SAAS,EAAK,EAAM,IAAI,EAExB,IAAM,EAAgC,CACpC,GAAG,EACH,SAAU,EACV,SAAU,EAAgB,EAAO,CAAY,CAC/C,EAIA,OAFA,EAAM,UAAU,IAAI,CAA2C,MAElD,CACX,EAAM,UAAU,OAAO,CAA2C,EAE9D,EAAM,UAAU,OAAS,GAC3B,EAAA,WAAW,EAAK,EAAO,EAAM,YAAY,QAAU,EAAI,aAAa,CAExE,CACF,CACF,CACF"}
1
+ {"version":3,"file":"query-observe.cjs","names":[],"sources":["../src/query-observe.ts"],"sourcesContent":["import type { CacheContext, CacheEntry } from './query-cache';\nimport type { AsyncState, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { cancelGc, ensureEntry, hashKey, resolveValue, scheduleGc } from './query-cache';\n\nexport type QueryObserver<T, S> = {\n listener: () => void;\n placeholderData?: S | (() => S | undefined);\n previous?: QueryState<S>;\n select?: (data: T | undefined) => S | undefined;\n};\n\nconst LOADING_STATE: AsyncState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n});\n\nexport function toBaseState<T>(entry: CacheEntry<T>): QueryState<T> {\n if (entry.status === 'loading') {\n return {\n data: undefined,\n error: null,\n isFetching: entry.isFetching,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n } as QueryState<T>;\n }\n\n if (entry.status === 'success') {\n return {\n data: entry.data as T,\n error: null,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'success',\n updatedAt: entry.updatedAt as number,\n };\n }\n\n return {\n data: entry.data,\n error: entry.error as Error,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'error',\n updatedAt: entry.updatedAt as number,\n };\n}\n\nexport function toObserverState<T, S>(entry: CacheEntry<T>, observer: QueryObserver<T, S>): QueryState<S> {\n const base = toBaseState(entry);\n\n if (base.status === 'loading') {\n const placeholder = resolveValue(observer.placeholderData as S | (() => S | undefined) | undefined);\n\n return { ...base, data: placeholder } as QueryState<S>;\n }\n\n const selected = observer.select ? observer.select(base.data as T | undefined) : (base.data as S | undefined);\n\n return { ...base, data: selected } as QueryState<S>;\n}\n\nexport function notify<T>(entry: CacheEntry<T>): void {\n for (const observer of entry.observers) {\n const typed = observer as QueryObserver<T, unknown>;\n const next = toObserverState(entry, typed);\n const prev = typed.previous;\n\n const dataUnchanged = Object.is(prev?.data, next.data);\n const metaUnchanged =\n !!prev && prev.status === next.status && prev.isFetching === next.isFetching && Object.is(prev.error, next.error);\n\n const shouldSkip = typed.select\n ? metaUnchanged && dataUnchanged\n : metaUnchanged && prev?.updatedAt === next.updatedAt && dataUnchanged;\n\n if (shouldSkip) continue;\n\n typed.previous = next;\n typed.listener();\n }\n}\n\nexport function watchInternal<T = unknown, S = T>(\n ctx: CacheContext,\n key: Parameters<typeof ensureEntry>[1],\n opts?: { placeholderData?: S | (() => S | undefined); select?: (data: T | undefined) => S | undefined },\n): SyncStore<QueryState<S>> {\n const peekObserver: QueryObserver<T, S> = { listener: () => {}, ...opts };\n\n return {\n peek(): QueryState<S> {\n const entry = ctx.entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return LOADING_STATE as QueryState<S>;\n\n return toObserverState(entry, peekObserver);\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n if (ctx.disposed) return () => {};\n\n const entry = ensureEntry<T>(ctx, key);\n\n cancelGc(ctx, entry.hash);\n\n const observer: QueryObserver<T, S> = {\n ...opts,\n listener: onStoreChange,\n previous: toObserverState(entry, peekObserver),\n };\n\n entry.observers.add(observer as QueryObserver<unknown, unknown>);\n\n return () => {\n entry.observers.delete(observer as QueryObserver<unknown, unknown>);\n\n if (entry.observers.size === 0) {\n scheduleGc(ctx, entry, entry.lastConfig?.gcTime ?? ctx.gcTimeDefault);\n }\n };\n },\n };\n}\n"],"mappings":"qCAYA,IAAM,EAAqC,OAAO,OAAO,CACvD,KAAM,IAAA,GACN,MAAO,KACP,WAAY,GACZ,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,CAAC,EAED,SAAgB,EAAe,EAAqC,CAuBlE,OAtBI,EAAM,SAAW,UACZ,CACL,KAAM,IAAA,GACN,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,EAGE,EAAM,SAAW,UACZ,CACL,KAAM,EAAM,KACZ,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,EAAM,SACnB,EAGK,CACL,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,QACR,UAAW,EAAM,SACnB,CACF,CAEA,SAAgB,EAAsB,EAAsB,EAA8C,CACxG,IAAM,EAAO,EAAY,CAAK,EAE9B,GAAI,EAAK,SAAW,UAAW,CAC7B,IAAM,EAAc,EAAA,aAAa,EAAS,eAAwD,EAElG,MAAO,CAAE,GAAG,EAAM,KAAM,CAAY,CACtC,CAEA,IAAM,EAAW,EAAS,OAAS,EAAS,OAAO,EAAK,IAAqB,EAAK,EAAK,KAEvF,MAAO,CAAE,GAAG,EAAM,KAAM,CAAS,CACnC,CAEA,SAAgB,EAAU,EAA4B,CACpD,IAAK,IAAM,KAAY,EAAM,UAAW,CACtC,IAAM,EAAQ,EACR,EAAO,EAAgB,EAAO,CAAK,EACnC,EAAO,EAAM,SAEb,EAAgB,OAAO,GAAG,GAAM,KAAM,EAAK,IAAI,EAC/C,EACJ,CAAC,CAAC,GAAQ,EAAK,SAAW,EAAK,QAAU,EAAK,aAAe,EAAK,YAAc,OAAO,GAAG,EAAK,MAAO,EAAK,KAAK,GAE/F,EAAM,OACrB,GAAiB,EACjB,GAAiB,GAAM,YAAc,EAAK,WAAa,KAI3D,EAAM,SAAW,EACjB,EAAM,SAAS,EACjB,CACF,CAEA,SAAgB,EACd,EACA,EACA,EAC0B,CAC1B,IAAM,EAAoC,CAAE,aAAgB,CAAC,EAAG,GAAG,CAAK,EAExE,MAAO,CACL,MAAsB,CACpB,IAAM,EAAQ,EAAI,QAAQ,IAAI,EAAA,QAAQ,CAAG,CAAC,EAI1C,OAFK,EAEE,EAAgB,EAAO,CAAY,EAFvB,CAGrB,EAEA,UAAU,EAAwC,CAChD,GAAI,EAAI,SAAU,UAAa,CAAC,EAEhC,IAAM,EAAQ,EAAA,YAAe,EAAK,CAAG,EAErC,EAAA,SAAS,EAAK,EAAM,IAAI,EAExB,IAAM,EAAgC,CACpC,GAAG,EACH,SAAU,EACV,SAAU,EAAgB,EAAO,CAAY,CAC/C,EAIA,OAFA,EAAM,UAAU,IAAI,CAA2C,MAElD,CACX,EAAM,UAAU,OAAO,CAA2C,EAE9D,EAAM,UAAU,OAAS,GAC3B,EAAA,WAAW,EAAK,EAAO,EAAM,YAAY,QAAU,EAAI,aAAa,CAExE,CACF,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"query-observe.d.ts","sourceRoot":"","sources":["../src/query-observe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAc,UAAU,EAAE,SAAS,EAAe,MAAM,SAAS,CAAC;AAE9E,OAAO,EAAY,WAAW,EAAqC,MAAM,eAAe,CAAC;AAEzF,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,SAAS,CAAC;CACjD,CAAC;AAWF,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CA+BlE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAYxG;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAmBpD;AAED,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,EAC9C,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EACtC,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,SAAS,CAAA;CAAE,GACtG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAkC1B"}
1
+ {"version":3,"file":"query-observe.d.ts","sourceRoot":"","sources":["../src/query-observe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAc,UAAU,EAAE,SAAS,EAAe,MAAM,SAAS,CAAC;AAE9E,OAAO,EAAY,WAAW,EAAqC,MAAM,eAAe,CAAC;AAEzF,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,SAAS,CAAC;CACjD,CAAC;AAWF,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CA+BlE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAYxG;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAmBpD;AAED,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,EAC9C,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EACtC,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,SAAS,CAAA;CAAE,GACtG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAoC1B"}
@@ -1,2 +1,2 @@
1
- import{cancelGc as e,ensureEntry as t,hashKey as n,resolveValue as r,scheduleGc as i}from"./query-cache.js";var a=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!0,status:`loading`,updatedAt:void 0});function o(e){return e.status===`loading`?{data:void 0,error:null,isFetching:e.isFetching,isLoading:!0,status:`loading`,updatedAt:void 0}:e.status===`success`?{data:e.data,error:null,isFetching:e.isFetching,isLoading:!1,status:`success`,updatedAt:e.updatedAt}:{data:e.data,error:e.error,isFetching:e.isFetching,isLoading:!1,status:`error`,updatedAt:e.updatedAt}}function s(e,t){let n=o(e);if(n.status===`loading`){let e=r(t.placeholderData);return{...n,data:e}}let i=t.select?t.select(n.data):n.data;return{...n,data:i}}function c(e){for(let t of e.observers){let n=t,r=s(e,n),i=n.previous,a=Object.is(i?.data,r.data),o=!!i&&i.status===r.status&&i.isFetching===r.isFetching&&Object.is(i.error,r.error);(n.select?o&&a:o&&i?.updatedAt===r.updatedAt&&a)||(n.previous=r,n.listener())}}function l(r,o,c){let l={listener:()=>{},...c};return{peek(){let e=r.entries.get(n(o));return e?s(e,l):a},subscribe(n){let a=t(r,o);e(r,a.hash);let u={...c,listener:n,previous:s(a,l)};return a.observers.add(u),()=>{a.observers.delete(u),a.observers.size===0&&i(r,a,a.lastConfig?.gcTime??r.gcTimeDefault)}}}}export{c as notify,o as toBaseState,s as toObserverState,l as watchInternal};
1
+ import{cancelGc as e,ensureEntry as t,hashKey as n,resolveValue as r,scheduleGc as i}from"./query-cache.js";var a=Object.freeze({data:void 0,error:null,isFetching:!1,isLoading:!0,status:`loading`,updatedAt:void 0});function o(e){return e.status===`loading`?{data:void 0,error:null,isFetching:e.isFetching,isLoading:!0,status:`loading`,updatedAt:void 0}:e.status===`success`?{data:e.data,error:null,isFetching:e.isFetching,isLoading:!1,status:`success`,updatedAt:e.updatedAt}:{data:e.data,error:e.error,isFetching:e.isFetching,isLoading:!1,status:`error`,updatedAt:e.updatedAt}}function s(e,t){let n=o(e);if(n.status===`loading`){let e=r(t.placeholderData);return{...n,data:e}}let i=t.select?t.select(n.data):n.data;return{...n,data:i}}function c(e){for(let t of e.observers){let n=t,r=s(e,n),i=n.previous,a=Object.is(i?.data,r.data),o=!!i&&i.status===r.status&&i.isFetching===r.isFetching&&Object.is(i.error,r.error);(n.select?o&&a:o&&i?.updatedAt===r.updatedAt&&a)||(n.previous=r,n.listener())}}function l(r,o,c){let l={listener:()=>{},...c};return{peek(){let e=r.entries.get(n(o));return e?s(e,l):a},subscribe(n){if(r.disposed)return()=>{};let a=t(r,o);e(r,a.hash);let u={...c,listener:n,previous:s(a,l)};return a.observers.add(u),()=>{a.observers.delete(u),a.observers.size===0&&i(r,a,a.lastConfig?.gcTime??r.gcTimeDefault)}}}}export{c as notify,o as toBaseState,s as toObserverState,l as watchInternal};
2
2
  //# sourceMappingURL=query-observe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-observe.js","names":[],"sources":["../src/query-observe.ts"],"sourcesContent":["import type { CacheContext, CacheEntry } from './query-cache';\nimport type { AsyncState, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { cancelGc, ensureEntry, hashKey, resolveValue, scheduleGc } from './query-cache';\n\nexport type QueryObserver<T, S> = {\n listener: () => void;\n placeholderData?: S | (() => S | undefined);\n previous?: QueryState<S>;\n select?: (data: T | undefined) => S | undefined;\n};\n\nconst LOADING_STATE: AsyncState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n});\n\nexport function toBaseState<T>(entry: CacheEntry<T>): QueryState<T> {\n if (entry.status === 'loading') {\n return {\n data: undefined,\n error: null,\n isFetching: entry.isFetching,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n } as QueryState<T>;\n }\n\n if (entry.status === 'success') {\n return {\n data: entry.data as T,\n error: null,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'success',\n updatedAt: entry.updatedAt as number,\n };\n }\n\n return {\n data: entry.data,\n error: entry.error as Error,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'error',\n updatedAt: entry.updatedAt as number,\n };\n}\n\nexport function toObserverState<T, S>(entry: CacheEntry<T>, observer: QueryObserver<T, S>): QueryState<S> {\n const base = toBaseState(entry);\n\n if (base.status === 'loading') {\n const placeholder = resolveValue(observer.placeholderData as S | (() => S | undefined) | undefined);\n\n return { ...base, data: placeholder } as QueryState<S>;\n }\n\n const selected = observer.select ? observer.select(base.data as T | undefined) : (base.data as S | undefined);\n\n return { ...base, data: selected } as QueryState<S>;\n}\n\nexport function notify<T>(entry: CacheEntry<T>): void {\n for (const observer of entry.observers) {\n const typed = observer as QueryObserver<T, unknown>;\n const next = toObserverState(entry, typed);\n const prev = typed.previous;\n\n const dataUnchanged = Object.is(prev?.data, next.data);\n const metaUnchanged =\n !!prev && prev.status === next.status && prev.isFetching === next.isFetching && Object.is(prev.error, next.error);\n\n const shouldSkip = typed.select\n ? metaUnchanged && dataUnchanged\n : metaUnchanged && prev?.updatedAt === next.updatedAt && dataUnchanged;\n\n if (shouldSkip) continue;\n\n typed.previous = next;\n typed.listener();\n }\n}\n\nexport function watchInternal<T = unknown, S = T>(\n ctx: CacheContext,\n key: Parameters<typeof ensureEntry>[1],\n opts?: { placeholderData?: S | (() => S | undefined); select?: (data: T | undefined) => S | undefined },\n): SyncStore<QueryState<S>> {\n const peekObserver: QueryObserver<T, S> = { listener: () => {}, ...opts };\n\n return {\n peek(): QueryState<S> {\n const entry = ctx.entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return LOADING_STATE as QueryState<S>;\n\n return toObserverState(entry, peekObserver);\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n const entry = ensureEntry<T>(ctx, key);\n\n cancelGc(ctx, entry.hash);\n\n const observer: QueryObserver<T, S> = {\n ...opts,\n listener: onStoreChange,\n previous: toObserverState(entry, peekObserver),\n };\n\n entry.observers.add(observer as QueryObserver<unknown, unknown>);\n\n return () => {\n entry.observers.delete(observer as QueryObserver<unknown, unknown>);\n\n if (entry.observers.size === 0) {\n scheduleGc(ctx, entry, entry.lastConfig?.gcTime ?? ctx.gcTimeDefault);\n }\n };\n },\n };\n}\n"],"mappings":"4GAYA,IAAM,EAAqC,OAAO,OAAO,CACvD,KAAM,IAAA,GACN,MAAO,KACP,WAAY,GACZ,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,CAAC,EAED,SAAgB,EAAe,EAAqC,CAuBlE,OAtBI,EAAM,SAAW,UACZ,CACL,KAAM,IAAA,GACN,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,EAGE,EAAM,SAAW,UACZ,CACL,KAAM,EAAM,KACZ,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,EAAM,SACnB,EAGK,CACL,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,QACR,UAAW,EAAM,SACnB,CACF,CAEA,SAAgB,EAAsB,EAAsB,EAA8C,CACxG,IAAM,EAAO,EAAY,CAAK,EAE9B,GAAI,EAAK,SAAW,UAAW,CAC7B,IAAM,EAAc,EAAa,EAAS,eAAwD,EAElG,MAAO,CAAE,GAAG,EAAM,KAAM,CAAY,CACtC,CAEA,IAAM,EAAW,EAAS,OAAS,EAAS,OAAO,EAAK,IAAqB,EAAK,EAAK,KAEvF,MAAO,CAAE,GAAG,EAAM,KAAM,CAAS,CACnC,CAEA,SAAgB,EAAU,EAA4B,CACpD,IAAK,IAAM,KAAY,EAAM,UAAW,CACtC,IAAM,EAAQ,EACR,EAAO,EAAgB,EAAO,CAAK,EACnC,EAAO,EAAM,SAEb,EAAgB,OAAO,GAAG,GAAM,KAAM,EAAK,IAAI,EAC/C,EACJ,CAAC,CAAC,GAAQ,EAAK,SAAW,EAAK,QAAU,EAAK,aAAe,EAAK,YAAc,OAAO,GAAG,EAAK,MAAO,EAAK,KAAK,GAE/F,EAAM,OACrB,GAAiB,EACjB,GAAiB,GAAM,YAAc,EAAK,WAAa,KAI3D,EAAM,SAAW,EACjB,EAAM,SAAS,EACjB,CACF,CAEA,SAAgB,EACd,EACA,EACA,EAC0B,CAC1B,IAAM,EAAoC,CAAE,aAAgB,CAAC,EAAG,GAAG,CAAK,EAExE,MAAO,CACL,MAAsB,CACpB,IAAM,EAAQ,EAAI,QAAQ,IAAI,EAAQ,CAAG,CAAC,EAI1C,OAFK,EAEE,EAAgB,EAAO,CAAY,EAFvB,CAGrB,EAEA,UAAU,EAAwC,CAChD,IAAM,EAAQ,EAAe,EAAK,CAAG,EAErC,EAAS,EAAK,EAAM,IAAI,EAExB,IAAM,EAAgC,CACpC,GAAG,EACH,SAAU,EACV,SAAU,EAAgB,EAAO,CAAY,CAC/C,EAIA,OAFA,EAAM,UAAU,IAAI,CAA2C,MAElD,CACX,EAAM,UAAU,OAAO,CAA2C,EAE9D,EAAM,UAAU,OAAS,GAC3B,EAAW,EAAK,EAAO,EAAM,YAAY,QAAU,EAAI,aAAa,CAExE,CACF,CACF,CACF"}
1
+ {"version":3,"file":"query-observe.js","names":[],"sources":["../src/query-observe.ts"],"sourcesContent":["import type { CacheContext, CacheEntry } from './query-cache';\nimport type { AsyncState, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { cancelGc, ensureEntry, hashKey, resolveValue, scheduleGc } from './query-cache';\n\nexport type QueryObserver<T, S> = {\n listener: () => void;\n placeholderData?: S | (() => S | undefined);\n previous?: QueryState<S>;\n select?: (data: T | undefined) => S | undefined;\n};\n\nconst LOADING_STATE: AsyncState<unknown> = Object.freeze({\n data: undefined,\n error: null,\n isFetching: false,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n});\n\nexport function toBaseState<T>(entry: CacheEntry<T>): QueryState<T> {\n if (entry.status === 'loading') {\n return {\n data: undefined,\n error: null,\n isFetching: entry.isFetching,\n isLoading: true,\n status: 'loading',\n updatedAt: undefined,\n } as QueryState<T>;\n }\n\n if (entry.status === 'success') {\n return {\n data: entry.data as T,\n error: null,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'success',\n updatedAt: entry.updatedAt as number,\n };\n }\n\n return {\n data: entry.data,\n error: entry.error as Error,\n isFetching: entry.isFetching,\n isLoading: false,\n status: 'error',\n updatedAt: entry.updatedAt as number,\n };\n}\n\nexport function toObserverState<T, S>(entry: CacheEntry<T>, observer: QueryObserver<T, S>): QueryState<S> {\n const base = toBaseState(entry);\n\n if (base.status === 'loading') {\n const placeholder = resolveValue(observer.placeholderData as S | (() => S | undefined) | undefined);\n\n return { ...base, data: placeholder } as QueryState<S>;\n }\n\n const selected = observer.select ? observer.select(base.data as T | undefined) : (base.data as S | undefined);\n\n return { ...base, data: selected } as QueryState<S>;\n}\n\nexport function notify<T>(entry: CacheEntry<T>): void {\n for (const observer of entry.observers) {\n const typed = observer as QueryObserver<T, unknown>;\n const next = toObserverState(entry, typed);\n const prev = typed.previous;\n\n const dataUnchanged = Object.is(prev?.data, next.data);\n const metaUnchanged =\n !!prev && prev.status === next.status && prev.isFetching === next.isFetching && Object.is(prev.error, next.error);\n\n const shouldSkip = typed.select\n ? metaUnchanged && dataUnchanged\n : metaUnchanged && prev?.updatedAt === next.updatedAt && dataUnchanged;\n\n if (shouldSkip) continue;\n\n typed.previous = next;\n typed.listener();\n }\n}\n\nexport function watchInternal<T = unknown, S = T>(\n ctx: CacheContext,\n key: Parameters<typeof ensureEntry>[1],\n opts?: { placeholderData?: S | (() => S | undefined); select?: (data: T | undefined) => S | undefined },\n): SyncStore<QueryState<S>> {\n const peekObserver: QueryObserver<T, S> = { listener: () => {}, ...opts };\n\n return {\n peek(): QueryState<S> {\n const entry = ctx.entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return LOADING_STATE as QueryState<S>;\n\n return toObserverState(entry, peekObserver);\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n if (ctx.disposed) return () => {};\n\n const entry = ensureEntry<T>(ctx, key);\n\n cancelGc(ctx, entry.hash);\n\n const observer: QueryObserver<T, S> = {\n ...opts,\n listener: onStoreChange,\n previous: toObserverState(entry, peekObserver),\n };\n\n entry.observers.add(observer as QueryObserver<unknown, unknown>);\n\n return () => {\n entry.observers.delete(observer as QueryObserver<unknown, unknown>);\n\n if (entry.observers.size === 0) {\n scheduleGc(ctx, entry, entry.lastConfig?.gcTime ?? ctx.gcTimeDefault);\n }\n };\n },\n };\n}\n"],"mappings":"4GAYA,IAAM,EAAqC,OAAO,OAAO,CACvD,KAAM,IAAA,GACN,MAAO,KACP,WAAY,GACZ,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,CAAC,EAED,SAAgB,EAAe,EAAqC,CAuBlE,OAtBI,EAAM,SAAW,UACZ,CACL,KAAM,IAAA,GACN,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,IAAA,EACb,EAGE,EAAM,SAAW,UACZ,CACL,KAAM,EAAM,KACZ,MAAO,KACP,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,UACR,UAAW,EAAM,SACnB,EAGK,CACL,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,WAAY,EAAM,WAClB,UAAW,GACX,OAAQ,QACR,UAAW,EAAM,SACnB,CACF,CAEA,SAAgB,EAAsB,EAAsB,EAA8C,CACxG,IAAM,EAAO,EAAY,CAAK,EAE9B,GAAI,EAAK,SAAW,UAAW,CAC7B,IAAM,EAAc,EAAa,EAAS,eAAwD,EAElG,MAAO,CAAE,GAAG,EAAM,KAAM,CAAY,CACtC,CAEA,IAAM,EAAW,EAAS,OAAS,EAAS,OAAO,EAAK,IAAqB,EAAK,EAAK,KAEvF,MAAO,CAAE,GAAG,EAAM,KAAM,CAAS,CACnC,CAEA,SAAgB,EAAU,EAA4B,CACpD,IAAK,IAAM,KAAY,EAAM,UAAW,CACtC,IAAM,EAAQ,EACR,EAAO,EAAgB,EAAO,CAAK,EACnC,EAAO,EAAM,SAEb,EAAgB,OAAO,GAAG,GAAM,KAAM,EAAK,IAAI,EAC/C,EACJ,CAAC,CAAC,GAAQ,EAAK,SAAW,EAAK,QAAU,EAAK,aAAe,EAAK,YAAc,OAAO,GAAG,EAAK,MAAO,EAAK,KAAK,GAE/F,EAAM,OACrB,GAAiB,EACjB,GAAiB,GAAM,YAAc,EAAK,WAAa,KAI3D,EAAM,SAAW,EACjB,EAAM,SAAS,EACjB,CACF,CAEA,SAAgB,EACd,EACA,EACA,EAC0B,CAC1B,IAAM,EAAoC,CAAE,aAAgB,CAAC,EAAG,GAAG,CAAK,EAExE,MAAO,CACL,MAAsB,CACpB,IAAM,EAAQ,EAAI,QAAQ,IAAI,EAAQ,CAAG,CAAC,EAI1C,OAFK,EAEE,EAAgB,EAAO,CAAY,EAFvB,CAGrB,EAEA,UAAU,EAAwC,CAChD,GAAI,EAAI,SAAU,UAAa,CAAC,EAEhC,IAAM,EAAQ,EAAe,EAAK,CAAG,EAErC,EAAS,EAAK,EAAM,IAAI,EAExB,IAAM,EAAgC,CACpC,GAAG,EACH,SAAU,EACV,SAAU,EAAgB,EAAO,CAAY,CAC/C,EAIA,OAFA,EAAM,UAAU,IAAI,CAA2C,MAElD,CACX,EAAM,UAAU,OAAO,CAA2C,EAE9D,EAAM,UAAU,OAAS,GAC3B,EAAW,EAAK,EAAO,EAAM,YAAY,QAAU,EAAI,aAAa,CAExE,CACF,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"query.cjs","names":[],"sources":["../src/query.ts"],"sourcesContent":["import type { RetryOptions } from './retry';\nimport type { QueryKey, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport {\n buildCacheContext,\n ensureEntry,\n evictEntry,\n hashAtom,\n hashKey,\n isKeyOrPrefix,\n makeFetchConfig,\n resolveValue,\n scheduleGc,\n startFetch,\n type CacheContext,\n type CacheEntry,\n type FetchConfig,\n type QueryFn,\n type QueryFnContext,\n} from './query-cache';\nimport { notify, toBaseState, watchInternal } from './query-observe';\n\nexport type { QueryFn, QueryFnContext };\n\n/**\n * Options for `qc.fetch()`. Always throws on error and returns `T`.\n * Use `observe()` for reactive subscriptions with select/placeholder support.\n */\nexport type QueryOptions<T> = {\n /**\n * When `false` the fetch is skipped and the current cached value is returned.\n * If no cached data exists yet, the return value is `undefined` — use `getState()`\n * to inspect entry status in that case.\n * Defaults to `true`.\n */\n enabled?: boolean;\n fn: QueryFn<T>;\n gcTime?: number;\n /** Pre-seed the cache as a successful entry when no data exists. Subject to normal staleTime checks. */\n initialData?: T | (() => T | undefined);\n key: QueryKey;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Shared reactive fields for `observe()`.\n */\ntype ObserveExtras<T, S> = {\n /**\n * Temporary value shown while a fetch is in-flight and no cached data exists.\n * Does not affect cache state — only the value returned by `store.peek()`.\n */\n placeholderData?: S | (() => S | undefined);\n /** Transform the cached data before it is delivered to store subscribers. */\n select?: (data: T | undefined) => S | undefined;\n};\n\n/**\n * Options for `qc.observe()`. Two forms:\n *\n * - `fetch?: true` (default) — triggers a background fetch; `fn` is required.\n * - `fetch: false` — read-only store; `fn` is not required or called.\n *\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n */\nexport type ObserveOptions<T, S = T> =\n | ({ fetch?: true } & QueryOptions<T> & ObserveExtras<T, S>)\n | ({ fetch: false; key: QueryKey } & Partial<QueryOptions<T>> & ObserveExtras<T, S>);\n\nexport type QueryClientOptions = {\n /**\n * Optional fetch implementation injected by `createCourier` so that\n * query fetches flow through the shared interceptor pipeline (auth, logging, etc.).\n * When omitted `globalThis.fetch` is used directly.\n */\n fetch?: typeof globalThis.fetch;\n gcTime?: number;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Creates a key-based query cache with request deduplication, retries, stale-time/gc-time\n * eviction, and `observe()`-based reactive subscriptions (`SyncStore`-compatible).\n *\n * @example\n * ```ts\n * const qc = createQuery({ staleTime: 30_000 });\n *\n * // One-shot fetch, always resolves or throws\n * const user = await qc.fetch({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Reactive subscription — errors surface via store.peek().status, not rejection\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n * const unsub = store.subscribe(() => console.log(store.peek()));\n *\n * // later:\n * qc.dispose();\n * ```\n */\nexport function createQuery(opts?: QueryClientOptions) {\n let disposed = false;\n const disposeController = new AbortController();\n const entries = new Map<string, CacheEntry>();\n const gcTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n const ctx: CacheContext = buildCacheContext(\n {\n delay: opts?.delay,\n gcTime: opts?.gcTime,\n shouldRetry: opts?.shouldRetry,\n staleTime: opts?.staleTime,\n times: opts?.times,\n },\n entries,\n gcTimers,\n notify,\n );\n\n async function fetchQuery<T>(options: QueryOptions<T>): Promise<T> {\n if (disposed) throw new CourierDisposedError('QueryClient');\n\n const { enabled = true, fn, gcTime, initialData, key, staleTime, ...retryOpts } = options;\n\n // When disabled and no existing entry, return undefined without creating a cache entry.\n if (!enabled) {\n const h = hashKey(key);\n const existing = entries.get(h);\n\n return (existing?.data as T | undefined) ?? (undefined as unknown as T);\n }\n\n const entry = ensureEntry<T>(ctx, key);\n const config = makeFetchConfig<T>(ctx, { fn, gcTime, staleTime, ...retryOpts });\n\n if (initialData !== undefined && entry.status === 'loading' && entry.data === undefined) {\n const initVal = resolveValue(initialData);\n\n if (initVal !== undefined) {\n entry.data = initVal;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = Date.now();\n }\n }\n\n if (\n entry.status === 'success' &&\n entry.updatedAt !== undefined &&\n Date.now() - entry.updatedAt < config.staleTime &&\n !entry.isFetching\n ) {\n entry.lastConfig = config;\n\n return entry.data as T;\n }\n\n entry.lastConfig = config;\n\n if (entry.inflight) return entry.inflight.promise;\n\n return startFetch(ctx, entry, config);\n }\n\n function revalidateObservedEntry<T>(entry: CacheEntry<T>): void {\n if (!entry.lastConfig) {\n evictEntry(ctx, entry);\n\n return;\n }\n\n if (entry.isFetching) return;\n\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<T>).catch(() => {});\n }\n\n function invalidate(key: QueryKey): void {\n const prefixHash = key.map((k) => hashAtom(k));\n\n for (const entry of [...entries.values()]) {\n if (isKeyOrPrefix(entry, prefixHash)) {\n if (entry.observers.size > 0) {\n revalidateObservedEntry(entry);\n continue;\n }\n\n evictEntry(ctx, entry);\n }\n }\n }\n\n function set<T>(key: QueryKey, data: T, opts?: { gcTime?: number; updatedAt?: number }): void;\n function set<T>(\n key: QueryKey,\n updater: (old: T | undefined) => T,\n opts?: { gcTime?: number; updatedAt?: number },\n ): void;\n function set<T>(\n key: QueryKey,\n dataOrUpdater: T | ((old: T | undefined) => T),\n opts?: { gcTime?: number; updatedAt?: number },\n ): void {\n const entry = ensureEntry<T>(ctx, key);\n const gcTime = opts?.gcTime ?? entry.lastConfig?.gcTime ?? ctx.gcTimeDefault;\n\n entry.data =\n typeof dataOrUpdater === 'function' ? (dataOrUpdater as (old: T | undefined) => T)(entry.data) : dataOrUpdater;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = opts?.updatedAt ?? Date.now();\n entry.isFetching = false;\n entry.inflight = null;\n notify(entry);\n scheduleGc(ctx, entry, gcTime);\n }\n\n function isStaleAndRevalidatable(entry: CacheEntry): boolean {\n if (entry.observers.size === 0 || !entry.lastConfig || entry.isFetching) return false;\n\n if (entry.status === 'success' && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n if (entry.status === 'error' && entry.data !== undefined && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n return false;\n }\n\n /**\n * Returns a `SyncStore` for `key` and optionally triggers a background fetch.\n *\n * **With fetch (default):** pass `fn` and the store triggers a background fetch on creation.\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n *\n * **Without fetch:** pass `fetch: false` — `fn` is not required. The store reflects\n * whatever is in the cache. Useful when another path is responsible for populating the entry.\n *\n * @example\n * ```ts\n * // Fetch form\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Read-only form — no fn required\n * const store = qc.observe({ key: ['users', 1], fetch: false });\n * ```\n */\n function observe<T = unknown, S = T>(options: ObserveOptions<T, S>): SyncStore<QueryState<S>> {\n const {\n fetch: shouldFetch = true,\n key,\n placeholderData,\n select,\n } = options as {\n fetch?: boolean;\n key: QueryKey;\n placeholderData?: S | (() => S | undefined);\n select?: (data: T | undefined) => S | undefined;\n };\n\n const store = watchInternal<T, S>(ctx, key, { placeholderData, select });\n\n if (shouldFetch && (options as { fn?: QueryFn<T> }).fn) {\n fetchQuery({ ...(options as QueryOptions<T>) }).catch(() => {});\n }\n\n return store;\n }\n\n return {\n cancel(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (!entry?.inflight) return;\n\n entry.inflight.controller.abort();\n },\n\n cancelAll(): void {\n for (const entry of entries.values()) {\n entry.inflight?.controller.abort();\n }\n },\n\n clear(): void {\n for (const entry of [...entries.values()]) {\n evictEntry(ctx, entry);\n }\n },\n\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n ctx.disposed = true;\n disposeController.abort();\n\n for (const [, entry] of entries) {\n entry.observers.clear();\n entry.inflight?.controller.abort();\n }\n\n for (const [, timer] of gcTimers) {\n clearTimeout(timer);\n }\n\n gcTimers.clear();\n entries.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n fetch: fetchQuery,\n\n fetchMany<T = unknown>(queries: QueryOptions<T>[]): Promise<T[]> {\n return Promise.all(queries.map((q) => fetchQuery(q)));\n },\n\n get<T>(key: QueryKey): T | undefined {\n const entry = entries.get(hashKey(key));\n\n return entry ? (entry.data as T | undefined) : undefined;\n },\n\n getState<T>(key: QueryKey): QueryState<T> | null {\n const entry = entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return null;\n\n return toBaseState(entry);\n },\n\n invalidate,\n\n keys(): QueryKey[] {\n return [...entries.values()].map((e) => e.key);\n },\n\n observe,\n\n observeMany<T = unknown>(keys: QueryKey[]): SyncStore<QueryState<T>[]> {\n const stores = keys.map((k) => watchInternal<T>(ctx, k));\n\n return {\n peek(): QueryState<T>[] {\n return stores.map((s) => s.peek());\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n const unsubs = stores.map((s) => s.subscribe(onStoreChange));\n\n return () => {\n for (const u of unsubs) u();\n };\n },\n };\n },\n\n refetchStale(): void {\n for (const entry of [...entries.values()]) {\n if (isStaleAndRevalidatable(entry)) {\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<unknown>).catch(() => {});\n }\n }\n },\n\n remove(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (entry) evictEntry(ctx, entry);\n },\n\n set,\n\n get size(): number {\n return entries.size;\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport type QueryClient = ReturnType<typeof createQuery>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"query.cjs","names":[],"sources":["../src/query.ts"],"sourcesContent":["import type { RetryOptions } from './retry';\nimport type { QueryKey, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport {\n buildCacheContext,\n ensureEntry,\n evictEntry,\n hashAtom,\n hashKey,\n isKeyOrPrefix,\n makeFetchConfig,\n resolveValue,\n scheduleGc,\n startFetch,\n type CacheContext,\n type CacheEntry,\n type FetchConfig,\n type QueryFn,\n type QueryFnContext,\n} from './query-cache';\nimport { notify, toBaseState, watchInternal } from './query-observe';\n\nexport type { QueryFn, QueryFnContext };\n\n/**\n * Options for `qc.fetch()`. Always throws on error and returns `T`.\n * Use `observe()` for reactive subscriptions with select/placeholder support.\n */\nexport type QueryOptions<T> = {\n /**\n * When `false` the fetch is skipped and the current cached value is returned.\n * If no cached data exists yet, the return value is `undefined` — use `getState()`\n * to inspect entry status in that case.\n * Defaults to `true`.\n */\n enabled?: boolean;\n fn: QueryFn<T>;\n gcTime?: number;\n /** Pre-seed the cache as a successful entry when no data exists. Subject to normal staleTime checks. */\n initialData?: T | (() => T | undefined);\n key: QueryKey;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Shared reactive fields for `observe()`.\n */\ntype ObserveExtras<T, S> = {\n /**\n * Temporary value shown while a fetch is in-flight and no cached data exists.\n * Does not affect cache state — only the value returned by `store.peek()`.\n */\n placeholderData?: S | (() => S | undefined);\n /** Transform the cached data before it is delivered to store subscribers. */\n select?: (data: T | undefined) => S | undefined;\n};\n\n/**\n * Options for `qc.observe()`. Two forms:\n *\n * - `fetch?: true` (default) — triggers a background fetch; `fn` is required.\n * - `fetch: false` — read-only store; `fn` is not required or called.\n *\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n */\nexport type ObserveOptions<T, S = T> =\n | ({ fetch?: true } & QueryOptions<T> & ObserveExtras<T, S>)\n | ({ fetch: false; key: QueryKey } & Partial<QueryOptions<T>> & ObserveExtras<T, S>);\n\nexport type QueryClientOptions = {\n gcTime?: number;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Creates a key-based query cache with request deduplication, retries, stale-time/gc-time\n * eviction, and `observe()`-based reactive subscriptions (`SyncStore`-compatible).\n *\n * @example\n * ```ts\n * const qc = createQuery({ staleTime: 30_000 });\n *\n * // One-shot fetch, always resolves or throws\n * const user = await qc.fetch({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Reactive subscription — errors surface via store.peek().status, not rejection\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n * const unsub = store.subscribe(() => console.log(store.peek()));\n *\n * // later:\n * qc.dispose();\n * ```\n */\nexport function createQuery(opts?: QueryClientOptions) {\n let disposed = false;\n const disposeController = new AbortController();\n const entries = new Map<string, CacheEntry>();\n const gcTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n const ctx: CacheContext = buildCacheContext(\n {\n delay: opts?.delay,\n gcTime: opts?.gcTime,\n shouldRetry: opts?.shouldRetry,\n staleTime: opts?.staleTime,\n times: opts?.times,\n },\n entries,\n gcTimers,\n notify,\n );\n\n async function fetchQuery<T>(options: QueryOptions<T>): Promise<T> {\n if (disposed) throw new CourierDisposedError('QueryClient');\n\n const { enabled = true, fn, gcTime, initialData, key, staleTime, ...retryOpts } = options;\n\n // When disabled and no existing entry, return undefined without creating a cache entry.\n if (!enabled) {\n const h = hashKey(key);\n const existing = entries.get(h);\n\n return (existing?.data as T | undefined) ?? (undefined as unknown as T);\n }\n\n const entry = ensureEntry<T>(ctx, key);\n const config = makeFetchConfig<T>(ctx, { fn, gcTime, staleTime, ...retryOpts });\n\n if (initialData !== undefined && entry.status === 'loading' && entry.data === undefined) {\n const initVal = resolveValue(initialData);\n\n if (initVal !== undefined) {\n entry.data = initVal;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = Date.now();\n }\n }\n\n if (\n entry.status === 'success' &&\n entry.updatedAt !== undefined &&\n Date.now() - entry.updatedAt < config.staleTime &&\n !entry.isFetching\n ) {\n entry.lastConfig = config;\n\n return entry.data as T;\n }\n\n entry.lastConfig = config;\n\n if (entry.inflight) return entry.inflight.promise;\n\n return startFetch(ctx, entry, config);\n }\n\n function revalidateObservedEntry<T>(entry: CacheEntry<T>): void {\n if (!entry.lastConfig) {\n evictEntry(ctx, entry);\n\n return;\n }\n\n if (entry.isFetching) return;\n\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<T>).catch(() => {});\n }\n\n function invalidate(key: QueryKey): void {\n const prefixHash = key.map((k) => hashAtom(k));\n\n for (const entry of [...entries.values()]) {\n if (isKeyOrPrefix(entry, prefixHash)) {\n if (entry.observers.size > 0) {\n revalidateObservedEntry(entry);\n continue;\n }\n\n evictEntry(ctx, entry);\n }\n }\n }\n\n function set<T>(key: QueryKey, data: T, opts?: { gcTime?: number; updatedAt?: number }): void;\n function set<T>(\n key: QueryKey,\n updater: (old: T | undefined) => T,\n opts?: { gcTime?: number; updatedAt?: number },\n ): void;\n function set<T>(\n key: QueryKey,\n dataOrUpdater: T | ((old: T | undefined) => T),\n opts?: { gcTime?: number; updatedAt?: number },\n ): void {\n const entry = ensureEntry<T>(ctx, key);\n const gcTime = opts?.gcTime ?? entry.lastConfig?.gcTime ?? ctx.gcTimeDefault;\n\n entry.data =\n typeof dataOrUpdater === 'function' ? (dataOrUpdater as (old: T | undefined) => T)(entry.data) : dataOrUpdater;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = opts?.updatedAt ?? Date.now();\n entry.isFetching = false;\n entry.inflight = null;\n notify(entry);\n scheduleGc(ctx, entry, gcTime);\n }\n\n function isStaleAndRevalidatable(entry: CacheEntry): boolean {\n if (entry.observers.size === 0 || !entry.lastConfig || entry.isFetching) return false;\n\n if (entry.status === 'success' && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n if (entry.status === 'error' && entry.data !== undefined && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n return false;\n }\n\n /**\n * Returns a `SyncStore` for `key` and optionally triggers a background fetch.\n *\n * **With fetch (default):** pass `fn` and the store triggers a background fetch on creation.\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n *\n * **Without fetch:** pass `fetch: false` — `fn` is not required. The store reflects\n * whatever is in the cache. Useful when another path is responsible for populating the entry.\n *\n * @example\n * ```ts\n * // Fetch form\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Read-only form — no fn required\n * const store = qc.observe({ key: ['users', 1], fetch: false });\n * ```\n */\n function observe<T = unknown, S = T>(options: ObserveOptions<T, S>): SyncStore<QueryState<S>> {\n const {\n fetch: shouldFetch = true,\n key,\n placeholderData,\n select,\n } = options as {\n fetch?: boolean;\n key: QueryKey;\n placeholderData?: S | (() => S | undefined);\n select?: (data: T | undefined) => S | undefined;\n };\n\n const store = watchInternal<T, S>(ctx, key, { placeholderData, select });\n\n if (shouldFetch && (options as { fn?: QueryFn<T> }).fn) {\n fetchQuery({ ...(options as QueryOptions<T>) }).catch(() => {});\n }\n\n return store;\n }\n\n return {\n cancel(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (!entry?.inflight) return;\n\n entry.inflight.controller.abort();\n },\n\n cancelAll(): void {\n for (const entry of entries.values()) {\n entry.inflight?.controller.abort();\n }\n },\n\n clear(): void {\n for (const entry of [...entries.values()]) {\n evictEntry(ctx, entry);\n }\n },\n\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n ctx.disposed = true;\n disposeController.abort();\n\n for (const [, entry] of entries) {\n entry.observers.clear();\n entry.inflight?.controller.abort();\n }\n\n for (const [, timer] of gcTimers) {\n clearTimeout(timer);\n }\n\n gcTimers.clear();\n entries.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n fetch: fetchQuery,\n\n fetchMany<T = unknown>(queries: QueryOptions<T>[]): Promise<T[]> {\n return Promise.all(queries.map((q) => fetchQuery(q)));\n },\n\n get<T>(key: QueryKey): T | undefined {\n const entry = entries.get(hashKey(key));\n\n return entry ? (entry.data as T | undefined) : undefined;\n },\n\n getState<T>(key: QueryKey): QueryState<T> | null {\n const entry = entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return null;\n\n return toBaseState(entry);\n },\n\n invalidate,\n\n keys(): QueryKey[] {\n return [...entries.values()].map((e) => e.key);\n },\n\n observe,\n\n observeMany<T = unknown>(keys: QueryKey[]): SyncStore<QueryState<T>[]> {\n const stores = keys.map((k) => watchInternal<T>(ctx, k));\n\n return {\n peek(): QueryState<T>[] {\n return stores.map((s) => s.peek());\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n const unsubs = stores.map((s) => s.subscribe(onStoreChange));\n\n return () => {\n for (const u of unsubs) u();\n };\n },\n };\n },\n\n refetchStale(): void {\n for (const entry of [...entries.values()]) {\n if (isStaleAndRevalidatable(entry)) {\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<unknown>).catch(() => {});\n }\n }\n },\n\n remove(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (entry) evictEntry(ctx, entry);\n },\n\n set,\n\n get size(): number {\n return entries.size;\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport type QueryClient = ReturnType<typeof createQuery>;\n"],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -48,12 +48,6 @@ export type ObserveOptions<T, S = T> = ({
48
48
  key: QueryKey;
49
49
  } & Partial<QueryOptions<T>> & ObserveExtras<T, S>);
50
50
  export type QueryClientOptions = {
51
- /**
52
- * Optional fetch implementation injected by `createCourier` so that
53
- * query fetches flow through the shared interceptor pipeline (auth, logging, etc.).
54
- * When omitted `globalThis.fetch` is used directly.
55
- */
56
- fetch?: typeof globalThis.fetch;
57
51
  gcTime?: number;
58
52
  staleTime?: number;
59
53
  } & RetryOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","names":[],"sources":["../src/query.ts"],"sourcesContent":["import type { RetryOptions } from './retry';\nimport type { QueryKey, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport {\n buildCacheContext,\n ensureEntry,\n evictEntry,\n hashAtom,\n hashKey,\n isKeyOrPrefix,\n makeFetchConfig,\n resolveValue,\n scheduleGc,\n startFetch,\n type CacheContext,\n type CacheEntry,\n type FetchConfig,\n type QueryFn,\n type QueryFnContext,\n} from './query-cache';\nimport { notify, toBaseState, watchInternal } from './query-observe';\n\nexport type { QueryFn, QueryFnContext };\n\n/**\n * Options for `qc.fetch()`. Always throws on error and returns `T`.\n * Use `observe()` for reactive subscriptions with select/placeholder support.\n */\nexport type QueryOptions<T> = {\n /**\n * When `false` the fetch is skipped and the current cached value is returned.\n * If no cached data exists yet, the return value is `undefined` — use `getState()`\n * to inspect entry status in that case.\n * Defaults to `true`.\n */\n enabled?: boolean;\n fn: QueryFn<T>;\n gcTime?: number;\n /** Pre-seed the cache as a successful entry when no data exists. Subject to normal staleTime checks. */\n initialData?: T | (() => T | undefined);\n key: QueryKey;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Shared reactive fields for `observe()`.\n */\ntype ObserveExtras<T, S> = {\n /**\n * Temporary value shown while a fetch is in-flight and no cached data exists.\n * Does not affect cache state — only the value returned by `store.peek()`.\n */\n placeholderData?: S | (() => S | undefined);\n /** Transform the cached data before it is delivered to store subscribers. */\n select?: (data: T | undefined) => S | undefined;\n};\n\n/**\n * Options for `qc.observe()`. Two forms:\n *\n * - `fetch?: true` (default) — triggers a background fetch; `fn` is required.\n * - `fetch: false` — read-only store; `fn` is not required or called.\n *\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n */\nexport type ObserveOptions<T, S = T> =\n | ({ fetch?: true } & QueryOptions<T> & ObserveExtras<T, S>)\n | ({ fetch: false; key: QueryKey } & Partial<QueryOptions<T>> & ObserveExtras<T, S>);\n\nexport type QueryClientOptions = {\n /**\n * Optional fetch implementation injected by `createCourier` so that\n * query fetches flow through the shared interceptor pipeline (auth, logging, etc.).\n * When omitted `globalThis.fetch` is used directly.\n */\n fetch?: typeof globalThis.fetch;\n gcTime?: number;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Creates a key-based query cache with request deduplication, retries, stale-time/gc-time\n * eviction, and `observe()`-based reactive subscriptions (`SyncStore`-compatible).\n *\n * @example\n * ```ts\n * const qc = createQuery({ staleTime: 30_000 });\n *\n * // One-shot fetch, always resolves or throws\n * const user = await qc.fetch({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Reactive subscription — errors surface via store.peek().status, not rejection\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n * const unsub = store.subscribe(() => console.log(store.peek()));\n *\n * // later:\n * qc.dispose();\n * ```\n */\nexport function createQuery(opts?: QueryClientOptions) {\n let disposed = false;\n const disposeController = new AbortController();\n const entries = new Map<string, CacheEntry>();\n const gcTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n const ctx: CacheContext = buildCacheContext(\n {\n delay: opts?.delay,\n gcTime: opts?.gcTime,\n shouldRetry: opts?.shouldRetry,\n staleTime: opts?.staleTime,\n times: opts?.times,\n },\n entries,\n gcTimers,\n notify,\n );\n\n async function fetchQuery<T>(options: QueryOptions<T>): Promise<T> {\n if (disposed) throw new CourierDisposedError('QueryClient');\n\n const { enabled = true, fn, gcTime, initialData, key, staleTime, ...retryOpts } = options;\n\n // When disabled and no existing entry, return undefined without creating a cache entry.\n if (!enabled) {\n const h = hashKey(key);\n const existing = entries.get(h);\n\n return (existing?.data as T | undefined) ?? (undefined as unknown as T);\n }\n\n const entry = ensureEntry<T>(ctx, key);\n const config = makeFetchConfig<T>(ctx, { fn, gcTime, staleTime, ...retryOpts });\n\n if (initialData !== undefined && entry.status === 'loading' && entry.data === undefined) {\n const initVal = resolveValue(initialData);\n\n if (initVal !== undefined) {\n entry.data = initVal;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = Date.now();\n }\n }\n\n if (\n entry.status === 'success' &&\n entry.updatedAt !== undefined &&\n Date.now() - entry.updatedAt < config.staleTime &&\n !entry.isFetching\n ) {\n entry.lastConfig = config;\n\n return entry.data as T;\n }\n\n entry.lastConfig = config;\n\n if (entry.inflight) return entry.inflight.promise;\n\n return startFetch(ctx, entry, config);\n }\n\n function revalidateObservedEntry<T>(entry: CacheEntry<T>): void {\n if (!entry.lastConfig) {\n evictEntry(ctx, entry);\n\n return;\n }\n\n if (entry.isFetching) return;\n\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<T>).catch(() => {});\n }\n\n function invalidate(key: QueryKey): void {\n const prefixHash = key.map((k) => hashAtom(k));\n\n for (const entry of [...entries.values()]) {\n if (isKeyOrPrefix(entry, prefixHash)) {\n if (entry.observers.size > 0) {\n revalidateObservedEntry(entry);\n continue;\n }\n\n evictEntry(ctx, entry);\n }\n }\n }\n\n function set<T>(key: QueryKey, data: T, opts?: { gcTime?: number; updatedAt?: number }): void;\n function set<T>(\n key: QueryKey,\n updater: (old: T | undefined) => T,\n opts?: { gcTime?: number; updatedAt?: number },\n ): void;\n function set<T>(\n key: QueryKey,\n dataOrUpdater: T | ((old: T | undefined) => T),\n opts?: { gcTime?: number; updatedAt?: number },\n ): void {\n const entry = ensureEntry<T>(ctx, key);\n const gcTime = opts?.gcTime ?? entry.lastConfig?.gcTime ?? ctx.gcTimeDefault;\n\n entry.data =\n typeof dataOrUpdater === 'function' ? (dataOrUpdater as (old: T | undefined) => T)(entry.data) : dataOrUpdater;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = opts?.updatedAt ?? Date.now();\n entry.isFetching = false;\n entry.inflight = null;\n notify(entry);\n scheduleGc(ctx, entry, gcTime);\n }\n\n function isStaleAndRevalidatable(entry: CacheEntry): boolean {\n if (entry.observers.size === 0 || !entry.lastConfig || entry.isFetching) return false;\n\n if (entry.status === 'success' && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n if (entry.status === 'error' && entry.data !== undefined && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n return false;\n }\n\n /**\n * Returns a `SyncStore` for `key` and optionally triggers a background fetch.\n *\n * **With fetch (default):** pass `fn` and the store triggers a background fetch on creation.\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n *\n * **Without fetch:** pass `fetch: false` — `fn` is not required. The store reflects\n * whatever is in the cache. Useful when another path is responsible for populating the entry.\n *\n * @example\n * ```ts\n * // Fetch form\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Read-only form — no fn required\n * const store = qc.observe({ key: ['users', 1], fetch: false });\n * ```\n */\n function observe<T = unknown, S = T>(options: ObserveOptions<T, S>): SyncStore<QueryState<S>> {\n const {\n fetch: shouldFetch = true,\n key,\n placeholderData,\n select,\n } = options as {\n fetch?: boolean;\n key: QueryKey;\n placeholderData?: S | (() => S | undefined);\n select?: (data: T | undefined) => S | undefined;\n };\n\n const store = watchInternal<T, S>(ctx, key, { placeholderData, select });\n\n if (shouldFetch && (options as { fn?: QueryFn<T> }).fn) {\n fetchQuery({ ...(options as QueryOptions<T>) }).catch(() => {});\n }\n\n return store;\n }\n\n return {\n cancel(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (!entry?.inflight) return;\n\n entry.inflight.controller.abort();\n },\n\n cancelAll(): void {\n for (const entry of entries.values()) {\n entry.inflight?.controller.abort();\n }\n },\n\n clear(): void {\n for (const entry of [...entries.values()]) {\n evictEntry(ctx, entry);\n }\n },\n\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n ctx.disposed = true;\n disposeController.abort();\n\n for (const [, entry] of entries) {\n entry.observers.clear();\n entry.inflight?.controller.abort();\n }\n\n for (const [, timer] of gcTimers) {\n clearTimeout(timer);\n }\n\n gcTimers.clear();\n entries.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n fetch: fetchQuery,\n\n fetchMany<T = unknown>(queries: QueryOptions<T>[]): Promise<T[]> {\n return Promise.all(queries.map((q) => fetchQuery(q)));\n },\n\n get<T>(key: QueryKey): T | undefined {\n const entry = entries.get(hashKey(key));\n\n return entry ? (entry.data as T | undefined) : undefined;\n },\n\n getState<T>(key: QueryKey): QueryState<T> | null {\n const entry = entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return null;\n\n return toBaseState(entry);\n },\n\n invalidate,\n\n keys(): QueryKey[] {\n return [...entries.values()].map((e) => e.key);\n },\n\n observe,\n\n observeMany<T = unknown>(keys: QueryKey[]): SyncStore<QueryState<T>[]> {\n const stores = keys.map((k) => watchInternal<T>(ctx, k));\n\n return {\n peek(): QueryState<T>[] {\n return stores.map((s) => s.peek());\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n const unsubs = stores.map((s) => s.subscribe(onStoreChange));\n\n return () => {\n for (const u of unsubs) u();\n };\n },\n };\n },\n\n refetchStale(): void {\n for (const entry of [...entries.values()]) {\n if (isStaleAndRevalidatable(entry)) {\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<unknown>).catch(() => {});\n }\n }\n },\n\n remove(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (entry) evictEntry(ctx, entry);\n },\n\n set,\n\n get size(): number {\n return entries.size;\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport type QueryClient = ReturnType<typeof createQuery>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"query.js","names":[],"sources":["../src/query.ts"],"sourcesContent":["import type { RetryOptions } from './retry';\nimport type { QueryKey, QueryState, SyncStore, Unsubscribe } from './types';\n\nimport { CourierDisposedError } from './errors';\nimport {\n buildCacheContext,\n ensureEntry,\n evictEntry,\n hashAtom,\n hashKey,\n isKeyOrPrefix,\n makeFetchConfig,\n resolveValue,\n scheduleGc,\n startFetch,\n type CacheContext,\n type CacheEntry,\n type FetchConfig,\n type QueryFn,\n type QueryFnContext,\n} from './query-cache';\nimport { notify, toBaseState, watchInternal } from './query-observe';\n\nexport type { QueryFn, QueryFnContext };\n\n/**\n * Options for `qc.fetch()`. Always throws on error and returns `T`.\n * Use `observe()` for reactive subscriptions with select/placeholder support.\n */\nexport type QueryOptions<T> = {\n /**\n * When `false` the fetch is skipped and the current cached value is returned.\n * If no cached data exists yet, the return value is `undefined` — use `getState()`\n * to inspect entry status in that case.\n * Defaults to `true`.\n */\n enabled?: boolean;\n fn: QueryFn<T>;\n gcTime?: number;\n /** Pre-seed the cache as a successful entry when no data exists. Subject to normal staleTime checks. */\n initialData?: T | (() => T | undefined);\n key: QueryKey;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Shared reactive fields for `observe()`.\n */\ntype ObserveExtras<T, S> = {\n /**\n * Temporary value shown while a fetch is in-flight and no cached data exists.\n * Does not affect cache state — only the value returned by `store.peek()`.\n */\n placeholderData?: S | (() => S | undefined);\n /** Transform the cached data before it is delivered to store subscribers. */\n select?: (data: T | undefined) => S | undefined;\n};\n\n/**\n * Options for `qc.observe()`. Two forms:\n *\n * - `fetch?: true` (default) — triggers a background fetch; `fn` is required.\n * - `fetch: false` — read-only store; `fn` is not required or called.\n *\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n */\nexport type ObserveOptions<T, S = T> =\n | ({ fetch?: true } & QueryOptions<T> & ObserveExtras<T, S>)\n | ({ fetch: false; key: QueryKey } & Partial<QueryOptions<T>> & ObserveExtras<T, S>);\n\nexport type QueryClientOptions = {\n gcTime?: number;\n staleTime?: number;\n} & RetryOptions;\n\n/**\n * Creates a key-based query cache with request deduplication, retries, stale-time/gc-time\n * eviction, and `observe()`-based reactive subscriptions (`SyncStore`-compatible).\n *\n * @example\n * ```ts\n * const qc = createQuery({ staleTime: 30_000 });\n *\n * // One-shot fetch, always resolves or throws\n * const user = await qc.fetch({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Reactive subscription — errors surface via store.peek().status, not rejection\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n * const unsub = store.subscribe(() => console.log(store.peek()));\n *\n * // later:\n * qc.dispose();\n * ```\n */\nexport function createQuery(opts?: QueryClientOptions) {\n let disposed = false;\n const disposeController = new AbortController();\n const entries = new Map<string, CacheEntry>();\n const gcTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\n const ctx: CacheContext = buildCacheContext(\n {\n delay: opts?.delay,\n gcTime: opts?.gcTime,\n shouldRetry: opts?.shouldRetry,\n staleTime: opts?.staleTime,\n times: opts?.times,\n },\n entries,\n gcTimers,\n notify,\n );\n\n async function fetchQuery<T>(options: QueryOptions<T>): Promise<T> {\n if (disposed) throw new CourierDisposedError('QueryClient');\n\n const { enabled = true, fn, gcTime, initialData, key, staleTime, ...retryOpts } = options;\n\n // When disabled and no existing entry, return undefined without creating a cache entry.\n if (!enabled) {\n const h = hashKey(key);\n const existing = entries.get(h);\n\n return (existing?.data as T | undefined) ?? (undefined as unknown as T);\n }\n\n const entry = ensureEntry<T>(ctx, key);\n const config = makeFetchConfig<T>(ctx, { fn, gcTime, staleTime, ...retryOpts });\n\n if (initialData !== undefined && entry.status === 'loading' && entry.data === undefined) {\n const initVal = resolveValue(initialData);\n\n if (initVal !== undefined) {\n entry.data = initVal;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = Date.now();\n }\n }\n\n if (\n entry.status === 'success' &&\n entry.updatedAt !== undefined &&\n Date.now() - entry.updatedAt < config.staleTime &&\n !entry.isFetching\n ) {\n entry.lastConfig = config;\n\n return entry.data as T;\n }\n\n entry.lastConfig = config;\n\n if (entry.inflight) return entry.inflight.promise;\n\n return startFetch(ctx, entry, config);\n }\n\n function revalidateObservedEntry<T>(entry: CacheEntry<T>): void {\n if (!entry.lastConfig) {\n evictEntry(ctx, entry);\n\n return;\n }\n\n if (entry.isFetching) return;\n\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<T>).catch(() => {});\n }\n\n function invalidate(key: QueryKey): void {\n const prefixHash = key.map((k) => hashAtom(k));\n\n for (const entry of [...entries.values()]) {\n if (isKeyOrPrefix(entry, prefixHash)) {\n if (entry.observers.size > 0) {\n revalidateObservedEntry(entry);\n continue;\n }\n\n evictEntry(ctx, entry);\n }\n }\n }\n\n function set<T>(key: QueryKey, data: T, opts?: { gcTime?: number; updatedAt?: number }): void;\n function set<T>(\n key: QueryKey,\n updater: (old: T | undefined) => T,\n opts?: { gcTime?: number; updatedAt?: number },\n ): void;\n function set<T>(\n key: QueryKey,\n dataOrUpdater: T | ((old: T | undefined) => T),\n opts?: { gcTime?: number; updatedAt?: number },\n ): void {\n const entry = ensureEntry<T>(ctx, key);\n const gcTime = opts?.gcTime ?? entry.lastConfig?.gcTime ?? ctx.gcTimeDefault;\n\n entry.data =\n typeof dataOrUpdater === 'function' ? (dataOrUpdater as (old: T | undefined) => T)(entry.data) : dataOrUpdater;\n entry.error = null;\n entry.status = 'success';\n entry.updatedAt = opts?.updatedAt ?? Date.now();\n entry.isFetching = false;\n entry.inflight = null;\n notify(entry);\n scheduleGc(ctx, entry, gcTime);\n }\n\n function isStaleAndRevalidatable(entry: CacheEntry): boolean {\n if (entry.observers.size === 0 || !entry.lastConfig || entry.isFetching) return false;\n\n if (entry.status === 'success' && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n if (entry.status === 'error' && entry.data !== undefined && entry.updatedAt !== undefined) {\n return Date.now() - entry.updatedAt >= entry.lastConfig.staleTime;\n }\n\n return false;\n }\n\n /**\n * Returns a `SyncStore` for `key` and optionally triggers a background fetch.\n *\n * **With fetch (default):** pass `fn` and the store triggers a background fetch on creation.\n * Errors surface via `store.peek().status === 'error'`, not via Promise rejection.\n *\n * **Without fetch:** pass `fetch: false` — `fn` is not required. The store reflects\n * whatever is in the cache. Useful when another path is responsible for populating the entry.\n *\n * @example\n * ```ts\n * // Fetch form\n * const store = qc.observe({ key: ['users', 1], fn: ({ signal }) => fetchUser(1, signal) });\n *\n * // Read-only form — no fn required\n * const store = qc.observe({ key: ['users', 1], fetch: false });\n * ```\n */\n function observe<T = unknown, S = T>(options: ObserveOptions<T, S>): SyncStore<QueryState<S>> {\n const {\n fetch: shouldFetch = true,\n key,\n placeholderData,\n select,\n } = options as {\n fetch?: boolean;\n key: QueryKey;\n placeholderData?: S | (() => S | undefined);\n select?: (data: T | undefined) => S | undefined;\n };\n\n const store = watchInternal<T, S>(ctx, key, { placeholderData, select });\n\n if (shouldFetch && (options as { fn?: QueryFn<T> }).fn) {\n fetchQuery({ ...(options as QueryOptions<T>) }).catch(() => {});\n }\n\n return store;\n }\n\n return {\n cancel(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (!entry?.inflight) return;\n\n entry.inflight.controller.abort();\n },\n\n cancelAll(): void {\n for (const entry of entries.values()) {\n entry.inflight?.controller.abort();\n }\n },\n\n clear(): void {\n for (const entry of [...entries.values()]) {\n evictEntry(ctx, entry);\n }\n },\n\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n ctx.disposed = true;\n disposeController.abort();\n\n for (const [, entry] of entries) {\n entry.observers.clear();\n entry.inflight?.controller.abort();\n }\n\n for (const [, timer] of gcTimers) {\n clearTimeout(timer);\n }\n\n gcTimers.clear();\n entries.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n fetch: fetchQuery,\n\n fetchMany<T = unknown>(queries: QueryOptions<T>[]): Promise<T[]> {\n return Promise.all(queries.map((q) => fetchQuery(q)));\n },\n\n get<T>(key: QueryKey): T | undefined {\n const entry = entries.get(hashKey(key));\n\n return entry ? (entry.data as T | undefined) : undefined;\n },\n\n getState<T>(key: QueryKey): QueryState<T> | null {\n const entry = entries.get(hashKey(key)) as CacheEntry<T> | undefined;\n\n if (!entry) return null;\n\n return toBaseState(entry);\n },\n\n invalidate,\n\n keys(): QueryKey[] {\n return [...entries.values()].map((e) => e.key);\n },\n\n observe,\n\n observeMany<T = unknown>(keys: QueryKey[]): SyncStore<QueryState<T>[]> {\n const stores = keys.map((k) => watchInternal<T>(ctx, k));\n\n return {\n peek(): QueryState<T>[] {\n return stores.map((s) => s.peek());\n },\n\n subscribe(onStoreChange: () => void): Unsubscribe {\n const unsubs = stores.map((s) => s.subscribe(onStoreChange));\n\n return () => {\n for (const u of unsubs) u();\n };\n },\n };\n },\n\n refetchStale(): void {\n for (const entry of [...entries.values()]) {\n if (isStaleAndRevalidatable(entry)) {\n startFetch(ctx, entry, entry.lastConfig as FetchConfig<unknown>).catch(() => {});\n }\n }\n },\n\n remove(key: QueryKey): void {\n const entry = entries.get(hashKey(key));\n\n if (entry) evictEntry(ctx, entry);\n },\n\n set,\n\n get size(): number {\n return entries.size;\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n };\n}\n\nexport type QueryClient = ReturnType<typeof createQuery>;\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ const e=require("./errors.cjs"),t=require("./transport.cjs"),n=require("./retry.cjs"),r=require("./stream-shared.cjs");let i=require("@vielzeug/arsenal");async function*a(a,o,s={}){if(a.disposed)throw new e.CourierDisposedError(`StreamClient`);let{body:c,fetchInit:l,headers:u,method:d=c===void 0?`GET`:`POST`,onError:f,params:p,parse:m=`text`,query:h,reconnect:g=!1,signal:_,timeout:v}=s;v!==void 0&&t.validateTimeout(v);let y=g===!0?{}:g===!1?{times:0}:g,b=y.times??(g?5:0),x=y.delay??n.fullJitterDelay,S=new AbortController,C=a.track(S),w=0,T=!1;try{for(;!a.disposed&&!S.signal.aborted;){let t,n=!1;try{let t=(await r.openStreamWith(a,o,S,{body:c,extSignal:_,fetchInit:l,headers:u,method:d,params:p,query:h,timeout:v})).body.getReader(),i=new TextDecoder;try{if(m===`ndjson`){let r=``;for(;;){let{done:a,value:o}=await t.read();if(a){let t=r.trim();if(t)try{yield JSON.parse(t)}catch{throw new e.CourierParseError(`NDJSON: failed to parse line: ${t.slice(0,200)}`)}n=!0;break}r+=i.decode(o,{stream:!0});let s;for(;(s=r.indexOf(`
2
+ `))!==-1;){let t=r.slice(0,s).trim();if(r=r.slice(s+1),t)try{yield JSON.parse(t)}catch{throw new e.CourierParseError(`NDJSON: failed to parse line: ${t.slice(0,200)}`)}}}}else for(;;){let{done:e,value:r}=await t.read();if(e){n=!0;break}yield i.decode(r,{stream:!0})}}finally{t.releaseLock()}}catch(e){if(a.disposed||S.signal.aborted)break;t=e instanceof Error?e:Error(String(e))}if(a.disposed||S.signal.aborted)break;if(b===0){if(t)if(f)f(t);else throw t;else T=n;break}if(w>=b){let e=t??Error(`[courier] readable() reconnect budget exhausted`);if(f)f(e);else if(t)throw t;break}if(n&&!t){T=!0;break}let s=typeof x==`function`?x(w):x;w++,await(0,i.sleep)(s,S.signal).catch(()=>{})}}finally{T||S.abort(),C()}}exports.readable=a;
3
+ //# sourceMappingURL=stream-readable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-readable.cjs","names":[],"sources":["../src/stream-readable.ts"],"sourcesContent":["import { sleep } from '@vielzeug/arsenal';\n\nimport type { Params } from './url';\n\nimport { CourierDisposedError, CourierParseError } from './errors';\nimport { fullJitterDelay } from './retry';\nimport { openStreamWith, type ReconnectOptions, type StreamRequestConfig } from './stream-shared';\nimport { type TransportCore, validateTimeout } from './transport';\n\nexport type ReadableConfig<P extends string = string> = StreamRequestConfig<P> & {\n /**\n * Called when the reconnect budget is exhausted or a non-retriable error\n * occurs. Not called when the generator is aborted via signal or dispose.\n */\n onError?: (error: Error) => void;\n /**\n * Response body parsing mode.\n * - `'text'` (default) — yields raw decoded string chunks as they arrive.\n * - `'ndjson'` — splits by newline and JSON-parses each complete line; use `T` to type the values.\n */\n parse?: 'ndjson' | 'text';\n /**\n * Auto-reconnect on connection loss. Uses the same backoff semantics as\n * `sse()` reconnect. `true` uses defaults (5 attempts). Defaults to `false`.\n */\n reconnect?: boolean | ReconnectOptions;\n};\n\n/**\n * Opens an HTTP streaming connection and returns an async generator yielding\n * response body chunks as they arrive.\n *\n * - `parse: 'text'` (default) — yields raw decoded string chunks\n * - `parse: 'ndjson'` — splits by newline and JSON-parses each line; use `T` to\n * type the parsed values\n *\n * @example\n * ```ts\n * // Raw text stream\n * for await (const chunk of stream.readable('/completions', { body: { prompt } })) {\n * process.stdout.write(chunk);\n * }\n *\n * // NDJSON (e.g. AI token stream, log stream)\n * for await (const msg of stream.readable<ChatMessage>('/chat', {\n * body: { prompt },\n * parse: 'ndjson',\n * })) {\n * console.log(msg.content);\n * }\n * ```\n */\nexport async function* readable<T = string, P extends string = string>(\n transport: TransportCore,\n url: P,\n config: ReadableConfig<P> = {} as ReadableConfig<P>,\n): AsyncGenerator<T> {\n if (transport.disposed) throw new CourierDisposedError('StreamClient');\n\n const {\n body,\n fetchInit,\n headers: perRequestHeaders,\n method = body !== undefined ? 'POST' : 'GET',\n onError,\n params,\n parse = 'text',\n query,\n reconnect = false,\n signal: extSignal,\n timeout: cfgTimeout,\n } = config as ReadableConfig;\n\n if (cfgTimeout !== undefined) validateTimeout(cfgTimeout);\n\n const reconnectOpts: ReconnectOptions = reconnect === true ? {} : reconnect === false ? { times: 0 } : reconnect;\n const maxReconnects = reconnectOpts.times ?? (reconnect ? 5 : 0);\n const reconnectDelay: number | ((attempt: number) => number) = reconnectOpts.delay ?? fullJitterDelay;\n\n const ac = new AbortController();\n const untrack = transport.track(ac);\n let attempt = 0;\n let overallNaturalEnd = false;\n\n try {\n while (!transport.disposed && !ac.signal.aborted) {\n let connectError: Error | undefined;\n let connectionNaturalEnd = false;\n\n try {\n const res = await openStreamWith(transport, url, ac, {\n body,\n extSignal,\n fetchInit,\n headers: perRequestHeaders as Record<string, string> | undefined,\n method,\n params: params as Params | undefined,\n query,\n timeout: cfgTimeout,\n });\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n\n try {\n if (parse === 'ndjson') {\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n const remaining = buffer.trim();\n\n if (remaining) {\n try {\n yield JSON.parse(remaining) as T;\n } catch {\n throw new CourierParseError(`NDJSON: failed to parse line: ${remaining.slice(0, 200)}`);\n }\n }\n\n connectionNaturalEnd = true;\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n let nlIdx: number;\n\n while ((nlIdx = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, nlIdx).trim();\n\n buffer = buffer.slice(nlIdx + 1);\n\n if (line) {\n try {\n yield JSON.parse(line) as T;\n } catch {\n throw new CourierParseError(`NDJSON: failed to parse line: ${line.slice(0, 200)}`);\n }\n }\n }\n }\n } else {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n connectionNaturalEnd = true;\n break;\n }\n\n yield decoder.decode(value, { stream: true }) as unknown as T;\n }\n }\n } finally {\n reader.releaseLock();\n }\n } catch (err) {\n if (transport.disposed || ac.signal.aborted) break;\n\n connectError = err instanceof Error ? err : new Error(String(err));\n }\n\n if (transport.disposed || ac.signal.aborted) break;\n\n // No reconnect budget — throw or call onError on errors, stop either way\n if (maxReconnects === 0) {\n if (connectError) {\n if (onError) {\n onError(connectError);\n } else {\n throw connectError;\n }\n } else {\n overallNaturalEnd = connectionNaturalEnd;\n }\n\n break;\n }\n\n // Budget exhausted\n if (attempt >= maxReconnects) {\n const budgetError = connectError ?? new Error('[courier] readable() reconnect budget exhausted');\n\n if (onError) {\n onError(budgetError);\n } else if (connectError) {\n throw connectError;\n }\n\n break;\n }\n\n // Clean server close with no reconnect error — done\n if (connectionNaturalEnd && !connectError) {\n overallNaturalEnd = true;\n break;\n }\n\n // Sleep before reconnecting\n const delay = typeof reconnectDelay === 'function' ? reconnectDelay(attempt) : reconnectDelay;\n\n attempt++;\n await sleep(delay, ac.signal).catch(() => {});\n }\n } finally {\n if (!overallNaturalEnd) ac.abort();\n\n untrack();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { type ReconnectOptions, type StreamRequestConfig } from './stream-shared';
2
+ import { type TransportCore } from './transport';
3
+ export type ReadableConfig<P extends string = string> = StreamRequestConfig<P> & {
4
+ /**
5
+ * Called when the reconnect budget is exhausted or a non-retriable error
6
+ * occurs. Not called when the generator is aborted via signal or dispose.
7
+ */
8
+ onError?: (error: Error) => void;
9
+ /**
10
+ * Response body parsing mode.
11
+ * - `'text'` (default) — yields raw decoded string chunks as they arrive.
12
+ * - `'ndjson'` — splits by newline and JSON-parses each complete line; use `T` to type the values.
13
+ */
14
+ parse?: 'ndjson' | 'text';
15
+ /**
16
+ * Auto-reconnect on connection loss. Uses the same backoff semantics as
17
+ * `sse()` reconnect. `true` uses defaults (5 attempts). Defaults to `false`.
18
+ */
19
+ reconnect?: boolean | ReconnectOptions;
20
+ };
21
+ /**
22
+ * Opens an HTTP streaming connection and returns an async generator yielding
23
+ * response body chunks as they arrive.
24
+ *
25
+ * - `parse: 'text'` (default) — yields raw decoded string chunks
26
+ * - `parse: 'ndjson'` — splits by newline and JSON-parses each line; use `T` to
27
+ * type the parsed values
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * // Raw text stream
32
+ * for await (const chunk of stream.readable('/completions', { body: { prompt } })) {
33
+ * process.stdout.write(chunk);
34
+ * }
35
+ *
36
+ * // NDJSON (e.g. AI token stream, log stream)
37
+ * for await (const msg of stream.readable<ChatMessage>('/chat', {
38
+ * body: { prompt },
39
+ * parse: 'ndjson',
40
+ * })) {
41
+ * console.log(msg.content);
42
+ * }
43
+ * ```
44
+ */
45
+ export declare function readable<T = string, P extends string = string>(transport: TransportCore, url: P, config?: ReadableConfig<P>): AsyncGenerator<T>;
46
+ //# sourceMappingURL=stream-readable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-readable.d.ts","sourceRoot":"","sources":["../src/stream-readable.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAClG,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,aAAa,CAAC;AAElE,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,mBAAmB,CAAC,CAAC,CAAC,GAAG;IAC/E;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;CACxC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAuB,QAAQ,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EACnE,SAAS,EAAE,aAAa,EACxB,GAAG,EAAE,CAAC,EACN,MAAM,GAAE,cAAc,CAAC,CAAC,CAA2B,GAClD,cAAc,CAAC,CAAC,CAAC,CA4JnB"}
@@ -0,0 +1,3 @@
1
+ import{CourierDisposedError as e,CourierParseError as t}from"./errors.js";import{validateTimeout as n}from"./transport.js";import{fullJitterDelay as r}from"./retry.js";import{openStreamWith as i}from"./stream-shared.js";import{sleep as a}from"@vielzeug/arsenal";async function*o(o,s,c={}){if(o.disposed)throw new e(`StreamClient`);let{body:l,fetchInit:u,headers:d,method:f=l===void 0?`GET`:`POST`,onError:p,params:m,parse:h=`text`,query:g,reconnect:_=!1,signal:v,timeout:y}=c;y!==void 0&&n(y);let b=_===!0?{}:_===!1?{times:0}:_,x=b.times??(_?5:0),S=b.delay??r,C=new AbortController,w=o.track(C),T=0,E=!1;try{for(;!o.disposed&&!C.signal.aborted;){let e,n=!1;try{let e=(await i(o,s,C,{body:l,extSignal:v,fetchInit:u,headers:d,method:f,params:m,query:g,timeout:y})).body.getReader(),r=new TextDecoder;try{if(h===`ndjson`){let i=``;for(;;){let{done:a,value:o}=await e.read();if(a){let e=i.trim();if(e)try{yield JSON.parse(e)}catch{throw new t(`NDJSON: failed to parse line: ${e.slice(0,200)}`)}n=!0;break}i+=r.decode(o,{stream:!0});let s;for(;(s=i.indexOf(`
2
+ `))!==-1;){let e=i.slice(0,s).trim();if(i=i.slice(s+1),e)try{yield JSON.parse(e)}catch{throw new t(`NDJSON: failed to parse line: ${e.slice(0,200)}`)}}}}else for(;;){let{done:t,value:i}=await e.read();if(t){n=!0;break}yield r.decode(i,{stream:!0})}}finally{e.releaseLock()}}catch(t){if(o.disposed||C.signal.aborted)break;e=t instanceof Error?t:Error(String(t))}if(o.disposed||C.signal.aborted)break;if(x===0){if(e)if(p)p(e);else throw e;else E=n;break}if(T>=x){let t=e??Error(`[courier] readable() reconnect budget exhausted`);if(p)p(t);else if(e)throw e;break}if(n&&!e){E=!0;break}let r=typeof S==`function`?S(T):S;T++,await a(r,C.signal).catch(()=>{})}}finally{E||C.abort(),w()}}export{o as readable};
3
+ //# sourceMappingURL=stream-readable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-readable.js","names":[],"sources":["../src/stream-readable.ts"],"sourcesContent":["import { sleep } from '@vielzeug/arsenal';\n\nimport type { Params } from './url';\n\nimport { CourierDisposedError, CourierParseError } from './errors';\nimport { fullJitterDelay } from './retry';\nimport { openStreamWith, type ReconnectOptions, type StreamRequestConfig } from './stream-shared';\nimport { type TransportCore, validateTimeout } from './transport';\n\nexport type ReadableConfig<P extends string = string> = StreamRequestConfig<P> & {\n /**\n * Called when the reconnect budget is exhausted or a non-retriable error\n * occurs. Not called when the generator is aborted via signal or dispose.\n */\n onError?: (error: Error) => void;\n /**\n * Response body parsing mode.\n * - `'text'` (default) — yields raw decoded string chunks as they arrive.\n * - `'ndjson'` — splits by newline and JSON-parses each complete line; use `T` to type the values.\n */\n parse?: 'ndjson' | 'text';\n /**\n * Auto-reconnect on connection loss. Uses the same backoff semantics as\n * `sse()` reconnect. `true` uses defaults (5 attempts). Defaults to `false`.\n */\n reconnect?: boolean | ReconnectOptions;\n};\n\n/**\n * Opens an HTTP streaming connection and returns an async generator yielding\n * response body chunks as they arrive.\n *\n * - `parse: 'text'` (default) — yields raw decoded string chunks\n * - `parse: 'ndjson'` — splits by newline and JSON-parses each line; use `T` to\n * type the parsed values\n *\n * @example\n * ```ts\n * // Raw text stream\n * for await (const chunk of stream.readable('/completions', { body: { prompt } })) {\n * process.stdout.write(chunk);\n * }\n *\n * // NDJSON (e.g. AI token stream, log stream)\n * for await (const msg of stream.readable<ChatMessage>('/chat', {\n * body: { prompt },\n * parse: 'ndjson',\n * })) {\n * console.log(msg.content);\n * }\n * ```\n */\nexport async function* readable<T = string, P extends string = string>(\n transport: TransportCore,\n url: P,\n config: ReadableConfig<P> = {} as ReadableConfig<P>,\n): AsyncGenerator<T> {\n if (transport.disposed) throw new CourierDisposedError('StreamClient');\n\n const {\n body,\n fetchInit,\n headers: perRequestHeaders,\n method = body !== undefined ? 'POST' : 'GET',\n onError,\n params,\n parse = 'text',\n query,\n reconnect = false,\n signal: extSignal,\n timeout: cfgTimeout,\n } = config as ReadableConfig;\n\n if (cfgTimeout !== undefined) validateTimeout(cfgTimeout);\n\n const reconnectOpts: ReconnectOptions = reconnect === true ? {} : reconnect === false ? { times: 0 } : reconnect;\n const maxReconnects = reconnectOpts.times ?? (reconnect ? 5 : 0);\n const reconnectDelay: number | ((attempt: number) => number) = reconnectOpts.delay ?? fullJitterDelay;\n\n const ac = new AbortController();\n const untrack = transport.track(ac);\n let attempt = 0;\n let overallNaturalEnd = false;\n\n try {\n while (!transport.disposed && !ac.signal.aborted) {\n let connectError: Error | undefined;\n let connectionNaturalEnd = false;\n\n try {\n const res = await openStreamWith(transport, url, ac, {\n body,\n extSignal,\n fetchInit,\n headers: perRequestHeaders as Record<string, string> | undefined,\n method,\n params: params as Params | undefined,\n query,\n timeout: cfgTimeout,\n });\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n\n try {\n if (parse === 'ndjson') {\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n const remaining = buffer.trim();\n\n if (remaining) {\n try {\n yield JSON.parse(remaining) as T;\n } catch {\n throw new CourierParseError(`NDJSON: failed to parse line: ${remaining.slice(0, 200)}`);\n }\n }\n\n connectionNaturalEnd = true;\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n let nlIdx: number;\n\n while ((nlIdx = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, nlIdx).trim();\n\n buffer = buffer.slice(nlIdx + 1);\n\n if (line) {\n try {\n yield JSON.parse(line) as T;\n } catch {\n throw new CourierParseError(`NDJSON: failed to parse line: ${line.slice(0, 200)}`);\n }\n }\n }\n }\n } else {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n connectionNaturalEnd = true;\n break;\n }\n\n yield decoder.decode(value, { stream: true }) as unknown as T;\n }\n }\n } finally {\n reader.releaseLock();\n }\n } catch (err) {\n if (transport.disposed || ac.signal.aborted) break;\n\n connectError = err instanceof Error ? err : new Error(String(err));\n }\n\n if (transport.disposed || ac.signal.aborted) break;\n\n // No reconnect budget — throw or call onError on errors, stop either way\n if (maxReconnects === 0) {\n if (connectError) {\n if (onError) {\n onError(connectError);\n } else {\n throw connectError;\n }\n } else {\n overallNaturalEnd = connectionNaturalEnd;\n }\n\n break;\n }\n\n // Budget exhausted\n if (attempt >= maxReconnects) {\n const budgetError = connectError ?? new Error('[courier] readable() reconnect budget exhausted');\n\n if (onError) {\n onError(budgetError);\n } else if (connectError) {\n throw connectError;\n }\n\n break;\n }\n\n // Clean server close with no reconnect error — done\n if (connectionNaturalEnd && !connectError) {\n overallNaturalEnd = true;\n break;\n }\n\n // Sleep before reconnecting\n const delay = typeof reconnectDelay === 'function' ? reconnectDelay(attempt) : reconnectDelay;\n\n attempt++;\n await sleep(delay, ac.signal).catch(() => {});\n }\n } finally {\n if (!overallNaturalEnd) ac.abort();\n\n untrack();\n }\n}\n"],"mappings":"sQAoDA,eAAuB,EACrB,EACA,EACA,EAA4B,CAAC,EACV,CACnB,GAAI,EAAU,SAAU,MAAM,IAAI,EAAqB,cAAc,EAErE,GAAM,CACJ,OACA,YACA,QAAS,EACT,SAAS,IAAS,IAAA,GAAqB,MAAT,OAC9B,UACA,SACA,QAAQ,OACR,QACA,YAAY,GACZ,OAAQ,EACR,QAAS,GACP,EAEA,IAAe,IAAA,IAAW,EAAgB,CAAU,EAExD,IAAM,EAAkC,IAAc,GAAO,CAAC,EAAI,IAAc,GAAQ,CAAE,MAAO,CAAE,EAAI,EACjG,EAAgB,EAAc,QAAU,EAAY,EAAI,GACxD,EAAyD,EAAc,OAAS,EAEhF,EAAK,IAAI,gBACT,EAAU,EAAU,MAAM,CAAE,EAC9B,EAAU,EACV,EAAoB,GAExB,GAAI,CACF,KAAO,CAAC,EAAU,UAAY,CAAC,EAAG,OAAO,SAAS,CAChD,IAAI,EACA,EAAuB,GAE3B,GAAI,CAYF,IAAM,GAAS,MAXG,EAAe,EAAW,EAAK,EAAI,CACnD,OACA,YACA,YACA,QAAS,EACT,SACQ,SACR,QACA,QAAS,CACX,CAAC,EAAA,CAEkB,KAAM,UAAU,EAC7B,EAAU,IAAI,YAEpB,GAAI,CACF,GAAI,IAAU,SAAU,CACtB,IAAI,EAAS,GAEb,OAAa,CACX,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAE1C,GAAI,EAAM,CACR,IAAM,EAAY,EAAO,KAAK,EAE9B,GAAI,EACF,GAAI,CACF,MAAM,KAAK,MAAM,CAAS,CAC5B,MAAQ,CACN,MAAM,IAAI,EAAkB,iCAAiC,EAAU,MAAM,EAAG,GAAG,GAAG,CACxF,CAGF,EAAuB,GACvB,KACF,CAEA,GAAU,EAAQ,OAAO,EAAO,CAAE,OAAQ,EAAK,CAAC,EAEhD,IAAI,EAEJ,MAAQ,EAAQ,EAAO,QAAQ;CAAI,KAAO,IAAI,CAC5C,IAAM,EAAO,EAAO,MAAM,EAAG,CAAK,CAAC,CAAC,KAAK,EAIzC,GAFA,EAAS,EAAO,MAAM,EAAQ,CAAC,EAE3B,EACF,GAAI,CACF,MAAM,KAAK,MAAM,CAAI,CACvB,MAAQ,CACN,MAAM,IAAI,EAAkB,iCAAiC,EAAK,MAAM,EAAG,GAAG,GAAG,CACnF,CAEJ,CACF,CACF,MACE,OAAa,CACX,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAE1C,GAAI,EAAM,CACR,EAAuB,GACvB,KACF,CAEA,MAAM,EAAQ,OAAO,EAAO,CAAE,OAAQ,EAAK,CAAC,CAC9C,CAEJ,QAAU,CACR,EAAO,YAAY,CACrB,CACF,OAAS,EAAK,CACZ,GAAI,EAAU,UAAY,EAAG,OAAO,QAAS,MAE7C,EAAe,aAAe,MAAQ,EAAU,MAAM,OAAO,CAAG,CAAC,CACnE,CAEA,GAAI,EAAU,UAAY,EAAG,OAAO,QAAS,MAG7C,GAAI,IAAkB,EAAG,CACvB,GAAI,EACF,GAAI,EACF,EAAQ,CAAY,OAEpB,MAAM,MAGR,GAAoB,EAGtB,KACF,CAGA,GAAI,GAAW,EAAe,CAC5B,IAAM,EAAc,GAAoB,MAAM,iDAAiD,EAE/F,GAAI,EACF,EAAQ,CAAW,OACd,GAAI,EACT,MAAM,EAGR,KACF,CAGA,GAAI,GAAwB,CAAC,EAAc,CACzC,EAAoB,GACpB,KACF,CAGA,IAAM,EAAQ,OAAO,GAAmB,WAAa,EAAe,CAAO,EAAI,EAE/E,IACA,MAAM,EAAM,EAAO,EAAG,MAAM,CAAC,CAAC,UAAY,CAAC,CAAC,CAC9C,CACF,QAAU,CACH,GAAmB,EAAG,MAAM,EAEjC,EAAQ,CACV,CACF"}
@@ -0,0 +1,2 @@
1
+ const e=require("./errors.cjs"),t=require("./serialize.cjs"),n=require("./transport.cjs"),r=require("./url.cjs");async function i(i,a,o,s,c){let l=s.method.toUpperCase(),u=r.buildUrl(i.baseUrl,a,s.params,s.query),d=n.anySignal(s.extSignal,o.signal)??o.signal,f=n.buildTimeoutSignal(s.timeout??1/0,d),{headers:p,...m}=t.buildRequestInit(l,i.mergeHeaders(s.headers,c),s.body,f,s.fetchInit??{}),h;try{h=await i.dispatch({headers:p,init:m,url:u})}catch(t){throw e.classifyRequestError(t,l,u,f??d)}if(!h.ok){let t=await h.text().catch(()=>``);throw e.CourierHttpError.fromResponse(h,t,l,u)}if(!h.body)throw new e.CourierParseError(`Response has no body`);return h}exports.openStreamWith=i;
2
+ //# sourceMappingURL=stream-shared.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-shared.cjs","names":[],"sources":["../src/stream-shared.ts"],"sourcesContent":["import type { Params } from './url';\n\nimport { classifyRequestError, CourierHttpError, CourierParseError } from './errors';\nimport { buildRequestInit } from './serialize';\nimport { anySignal, buildTimeoutSignal, type TransportCore } from './transport';\nimport { buildUrl } from './url';\n\nexport type StreamRequestConfig<P extends string = string> = {\n body?: unknown;\n /** Raw fetch options for advanced use (credentials, cache, mode, referrer, etc.). */\n fetchInit?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>;\n headers?: Record<string, string>;\n method?: string;\n params?: P extends string ? Record<string, string | number | boolean> : never;\n query?: Params;\n /** External abort signal. Merged with internal cancellation. */\n signal?: AbortSignal;\n /** Request timeout in ms. Overrides client default (which is `Infinity` for streaming). */\n timeout?: number;\n};\n\nexport type ReconnectOptions = {\n /**\n * Delay between reconnect attempts in ms, or a zero-based function where\n * `attempt` is the number of failures so far (0 = waiting before the 2nd try).\n * Defaults to full-jitter exponential backoff: `[0, min(1s × 2ⁿ, 30s)]`.\n */\n delay?: number | ((attempt: number) => number);\n /** Total reconnect attempts after the first failure. Defaults to `5`. */\n times?: number;\n};\n\n/**\n * Shared stream setup: build URL, combine signals, merge headers, dispatch through\n * transport. Returns the Response so both sse() and readable() can focus on parsing.\n * The caller owns the AbortController lifecycle (track + untrack).\n */\nexport async function openStreamWith(\n transport: TransportCore,\n url: string,\n ac: AbortController,\n config: {\n body?: unknown;\n extSignal?: AbortSignal;\n fetchInit?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>;\n headers?: Record<string, string>;\n method: string;\n params?: Params;\n query?: Params;\n timeout?: number;\n },\n extraHeaders?: Record<string, string>,\n): Promise<Response> {\n // Normalize once — error paths and RequestInit both receive the canonical form.\n const method = config.method.toUpperCase();\n const full = buildUrl(transport.baseUrl, url, config.params, config.query);\n const combined = anySignal(config.extSignal, ac.signal) ?? ac.signal;\n // Streaming connections are long-lived — default to Infinity so the transport's\n // REST default (30s) does not cut off streams prematurely.\n const signal = buildTimeoutSignal(config.timeout ?? Number.POSITIVE_INFINITY, combined);\n const requestHeaders = transport.mergeHeaders(config.headers, extraHeaders);\n const { headers: initHeaders, ...restInit } = buildRequestInit(\n method,\n requestHeaders,\n config.body,\n signal,\n config.fetchInit ?? {},\n );\n\n let res: Response;\n\n try {\n res = await transport.dispatch({ headers: initHeaders as Record<string, string>, init: restInit, url: full });\n } catch (err) {\n throw classifyRequestError(err, method, full, signal ?? combined);\n }\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n\n throw CourierHttpError.fromResponse(res, text, method, full);\n }\n\n if (!res.body) throw new CourierParseError('Response has no body');\n\n return res;\n}\n"],"mappings":"iHAqCA,eAAsB,EACpB,EACA,EACA,EACA,EAUA,EACmB,CAEnB,IAAM,EAAS,EAAO,OAAO,YAAY,EACnC,EAAO,EAAA,SAAS,EAAU,QAAS,EAAK,EAAO,OAAQ,EAAO,KAAK,EACnE,EAAW,EAAA,UAAU,EAAO,UAAW,EAAG,MAAM,GAAK,EAAG,OAGxD,EAAS,EAAA,mBAAmB,EAAO,SAAW,IAA0B,CAAQ,EAEhF,CAAE,QAAS,EAAa,GAAG,GAAa,EAAA,iBAC5C,EAFqB,EAAU,aAAa,EAAO,QAAS,CAG5D,EACA,EAAO,KACP,EACA,EAAO,WAAa,CAAC,CACvB,EAEI,EAEJ,GAAI,CACF,EAAM,MAAM,EAAU,SAAS,CAAE,QAAS,EAAuC,KAAM,EAAU,IAAK,CAAK,CAAC,CAC9G,OAAS,EAAK,CACZ,MAAM,EAAA,qBAAqB,EAAK,EAAQ,EAAM,GAAU,CAAQ,CAClE,CAEA,GAAI,CAAC,EAAI,GAAI,CACX,IAAM,EAAO,MAAM,EAAI,KAAK,CAAC,CAAC,UAAY,EAAE,EAE5C,MAAM,EAAA,iBAAiB,aAAa,EAAK,EAAM,EAAQ,CAAI,CAC7D,CAEA,GAAI,CAAC,EAAI,KAAM,MAAM,IAAI,EAAA,kBAAkB,sBAAsB,EAEjE,OAAO,CACT"}