@vielzeug/courier 1.1.4 → 2.0.0
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.
- package/README.md +10 -12
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +1 -1
- package/dist/_dev.js.map +1 -1
- package/dist/api.cjs +1 -1
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.ts +4 -7
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/courier.cjs +5 -6
- package/dist/courier.cjs.map +1 -1
- package/dist/courier.d.ts +20 -142
- package/dist/courier.d.ts.map +1 -1
- package/dist/courier.iife.js +5 -6
- package/dist/courier.iife.js.map +1 -1
- package/dist/courier.js +5 -6
- package/dist/courier.js.map +1 -1
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.d.ts +0 -28
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +0 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +6 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/interceptors.cjs +1 -1
- package/dist/interceptors.cjs.map +1 -1
- package/dist/interceptors.js +1 -1
- package/dist/interceptors.js.map +1 -1
- package/dist/query.cjs +1 -1
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.ts +8 -101
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +1 -1
- package/dist/query.js.map +1 -1
- package/dist/serialize.cjs +1 -1
- package/dist/serialize.cjs.map +1 -1
- package/dist/serialize.d.ts +1 -1
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +1 -1
- package/dist/serialize.js.map +1 -1
- package/dist/stream.cjs +5 -1
- package/dist/stream.cjs.map +1 -1
- package/dist/stream.d.ts +22 -39
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +5 -1
- package/dist/stream.js.map +1 -1
- package/dist/transport.cjs.map +1 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +38 -46
- package/dist/types.d.ts.map +1 -1
- package/dist/url.cjs.map +1 -1
- package/dist/url.d.ts +0 -9
- package/dist/url.d.ts.map +1 -1
- package/dist/url.js.map +1 -1
- package/package.json +4 -4
- package/dist/batcher.cjs +0 -2
- package/dist/batcher.cjs.map +0 -1
- package/dist/batcher.d.ts +0 -66
- package/dist/batcher.d.ts.map +0 -1
- package/dist/batcher.js +0 -2
- package/dist/batcher.js.map +0 -1
- package/dist/focus.cjs +0 -2
- package/dist/focus.cjs.map +0 -1
- package/dist/focus.d.ts +0 -25
- package/dist/focus.d.ts.map +0 -1
- package/dist/focus.js +0 -2
- package/dist/focus.js.map +0 -1
- package/dist/mutation.cjs +0 -2
- package/dist/mutation.cjs.map +0 -1
- package/dist/mutation.d.ts +0 -81
- package/dist/mutation.d.ts.map +0 -1
- package/dist/mutation.js +0 -2
- package/dist/mutation.js.map +0 -1
- package/dist/persist.cjs +0 -2
- package/dist/persist.cjs.map +0 -1
- package/dist/persist.d.ts +0 -86
- package/dist/persist.d.ts.map +0 -1
- package/dist/persist.js +0 -2
- package/dist/persist.js.map +0 -1
- package/dist/query-cache.cjs +0 -2
- package/dist/query-cache.cjs.map +0 -1
- package/dist/query-cache.d.ts +0 -88
- package/dist/query-cache.d.ts.map +0 -1
- package/dist/query-cache.js +0 -2
- package/dist/query-cache.js.map +0 -1
- package/dist/query-observe.cjs +0 -2
- package/dist/query-observe.cjs.map +0 -1
- package/dist/query-observe.d.ts +0 -17
- package/dist/query-observe.d.ts.map +0 -1
- package/dist/query-observe.js +0 -2
- package/dist/query-observe.js.map +0 -1
- package/dist/retry.cjs +0 -2
- package/dist/retry.cjs.map +0 -1
- package/dist/retry.d.ts +0 -34
- package/dist/retry.d.ts.map +0 -1
- package/dist/retry.js +0 -2
- package/dist/retry.js.map +0 -1
- package/dist/stream-readable.cjs +0 -3
- package/dist/stream-readable.cjs.map +0 -1
- package/dist/stream-readable.d.ts +0 -46
- package/dist/stream-readable.d.ts.map +0 -1
- package/dist/stream-readable.js +0 -3
- package/dist/stream-readable.js.map +0 -1
- package/dist/stream-shared.cjs +0 -2
- package/dist/stream-shared.cjs.map +0 -1
- package/dist/stream-shared.d.ts +0 -41
- package/dist/stream-shared.d.ts.map +0 -1
- package/dist/stream-shared.js +0 -2
- package/dist/stream-shared.js.map +0 -1
- package/dist/stream-sse.cjs +0 -6
- package/dist/stream-sse.cjs.map +0 -1
- package/dist/stream-sse.d.ts +0 -59
- package/dist/stream-sse.d.ts.map +0 -1
- package/dist/stream-sse.js +0 -6
- package/dist/stream-sse.js.map +0 -1
- package/dist/sync-store.cjs +0 -2
- package/dist/sync-store.cjs.map +0 -1
- package/dist/sync-store.d.ts +0 -20
- package/dist/sync-store.d.ts.map +0 -1
- package/dist/sync-store.js +0 -2
- package/dist/sync-store.js.map +0 -1
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 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 { AsyncState, QueryCache, QueryContext, QueryDefinition, QueryKey, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { CourierDisposedError } from './errors';\nimport { hash } from './serialize';\n\nconst DEFAULT_STALE_TIME = 0;\n\ntype Entry = {\n controller: AbortController | undefined;\n definition: QueryDefinition<unknown> | undefined;\n key: QueryKey;\n promise: Promise<unknown> | undefined;\n snapshot: AsyncState<unknown>;\n};\n\ntype CancellableQueryCache = QueryCache & {\n cancelAll(): void;\n};\n\nfunction loading<T>(isFetching = false): AsyncState<T> {\n return { data: undefined, error: null, isFetching, status: 'loading', updatedAt: undefined };\n}\n\n/** Transport-agnostic cache. Queries are identified by explicit keys and fetch definitions. */\nexport function createQueryCache(options?: { signal?: AbortSignal; staleTime?: number }): CancellableQueryCache {\n const entries = new Map<string, Entry>();\n const listeners = new Map<string, Set<Unsubscribe>>();\n let disposed = options?.signal?.aborted ?? false;\n\n const notify = (key: QueryKey): void => {\n for (const listener of [...(listeners.get(hash(key)) ?? [])]) listener();\n };\n\n const cancelAll = (): void => {\n for (const entry of entries.values()) entry.controller?.abort();\n };\n\n const clear = (): void => {\n const keys = [...entries.values()].map((entry) => entry.key);\n\n cancelAll();\n entries.clear();\n\n for (const key of keys) notify(key);\n };\n\n options?.signal?.addEventListener(\n 'abort',\n () => {\n disposed = true;\n clear();\n },\n { once: true },\n );\n\n const entryFor = (key: QueryKey): Entry => {\n const id = hash(key);\n let entry = entries.get(id);\n\n if (!entry) {\n entry = {\n controller: undefined,\n definition: undefined,\n key,\n promise: undefined,\n snapshot: loading(),\n };\n entries.set(id, entry);\n }\n\n return entry;\n };\n\n const fetchEntry = <T>(entry: Entry, force: boolean): Promise<T> => {\n if (disposed) return Promise.reject(new CourierDisposedError('QueryCache'));\n\n if (!entry.definition)\n return Promise.reject(new Error(`No fetch function registered for query ${hash(entry.key)}.`));\n\n const staleTime = entry.definition.staleTime ?? options?.staleTime ?? DEFAULT_STALE_TIME;\n\n if (\n !force &&\n entry.snapshot.status === 'success' &&\n Date.now() - entry.snapshot.updatedAt < staleTime &&\n !entry.snapshot.isFetching\n ) {\n return Promise.resolve(entry.snapshot.data as T);\n }\n\n if (entry.promise) return entry.promise as Promise<T>;\n\n const controller = new AbortController();\n\n entry.controller = controller;\n entry.snapshot =\n entry.snapshot.status === 'success' ? { ...entry.snapshot, isFetching: true } : { ...loading<unknown>(true) };\n notify(entry.key);\n\n const promise = Promise.resolve()\n .then(() => entry.definition!.fetch({ key: entry.key, signal: controller.signal } as QueryContext))\n .then(\n (data) => {\n entry.snapshot = { data, error: null, isFetching: false, status: 'success', updatedAt: Date.now() };\n entry.controller = undefined;\n entry.promise = undefined;\n notify(entry.key);\n\n return data;\n },\n (cause: unknown) => {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n\n entry.snapshot = {\n data: entry.snapshot.status === 'success' ? entry.snapshot.data : undefined,\n error,\n isFetching: false,\n status: 'error',\n updatedAt: Date.now(),\n };\n entry.controller = undefined;\n entry.promise = undefined;\n notify(entry.key);\n\n throw error;\n },\n );\n\n entry.promise = promise;\n\n return promise as Promise<T>;\n };\n\n return {\n cancelAll,\n clear,\n fetch<T>(definition: QueryDefinition<T>, fetchOptions?: { force?: boolean }): Promise<T> {\n const entry = entryFor(definition.key);\n\n entry.definition = definition as QueryDefinition<unknown>;\n\n return fetchEntry<T>(entry, fetchOptions?.force ?? false);\n },\n get<T>(key: QueryKey): T | undefined {\n const entry = entries.get(hash(key));\n\n return entry?.snapshot.status === 'success' ? (entry.snapshot.data as T) : undefined;\n },\n getSnapshot<T>(key: QueryKey): AsyncState<T> | null {\n return (entries.get(hash(key))?.snapshot as AsyncState<T> | undefined) ?? null;\n },\n invalidate(key: QueryKey): void {\n for (const entry of entries.values()) {\n const matches =\n key.length <= entry.key.length && key.every((atom, index) => hash(atom) === hash(entry.key[index]));\n\n if (matches) {\n if (entry.snapshot.status === 'success') entry.snapshot = { ...entry.snapshot, updatedAt: 0 };\n\n notify(entry.key);\n }\n }\n },\n keys(): QueryKey[] {\n return [...entries.values()].map((entry) => entry.key);\n },\n refetchStale(): void {\n for (const entry of entries.values()) {\n if (!entry.definition || entry.snapshot.status !== 'success' || entry.snapshot.isFetching) continue;\n\n const staleTime = entry.definition.staleTime ?? options?.staleTime ?? DEFAULT_STALE_TIME;\n\n if (Date.now() - entry.snapshot.updatedAt >= staleTime) {\n void fetchEntry(entry, true).catch(() => {\n warn(`Failed to refetch stale query ${hash(entry.key)}.`);\n });\n }\n }\n },\n set<T>(key: QueryKey, data: T, setOptions?: { updatedAt?: number }): void {\n const entry = entryFor(key);\n\n entry.snapshot = {\n data,\n error: null,\n isFetching: false,\n status: 'success',\n updatedAt: setOptions?.updatedAt ?? Date.now(),\n };\n notify(key);\n },\n subscribe(key: QueryKey, listener: () => void): Unsubscribe {\n const id = hash(key);\n const keyListeners = listeners.get(id) ?? new Set<Unsubscribe>();\n\n keyListeners.add(listener);\n listeners.set(id, keyListeners);\n\n return () => {\n keyListeners.delete(listener);\n\n if (keyListeners.size === 0) listeners.delete(id);\n };\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/serialize.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require("@vielzeug/arsenal");function e(e,n,r,i,a){return r!==void 0&&!t(r)?{...a,body:JSON.stringify(r),headers:{"content-type":`application/json`,...n},method:e.toUpperCase(),signal:i}:{...a,...r!==void 0&&{body:r},headers:n,method:e.toUpperCase(),signal:i}}function t(e){return e instanceof FormData||e instanceof Blob||e instanceof URLSearchParams||typeof e==`string`||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||typeof ReadableStream<`u`&&e instanceof ReadableStream}exports.buildRequestInit=e,exports.isBodyInit=t;
|
|
1
|
+
require("@vielzeug/arsenal/object");function e(e,n,r,i,a){return r!==void 0&&!t(r)?{...a,body:JSON.stringify(r),headers:{"content-type":`application/json`,...n},method:e.toUpperCase(),signal:i}:{...a,...r!==void 0&&{body:r},headers:n,method:e.toUpperCase(),signal:i}}function t(e){return e instanceof FormData||e instanceof Blob||e instanceof URLSearchParams||typeof e==`string`||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||typeof ReadableStream<`u`&&e instanceof ReadableStream}exports.buildRequestInit=e,exports.isBodyInit=t;
|
|
2
2
|
//# sourceMappingURL=serialize.cjs.map
|
package/dist/serialize.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.cjs","names":[],"sources":["../src/serialize.ts"],"sourcesContent":["import { hash } from '@vielzeug/arsenal';\n\nexport { hash };\n\n/**\n * Build a `RequestInit` object from the request components, handling body serialisation.\n * Plain objects/arrays are JSON-serialised and `content-type: application/json` is added.\n * BodyInit values (FormData, Blob, etc.) are forwarded as-is.\n */\nexport function buildRequestInit(\n method: string,\n headers: Record<string, string>,\n body: unknown,\n signal: AbortSignal | undefined,\n rest: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>,\n): RequestInit {\n if (body !== undefined && !isBodyInit(body)) {\n return {\n ...rest,\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json', ...headers },\n method: method.toUpperCase(),\n signal,\n };\n }\n\n return {\n ...rest,\n ...(body !== undefined && { body: body as BodyInit }),\n headers,\n method: method.toUpperCase(),\n signal,\n };\n}\n\nexport function isBodyInit(value: unknown): value is BodyInit {\n return (\n value instanceof FormData ||\n value instanceof Blob ||\n value instanceof URLSearchParams ||\n typeof value === 'string' ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n (typeof ReadableStream !== 'undefined' && value instanceof ReadableStream)\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"serialize.cjs","names":[],"sources":["../src/serialize.ts"],"sourcesContent":["import { hash } from '@vielzeug/arsenal/object';\n\nexport { hash };\n\n/**\n * Build a `RequestInit` object from the request components, handling body serialisation.\n * Plain objects/arrays are JSON-serialised and `content-type: application/json` is added.\n * BodyInit values (FormData, Blob, etc.) are forwarded as-is.\n */\nexport function buildRequestInit(\n method: string,\n headers: Record<string, string>,\n body: unknown,\n signal: AbortSignal | undefined,\n rest: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>,\n): RequestInit {\n if (body !== undefined && !isBodyInit(body)) {\n return {\n ...rest,\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json', ...headers },\n method: method.toUpperCase(),\n signal,\n };\n }\n\n return {\n ...rest,\n ...(body !== undefined && { body: body as BodyInit }),\n headers,\n method: method.toUpperCase(),\n signal,\n };\n}\n\nexport function isBodyInit(value: unknown): value is BodyInit {\n return (\n value instanceof FormData ||\n value instanceof Blob ||\n value instanceof URLSearchParams ||\n typeof value === 'string' ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n (typeof ReadableStream !== 'undefined' && value instanceof ReadableStream)\n );\n}\n"],"mappings":"oCASA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACa,CAWb,OAVI,IAAS,IAAA,IAAa,CAAC,EAAW,CAAI,EACjC,CACL,GAAG,EACH,KAAM,KAAK,UAAU,CAAI,EACzB,QAAS,CAAE,eAAgB,mBAAoB,GAAG,CAAQ,EAC1D,OAAQ,EAAO,YAAY,EAC3B,QACF,EAGK,CACL,GAAG,EACH,GAAI,IAAS,IAAA,IAAa,CAAQ,MAAiB,EACnD,UACA,OAAQ,EAAO,YAAY,EAC3B,QACF,CACF,CAEA,SAAgB,EAAW,EAAmC,CAC5D,OACE,aAAiB,UACjB,aAAiB,MACjB,aAAiB,iBACjB,OAAO,GAAU,UACjB,aAAiB,aACjB,YAAY,OAAO,CAAK,GACvB,OAAO,eAAmB,KAAe,aAAiB,cAE/D"}
|
package/dist/serialize.d.ts
CHANGED
package/dist/serialize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAEhD,OAAO,EAAE,IAAI,EAAE,CAAC;AAEhB;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC,GAChE,WAAW,CAkBb;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAU5D"}
|
package/dist/serialize.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{hash as e}from"@vielzeug/arsenal";function t(e,t,r,i,a){return r!==void 0&&!n(r)?{...a,body:JSON.stringify(r),headers:{"content-type":`application/json`,...t},method:e.toUpperCase(),signal:i}:{...a,...r!==void 0&&{body:r},headers:t,method:e.toUpperCase(),signal:i}}function n(e){return e instanceof FormData||e instanceof Blob||e instanceof URLSearchParams||typeof e==`string`||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||typeof ReadableStream<`u`&&e instanceof ReadableStream}export{t as buildRequestInit,e as hash,n as isBodyInit};
|
|
1
|
+
import{hash as e}from"@vielzeug/arsenal/object";function t(e,t,r,i,a){return r!==void 0&&!n(r)?{...a,body:JSON.stringify(r),headers:{"content-type":`application/json`,...t},method:e.toUpperCase(),signal:i}:{...a,...r!==void 0&&{body:r},headers:t,method:e.toUpperCase(),signal:i}}function n(e){return e instanceof FormData||e instanceof Blob||e instanceof URLSearchParams||typeof e==`string`||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||typeof ReadableStream<`u`&&e instanceof ReadableStream}export{t as buildRequestInit,e as hash,n as isBodyInit};
|
|
2
2
|
//# sourceMappingURL=serialize.js.map
|
package/dist/serialize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.js","names":[],"sources":["../src/serialize.ts"],"sourcesContent":["import { hash } from '@vielzeug/arsenal';\n\nexport { hash };\n\n/**\n * Build a `RequestInit` object from the request components, handling body serialisation.\n * Plain objects/arrays are JSON-serialised and `content-type: application/json` is added.\n * BodyInit values (FormData, Blob, etc.) are forwarded as-is.\n */\nexport function buildRequestInit(\n method: string,\n headers: Record<string, string>,\n body: unknown,\n signal: AbortSignal | undefined,\n rest: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>,\n): RequestInit {\n if (body !== undefined && !isBodyInit(body)) {\n return {\n ...rest,\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json', ...headers },\n method: method.toUpperCase(),\n signal,\n };\n }\n\n return {\n ...rest,\n ...(body !== undefined && { body: body as BodyInit }),\n headers,\n method: method.toUpperCase(),\n signal,\n };\n}\n\nexport function isBodyInit(value: unknown): value is BodyInit {\n return (\n value instanceof FormData ||\n value instanceof Blob ||\n value instanceof URLSearchParams ||\n typeof value === 'string' ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n (typeof ReadableStream !== 'undefined' && value instanceof ReadableStream)\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"serialize.js","names":[],"sources":["../src/serialize.ts"],"sourcesContent":["import { hash } from '@vielzeug/arsenal/object';\n\nexport { hash };\n\n/**\n * Build a `RequestInit` object from the request components, handling body serialisation.\n * Plain objects/arrays are JSON-serialised and `content-type: application/json` is added.\n * BodyInit values (FormData, Blob, etc.) are forwarded as-is.\n */\nexport function buildRequestInit(\n method: string,\n headers: Record<string, string>,\n body: unknown,\n signal: AbortSignal | undefined,\n rest: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>,\n): RequestInit {\n if (body !== undefined && !isBodyInit(body)) {\n return {\n ...rest,\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json', ...headers },\n method: method.toUpperCase(),\n signal,\n };\n }\n\n return {\n ...rest,\n ...(body !== undefined && { body: body as BodyInit }),\n headers,\n method: method.toUpperCase(),\n signal,\n };\n}\n\nexport function isBodyInit(value: unknown): value is BodyInit {\n return (\n value instanceof FormData ||\n value instanceof Blob ||\n value instanceof URLSearchParams ||\n typeof value === 'string' ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n (typeof ReadableStream !== 'undefined' && value instanceof ReadableStream)\n );\n}\n"],"mappings":"gDASA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACa,CAWb,OAVI,IAAS,IAAA,IAAa,CAAC,EAAW,CAAI,EACjC,CACL,GAAG,EACH,KAAM,KAAK,UAAU,CAAI,EACzB,QAAS,CAAE,eAAgB,mBAAoB,GAAG,CAAQ,EAC1D,OAAQ,EAAO,YAAY,EAC3B,QACF,EAGK,CACL,GAAG,EACH,GAAI,IAAS,IAAA,IAAa,CAAQ,MAAiB,EACnD,UACA,OAAQ,EAAO,YAAY,EAC3B,QACF,CACF,CAEA,SAAgB,EAAW,EAAmC,CAC5D,OACE,aAAiB,UACjB,aAAiB,MACjB,aAAiB,iBACjB,OAAO,GAAU,UACjB,aAAiB,aACjB,YAAY,OAAO,CAAK,GACvB,OAAO,eAAmB,KAAe,aAAiB,cAE/D"}
|
package/dist/stream.cjs
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./serialize.cjs"),n=require("./transport.cjs"),r=require("./url.cjs");function i(e){let t=new AbortController,n=e(t.signal);return{next:e=>n.next(e),return:e=>(t.abort(),n.return(e)),[Symbol.asyncIterator](){return this},throw:e=>(t.abort(),n.throw(e))}}async function a(i,a,o){if(i.disposed)throw new e.CourierDisposedError(`Courier`);let s=new AbortController,c=i.track(s),l=(o.method??(o.body===void 0?`GET`:`POST`)).toUpperCase(),u=n.buildTimeoutSignal(o.timeout??1/0,n.anySignal(o.signal,s.signal)),d=a;try{d=r.buildUrl(i.baseUrl,a,o.params,o.query);let n=i.mergeHeaders(o.headers),{headers:f,...p}=t.buildRequestInit(l,n,o.body,u,o.fetchInit??{}),m=await i.dispatch({headers:f,init:p,url:d});if(!m.ok)throw e.CourierHttpError.fromResponse(m,await m.text().catch(()=>``),l,d);if(!m.body)throw new e.CourierParseError(`Response has no body`);return{controller:s,response:m,untrack:c}}catch(t){throw c(),t instanceof e.CourierHttpError||t instanceof e.CourierParseError?t:e.classifyRequestError(t,l,d,u)}}function o(t){async function*r(e,n){let{controller:r,response:i,untrack:o}=await a(t,e,n),s=i.body.getReader(),c=new TextDecoder,l=``,u=`message`,d=``;try{for(;;){let e=await s.read();if(e.done)break;l+=c.decode(e.value,{stream:!0}).replace(/\r\n?/g,`
|
|
2
|
+
`);let t;for(;(t=l.indexOf(`
|
|
3
|
+
`))!==-1;){let e=l.slice(0,t);if(l=l.slice(t+1),e===``){if(d!==``){let e=d.endsWith(`
|
|
4
|
+
`)?d.slice(0,-1):d;try{e=JSON.parse(e)}catch{}yield{data:e,event:u}}d=``,u=`message`;continue}if(e.startsWith(`:`))continue;let n=e.indexOf(`:`),r=n===-1?e:e.slice(0,n),i=(n===-1?``:e.slice(n+1)).replace(/^ /,``);r===`data`?d+=`${i}\n`:r===`event`&&(u=i)}}}finally{s.releaseLock(),r.abort(),o()}}async function*o(n,r){let{controller:i,response:o,untrack:s}=await a(t,n,r),c=o.body.getReader(),l=new TextDecoder,u=``;try{for(;;){let t=await c.read();if(t.done){if(r.parse===`ndjson`&&u.trim())try{yield JSON.parse(u)}catch{throw new e.CourierParseError(`NDJSON: failed to parse line: ${u.slice(0,200)}`)}break}let n=l.decode(t.value,{stream:!0});if(r.parse!==`ndjson`){yield n;continue}u+=n;let i;for(;(i=u.indexOf(`
|
|
5
|
+
`))!==-1;){let t=u.slice(0,i).trim();if(u=u.slice(i+1),t)try{yield JSON.parse(t)}catch{throw new e.CourierParseError(`NDJSON: failed to parse line: ${t.slice(0,200)}`)}}}}finally{c.releaseLock(),i.abort(),s()}}return{events(e,t={}){return i(i=>r(e,{...t,headers:{accept:`text/event-stream`,"cache-control":`no-cache`,...t.headers},signal:n.anySignal(t.signal,i)}))},read(e,t={}){return i(r=>o(e,{...t,signal:n.anySignal(t.signal,r)}))}}}exports.createStreams=o;
|
|
2
6
|
//# sourceMappingURL=stream.cjs.map
|
package/dist/stream.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.cjs","names":[],"sources":["../src/stream.ts"],"sourcesContent":["import type { ReconnectOptions, StreamRequestConfig } from './stream-shared';\n\nimport { readable, type ReadableConfig } from './stream-readable';\nimport { sse, type SseOptions, type SseSource, type SseStatus } from './stream-sse';\nimport { createTransportCore, type TransportCore, type TransportOptions } from './transport';\n\nexport type { ReadableConfig, ReconnectOptions, SseOptions, SseSource, SseStatus, StreamRequestConfig };\n\n/**\n * Creates a streaming client exposing `sse()` (Server-Sent Events) and `readable()`\n * (`ReadableStream`/NDJSON). Both share the same interceptor pipeline, header management,\n * and `AbortController` lifecycle as `createApi`.\n *\n * @example\n * ```ts\n * const stream = createStream({ baseUrl: 'https://api.example.com' });\n *\n * const src = stream.sse<{ message: { text: string } }>('/events', { reconnect: true });\n * src.on('message', (data) => console.log(data.text));\n *\n * for await (const chunk of stream.readable('/completions', { body: { prompt } })) {\n * process.stdout.write(chunk);\n * }\n *\n * // later:\n * stream.dispose();\n * ```\n */\nexport function createStream(opts?: TransportOptions & { transport?: TransportCore }) {\n const { transport: sharedTransport, ...transportOpts } = opts ?? {};\n const ownTransport = !sharedTransport;\n const transport = sharedTransport ?? createTransportCore(transportOpts);\n\n return {\n cancelAll(): void {\n transport.cancelAll();\n },\n get disposalSignal() {\n return transport.disposalSignal;\n },\n dispose(): void {\n if (ownTransport) transport.dispose();\n },\n get disposed() {\n return transport.disposed;\n },\n getHeaders: transport.getHeaders,\n headers: transport.headers,\n readable: <T = string, P extends string = string>(url: P, config?: ReadableConfig<P>): AsyncGenerator<T> =>\n readable<T, P>(transport, url, config),\n sse: <TEvents extends Record<string, unknown> = Record<string, string>, P extends string = string>(\n url: P,\n config?: SseOptions<P>,\n ): SseSource<TEvents> => sse<TEvents, P>(transport, url, config),\n [Symbol.dispose](): void {\n this.dispose();\n },\n use: transport.use,\n };\n}\n\nexport type StreamClient = ReturnType<typeof createStream>;\n"],"mappings":"oGA4BA,SAAgB,EAAa,EAAyD,CACpF,GAAM,CAAE,UAAW,EAAiB,GAAG,GAAkB,GAAQ,CAAC,EAC5D,EAAe,CAAC,EAChB,EAAY,GAAmB,EAAA,oBAAoB,CAAa,EAEtE,MAAO,CACL,WAAkB,CAChB,EAAU,UAAU,CACtB,EACA,IAAI,gBAAiB,CACnB,OAAO,EAAU,cACnB,EACA,SAAgB,CACV,GAAc,EAAU,QAAQ,CACtC,EACA,IAAI,UAAW,CACb,OAAO,EAAU,QACnB,EACA,WAAY,EAAU,WACtB,QAAS,EAAU,QACnB,UAAkD,EAAQ,IACxD,EAAA,SAAe,EAAW,EAAK,CAAM,EACvC,KACE,EACA,IACuB,EAAA,IAAgB,EAAW,EAAK,CAAM,EAC/D,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,EACA,IAAK,EAAU,GACjB,CACF"}
|
|
1
|
+
{"version":3,"file":"stream.cjs","names":[],"sources":["../src/stream.ts"],"sourcesContent":["import type { Params } from './url';\n\nimport { classifyRequestError, CourierDisposedError, CourierHttpError, CourierParseError } from './errors';\nimport { buildRequestInit } from './serialize';\nimport { anySignal, buildTimeoutSignal, type TransportCore } from './transport';\nimport { buildUrl } from './url';\n\nexport type StreamOptions<P extends string = string> = {\n body?: unknown;\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 signal?: AbortSignal;\n timeout?: number;\n};\n\nexport type StreamEvent<T = unknown> = { readonly data: T; readonly event: string };\n\nfunction abortable<T>(create: (signal: AbortSignal) => AsyncGenerator<T>): AsyncIterableIterator<T> {\n const controller = new AbortController();\n const iterator = create(controller.signal);\n\n return {\n next: (value?: undefined) => iterator.next(value),\n return: (value?: T) => {\n controller.abort();\n\n return iterator.return(value as T);\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n throw: (error?: unknown) => {\n controller.abort();\n\n return iterator.throw(error);\n },\n };\n}\n\nasync function open(\n transport: TransportCore,\n url: string,\n config: StreamOptions,\n): Promise<{ controller: AbortController; response: Response; untrack: () => void }> {\n if (transport.disposed) throw new CourierDisposedError('Courier');\n\n const controller = new AbortController();\n const untrack = transport.track(controller);\n const method = (config.method ?? (config.body === undefined ? 'GET' : 'POST')).toUpperCase();\n const signal = buildTimeoutSignal(\n config.timeout ?? Number.POSITIVE_INFINITY,\n anySignal(config.signal, controller.signal),\n );\n let fullUrl = url;\n\n try {\n fullUrl = buildUrl(transport.baseUrl, url, config.params as Params | undefined, config.query);\n\n const headers = transport.mergeHeaders(config.headers);\n const { headers: requestHeaders, ...init } = buildRequestInit(\n method,\n headers,\n config.body,\n signal,\n config.fetchInit ?? {},\n );\n const response = await transport.dispatch({\n headers: requestHeaders as Record<string, string>,\n init,\n url: fullUrl,\n });\n\n if (!response.ok)\n throw CourierHttpError.fromResponse(response, await response.text().catch(() => ''), method, fullUrl);\n\n if (!response.body) throw new CourierParseError('Response has no body');\n\n return { controller, response, untrack };\n } catch (error) {\n untrack();\n\n if (error instanceof CourierHttpError || error instanceof CourierParseError) throw error;\n\n throw classifyRequestError(error, method, fullUrl, signal);\n }\n}\n\n/** Platform-shaped streaming primitives: async iteration owns cancellation and backpressure. */\nexport function createStreams(transport: TransportCore) {\n async function* events<T = unknown, P extends string = string>(\n url: P,\n config: StreamOptions<P>,\n ): AsyncGenerator<StreamEvent<T>> {\n const { controller, response, untrack } = await open(transport, url, config);\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let event = 'message';\n let data = '';\n\n try {\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) break;\n\n buffer += decoder.decode(chunk.value, { stream: true }).replace(/\\r\\n?/g, '\\n');\n\n let newline: number;\n\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline);\n\n buffer = buffer.slice(newline + 1);\n\n if (line === '') {\n if (data !== '') {\n let parsed: unknown = data.endsWith('\\n') ? data.slice(0, -1) : data;\n\n try {\n parsed = JSON.parse(parsed as string);\n } catch {\n // SSE text payloads are valid raw values.\n }\n\n yield { data: parsed as T, event };\n }\n\n data = '';\n event = 'message';\n continue;\n }\n\n if (line.startsWith(':')) continue;\n\n const separator = line.indexOf(':');\n const field = separator === -1 ? line : line.slice(0, separator);\n const value = (separator === -1 ? '' : line.slice(separator + 1)).replace(/^ /, '');\n\n if (field === 'data') data += `${value}\\n`;\n else if (field === 'event') event = value;\n }\n }\n } finally {\n reader.releaseLock();\n controller.abort();\n untrack();\n }\n }\n\n async function* read<T = string, P extends string = string>(\n url: P,\n config: StreamOptions<P> & { parse?: 'ndjson' | 'text' },\n ): AsyncGenerator<T> {\n const { controller, response, untrack } = await open(transport, url, config);\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) {\n if (config.parse === 'ndjson' && buffer.trim()) {\n try {\n yield JSON.parse(buffer) as T;\n } catch {\n throw new CourierParseError(`NDJSON: failed to parse line: ${buffer.slice(0, 200)}`);\n }\n }\n\n break;\n }\n\n const text = decoder.decode(chunk.value, { stream: true });\n\n if (config.parse !== 'ndjson') {\n yield text as T;\n continue;\n }\n\n buffer += text;\n\n let newline: number;\n\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline).trim();\n\n buffer = buffer.slice(newline + 1);\n\n if (!line) continue;\n\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 } finally {\n reader.releaseLock();\n controller.abort();\n untrack();\n }\n }\n\n return {\n events<T = unknown, P extends string = string>(\n url: P,\n config: StreamOptions<P> = {},\n ): AsyncIterableIterator<StreamEvent<T>> {\n return abortable((signal) =>\n events<T, P>(url, {\n ...config,\n headers: { accept: 'text/event-stream', 'cache-control': 'no-cache', ...config.headers },\n signal: anySignal(config.signal, signal),\n }),\n );\n },\n read<T = string, P extends string = string>(\n url: P,\n config: StreamOptions<P> & { parse?: 'ndjson' | 'text' } = {},\n ): AsyncIterableIterator<T> {\n return abortable((signal) => read<T, P>(url, { ...config, signal: anySignal(config.signal, signal) }));\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,41 +1,24 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* stream.dispose();
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export declare function createStream(opts?: TransportOptions & {
|
|
27
|
-
transport?: TransportCore;
|
|
28
|
-
}): {
|
|
29
|
-
cancelAll(): void;
|
|
30
|
-
readonly disposalSignal: AbortSignal;
|
|
31
|
-
dispose(): void;
|
|
32
|
-
readonly disposed: boolean;
|
|
33
|
-
getHeaders: () => Readonly<Record<string, string>>;
|
|
34
|
-
headers: (updates: Record<string, string | undefined>) => void;
|
|
35
|
-
readable: <T = string, P extends string = string>(url: P, config?: ReadableConfig<P>) => AsyncGenerator<T>;
|
|
36
|
-
sse: <TEvents extends Record<string, unknown> = Record<string, string>, P extends string = string>(url: P, config?: SseOptions<P>) => SseSource<TEvents>;
|
|
37
|
-
[Symbol.dispose](): void;
|
|
38
|
-
use: (interceptor: import("./transport").Interceptor) => () => void;
|
|
1
|
+
import type { Params } from './url';
|
|
2
|
+
import { type TransportCore } from './transport';
|
|
3
|
+
export type StreamOptions<P extends string = string> = {
|
|
4
|
+
body?: unknown;
|
|
5
|
+
fetchInit?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
method?: string;
|
|
8
|
+
params?: P extends string ? Record<string, string | number | boolean> : never;
|
|
9
|
+
query?: Params;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
};
|
|
13
|
+
export type StreamEvent<T = unknown> = {
|
|
14
|
+
readonly data: T;
|
|
15
|
+
readonly event: string;
|
|
16
|
+
};
|
|
17
|
+
/** Platform-shaped streaming primitives: async iteration owns cancellation and backpressure. */
|
|
18
|
+
export declare function createStreams(transport: TransportCore): {
|
|
19
|
+
events<T = unknown, P extends string = string>(url: P, config?: StreamOptions<P>): AsyncIterableIterator<StreamEvent<T>>;
|
|
20
|
+
read<T = string, P extends string = string>(url: P, config?: StreamOptions<P> & {
|
|
21
|
+
parse?: "ndjson" | "text";
|
|
22
|
+
}): AsyncIterableIterator<T>;
|
|
39
23
|
};
|
|
40
|
-
export type StreamClient = ReturnType<typeof createStream>;
|
|
41
24
|
//# sourceMappingURL=stream.d.ts.map
|
package/dist/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAIpC,OAAO,EAAiC,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGhF,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,KAAK,CAAC;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAwEpF,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,SAAS,EAAE,aAAa;WAwH3C,CAAC,YAAY,CAAC,SAAS,MAAM,gBAC7B,CAAC,WACE,aAAa,CAAC,CAAC,CAAC,GACvB,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;SASnC,CAAC,WAAW,CAAC,SAAS,MAAM,gBAC1B,CAAC,WACE,aAAa,CAAC,CAAC,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAE,GACvD,qBAAqB,CAAC,CAAC,CAAC;EAI9B"}
|
package/dist/stream.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CourierDisposedError as e,CourierHttpError as t,CourierParseError as n,classifyRequestError as r}from"./errors.js";import{buildRequestInit as i}from"./serialize.js";import{anySignal as a,buildTimeoutSignal as o}from"./transport.js";import{buildUrl as s}from"./url.js";function c(e){let t=new AbortController,n=e(t.signal);return{next:e=>n.next(e),return:e=>(t.abort(),n.return(e)),[Symbol.asyncIterator](){return this},throw:e=>(t.abort(),n.throw(e))}}async function l(c,l,u){if(c.disposed)throw new e(`Courier`);let d=new AbortController,f=c.track(d),p=(u.method??(u.body===void 0?`GET`:`POST`)).toUpperCase(),m=o(u.timeout??1/0,a(u.signal,d.signal)),h=l;try{h=s(c.baseUrl,l,u.params,u.query);let e=c.mergeHeaders(u.headers),{headers:r,...a}=i(p,e,u.body,m,u.fetchInit??{}),o=await c.dispatch({headers:r,init:a,url:h});if(!o.ok)throw t.fromResponse(o,await o.text().catch(()=>``),p,h);if(!o.body)throw new n(`Response has no body`);return{controller:d,response:o,untrack:f}}catch(e){throw f(),e instanceof t||e instanceof n?e:r(e,p,h,m)}}function u(e){async function*t(t,n){let{controller:r,response:i,untrack:a}=await l(e,t,n),o=i.body.getReader(),s=new TextDecoder,c=``,u=`message`,d=``;try{for(;;){let e=await o.read();if(e.done)break;c+=s.decode(e.value,{stream:!0}).replace(/\r\n?/g,`
|
|
2
|
+
`);let t;for(;(t=c.indexOf(`
|
|
3
|
+
`))!==-1;){let e=c.slice(0,t);if(c=c.slice(t+1),e===``){if(d!==``){let e=d.endsWith(`
|
|
4
|
+
`)?d.slice(0,-1):d;try{e=JSON.parse(e)}catch{}yield{data:e,event:u}}d=``,u=`message`;continue}if(e.startsWith(`:`))continue;let n=e.indexOf(`:`),r=n===-1?e:e.slice(0,n),i=(n===-1?``:e.slice(n+1)).replace(/^ /,``);r===`data`?d+=`${i}\n`:r===`event`&&(u=i)}}}finally{o.releaseLock(),r.abort(),a()}}async function*r(t,r){let{controller:i,response:a,untrack:o}=await l(e,t,r),s=a.body.getReader(),c=new TextDecoder,u=``;try{for(;;){let e=await s.read();if(e.done){if(r.parse===`ndjson`&&u.trim())try{yield JSON.parse(u)}catch{throw new n(`NDJSON: failed to parse line: ${u.slice(0,200)}`)}break}let t=c.decode(e.value,{stream:!0});if(r.parse!==`ndjson`){yield t;continue}u+=t;let i;for(;(i=u.indexOf(`
|
|
5
|
+
`))!==-1;){let e=u.slice(0,i).trim();if(u=u.slice(i+1),e)try{yield JSON.parse(e)}catch{throw new n(`NDJSON: failed to parse line: ${e.slice(0,200)}`)}}}}finally{s.releaseLock(),i.abort(),o()}}return{events(e,n={}){return c(r=>t(e,{...n,headers:{accept:`text/event-stream`,"cache-control":`no-cache`,...n.headers},signal:a(n.signal,r)}))},read(e,t={}){return c(n=>r(e,{...t,signal:a(t.signal,n)}))}}}export{u as createStreams};
|
|
2
6
|
//# sourceMappingURL=stream.js.map
|
package/dist/stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","names":[],"sources":["../src/stream.ts"],"sourcesContent":["import type { ReconnectOptions, StreamRequestConfig } from './stream-shared';\n\nimport { readable, type ReadableConfig } from './stream-readable';\nimport { sse, type SseOptions, type SseSource, type SseStatus } from './stream-sse';\nimport { createTransportCore, type TransportCore, type TransportOptions } from './transport';\n\nexport type { ReadableConfig, ReconnectOptions, SseOptions, SseSource, SseStatus, StreamRequestConfig };\n\n/**\n * Creates a streaming client exposing `sse()` (Server-Sent Events) and `readable()`\n * (`ReadableStream`/NDJSON). Both share the same interceptor pipeline, header management,\n * and `AbortController` lifecycle as `createApi`.\n *\n * @example\n * ```ts\n * const stream = createStream({ baseUrl: 'https://api.example.com' });\n *\n * const src = stream.sse<{ message: { text: string } }>('/events', { reconnect: true });\n * src.on('message', (data) => console.log(data.text));\n *\n * for await (const chunk of stream.readable('/completions', { body: { prompt } })) {\n * process.stdout.write(chunk);\n * }\n *\n * // later:\n * stream.dispose();\n * ```\n */\nexport function createStream(opts?: TransportOptions & { transport?: TransportCore }) {\n const { transport: sharedTransport, ...transportOpts } = opts ?? {};\n const ownTransport = !sharedTransport;\n const transport = sharedTransport ?? createTransportCore(transportOpts);\n\n return {\n cancelAll(): void {\n transport.cancelAll();\n },\n get disposalSignal() {\n return transport.disposalSignal;\n },\n dispose(): void {\n if (ownTransport) transport.dispose();\n },\n get disposed() {\n return transport.disposed;\n },\n getHeaders: transport.getHeaders,\n headers: transport.headers,\n readable: <T = string, P extends string = string>(url: P, config?: ReadableConfig<P>): AsyncGenerator<T> =>\n readable<T, P>(transport, url, config),\n sse: <TEvents extends Record<string, unknown> = Record<string, string>, P extends string = string>(\n url: P,\n config?: SseOptions<P>,\n ): SseSource<TEvents> => sse<TEvents, P>(transport, url, config),\n [Symbol.dispose](): void {\n this.dispose();\n },\n use: transport.use,\n };\n}\n\nexport type StreamClient = ReturnType<typeof createStream>;\n"],"mappings":"2IA4BA,SAAgB,EAAa,EAAyD,CACpF,GAAM,CAAE,UAAW,EAAiB,GAAG,GAAkB,GAAQ,CAAC,EAC5D,EAAe,CAAC,EAChB,EAAY,GAAmB,EAAoB,CAAa,EAEtE,MAAO,CACL,WAAkB,CAChB,EAAU,UAAU,CACtB,EACA,IAAI,gBAAiB,CACnB,OAAO,EAAU,cACnB,EACA,SAAgB,CACV,GAAc,EAAU,QAAQ,CACtC,EACA,IAAI,UAAW,CACb,OAAO,EAAU,QACnB,EACA,WAAY,EAAU,WACtB,QAAS,EAAU,QACnB,UAAkD,EAAQ,IACxD,EAAe,EAAW,EAAK,CAAM,EACvC,KACE,EACA,IACuB,EAAgB,EAAW,EAAK,CAAM,EAC/D,CAAC,OAAO,UAAiB,CACvB,KAAK,QAAQ,CACf,EACA,IAAK,EAAU,GACjB,CACF"}
|
|
1
|
+
{"version":3,"file":"stream.js","names":[],"sources":["../src/stream.ts"],"sourcesContent":["import type { Params } from './url';\n\nimport { classifyRequestError, CourierDisposedError, CourierHttpError, CourierParseError } from './errors';\nimport { buildRequestInit } from './serialize';\nimport { anySignal, buildTimeoutSignal, type TransportCore } from './transport';\nimport { buildUrl } from './url';\n\nexport type StreamOptions<P extends string = string> = {\n body?: unknown;\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 signal?: AbortSignal;\n timeout?: number;\n};\n\nexport type StreamEvent<T = unknown> = { readonly data: T; readonly event: string };\n\nfunction abortable<T>(create: (signal: AbortSignal) => AsyncGenerator<T>): AsyncIterableIterator<T> {\n const controller = new AbortController();\n const iterator = create(controller.signal);\n\n return {\n next: (value?: undefined) => iterator.next(value),\n return: (value?: T) => {\n controller.abort();\n\n return iterator.return(value as T);\n },\n [Symbol.asyncIterator]() {\n return this;\n },\n throw: (error?: unknown) => {\n controller.abort();\n\n return iterator.throw(error);\n },\n };\n}\n\nasync function open(\n transport: TransportCore,\n url: string,\n config: StreamOptions,\n): Promise<{ controller: AbortController; response: Response; untrack: () => void }> {\n if (transport.disposed) throw new CourierDisposedError('Courier');\n\n const controller = new AbortController();\n const untrack = transport.track(controller);\n const method = (config.method ?? (config.body === undefined ? 'GET' : 'POST')).toUpperCase();\n const signal = buildTimeoutSignal(\n config.timeout ?? Number.POSITIVE_INFINITY,\n anySignal(config.signal, controller.signal),\n );\n let fullUrl = url;\n\n try {\n fullUrl = buildUrl(transport.baseUrl, url, config.params as Params | undefined, config.query);\n\n const headers = transport.mergeHeaders(config.headers);\n const { headers: requestHeaders, ...init } = buildRequestInit(\n method,\n headers,\n config.body,\n signal,\n config.fetchInit ?? {},\n );\n const response = await transport.dispatch({\n headers: requestHeaders as Record<string, string>,\n init,\n url: fullUrl,\n });\n\n if (!response.ok)\n throw CourierHttpError.fromResponse(response, await response.text().catch(() => ''), method, fullUrl);\n\n if (!response.body) throw new CourierParseError('Response has no body');\n\n return { controller, response, untrack };\n } catch (error) {\n untrack();\n\n if (error instanceof CourierHttpError || error instanceof CourierParseError) throw error;\n\n throw classifyRequestError(error, method, fullUrl, signal);\n }\n}\n\n/** Platform-shaped streaming primitives: async iteration owns cancellation and backpressure. */\nexport function createStreams(transport: TransportCore) {\n async function* events<T = unknown, P extends string = string>(\n url: P,\n config: StreamOptions<P>,\n ): AsyncGenerator<StreamEvent<T>> {\n const { controller, response, untrack } = await open(transport, url, config);\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let event = 'message';\n let data = '';\n\n try {\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) break;\n\n buffer += decoder.decode(chunk.value, { stream: true }).replace(/\\r\\n?/g, '\\n');\n\n let newline: number;\n\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline);\n\n buffer = buffer.slice(newline + 1);\n\n if (line === '') {\n if (data !== '') {\n let parsed: unknown = data.endsWith('\\n') ? data.slice(0, -1) : data;\n\n try {\n parsed = JSON.parse(parsed as string);\n } catch {\n // SSE text payloads are valid raw values.\n }\n\n yield { data: parsed as T, event };\n }\n\n data = '';\n event = 'message';\n continue;\n }\n\n if (line.startsWith(':')) continue;\n\n const separator = line.indexOf(':');\n const field = separator === -1 ? line : line.slice(0, separator);\n const value = (separator === -1 ? '' : line.slice(separator + 1)).replace(/^ /, '');\n\n if (field === 'data') data += `${value}\\n`;\n else if (field === 'event') event = value;\n }\n }\n } finally {\n reader.releaseLock();\n controller.abort();\n untrack();\n }\n }\n\n async function* read<T = string, P extends string = string>(\n url: P,\n config: StreamOptions<P> & { parse?: 'ndjson' | 'text' },\n ): AsyncGenerator<T> {\n const { controller, response, untrack } = await open(transport, url, config);\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const chunk = await reader.read();\n\n if (chunk.done) {\n if (config.parse === 'ndjson' && buffer.trim()) {\n try {\n yield JSON.parse(buffer) as T;\n } catch {\n throw new CourierParseError(`NDJSON: failed to parse line: ${buffer.slice(0, 200)}`);\n }\n }\n\n break;\n }\n\n const text = decoder.decode(chunk.value, { stream: true });\n\n if (config.parse !== 'ndjson') {\n yield text as T;\n continue;\n }\n\n buffer += text;\n\n let newline: number;\n\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline).trim();\n\n buffer = buffer.slice(newline + 1);\n\n if (!line) continue;\n\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 } finally {\n reader.releaseLock();\n controller.abort();\n untrack();\n }\n }\n\n return {\n events<T = unknown, P extends string = string>(\n url: P,\n config: StreamOptions<P> = {},\n ): AsyncIterableIterator<StreamEvent<T>> {\n return abortable((signal) =>\n events<T, P>(url, {\n ...config,\n headers: { accept: 'text/event-stream', 'cache-control': 'no-cache', ...config.headers },\n signal: anySignal(config.signal, signal),\n }),\n );\n },\n read<T = string, P extends string = string>(\n url: P,\n config: StreamOptions<P> & { parse?: 'ndjson' | 'text' } = {},\n ): AsyncIterableIterator<T> {\n return abortable((signal) => read<T, P>(url, { ...config, signal: anySignal(config.signal, signal) }));\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/transport.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.cjs","names":[],"sources":["../src/transport.ts"],"sourcesContent":["import { CourierError } from './errors';\n\n/**\n * Immutable request context passed through the interceptor pipeline.\n *\n * **Never mutate `init` or `headers` directly.** Use `ctx.withHeaders(updates)` to\n * produce a new context with merged headers — this is the safe, idiomatic pattern\n * for interceptors that need to add or override headers.\n *\n * @example\n * ```ts\n * // Correct — returns a new context\n * return next(ctx.withHeaders({ authorization: `Bearer ${token}` }));\n *\n * // Wrong — mutates shared state, risks stomping other interceptors\n * ctx.init.headers = { ...ctx.init.headers, authorization: `Bearer ${token}` };\n * ```\n */\nexport type FetchContext = {\n readonly headers: Readonly<Record<string, string>>;\n readonly init: Readonly<Omit<RequestInit, 'headers'>>;\n readonly url: string;\n /** Returns a new `FetchContext` with the given header overrides merged in (lowercase keys). */\n withHeaders(updates: Record<string, string>): FetchContext;\n};\n\nfunction makeFetchContext(\n url: string,\n headers: Record<string, string>,\n init: Omit<RequestInit, 'headers'>,\n): FetchContext {\n const ctx: FetchContext = {\n headers,\n init,\n url,\n withHeaders(updates: Record<string, string>): FetchContext {\n const merged = { ...headers };\n\n for (const [k, v] of Object.entries(updates)) merged[k.toLowerCase()] = v;\n\n return makeFetchContext(url, merged, init);\n },\n };\n\n return ctx;\n}\n\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\nexport type Interceptor = (ctx: FetchContext, next: (ctx: FetchContext) => Promise<Response>) => Promise<Response>;\n\nexport type TransportOptions = {\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n headers?: Record<string, string>;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT = 30_000;\n\nexport function validateTimeout(timeoutMs: number): void {\n if ((timeoutMs <= 0 || !Number.isFinite(timeoutMs)) && timeoutMs !== Number.POSITIVE_INFINITY) {\n throw new CourierError('[courier] timeout must be a positive number or Infinity');\n }\n}\n\nexport function buildTimeoutSignal(timeoutMs: number, external?: AbortSignal | null): AbortSignal | undefined {\n if (timeoutMs === Number.POSITIVE_INFINITY) {\n return external ?? undefined;\n }\n\n return anySignal(AbortSignal.timeout(timeoutMs), external);\n}\n\n/**\n * Shared transport core: interceptor pipeline, header management, AbortController lifecycle.\n *
|
|
1
|
+
{"version":3,"file":"transport.cjs","names":[],"sources":["../src/transport.ts"],"sourcesContent":["import { CourierError } from './errors';\n\n/**\n * Immutable request context passed through the interceptor pipeline.\n *\n * **Never mutate `init` or `headers` directly.** Use `ctx.withHeaders(updates)` to\n * produce a new context with merged headers — this is the safe, idiomatic pattern\n * for interceptors that need to add or override headers.\n *\n * @example\n * ```ts\n * // Correct — returns a new context\n * return next(ctx.withHeaders({ authorization: `Bearer ${token}` }));\n *\n * // Wrong — mutates shared state, risks stomping other interceptors\n * ctx.init.headers = { ...ctx.init.headers, authorization: `Bearer ${token}` };\n * ```\n */\nexport type FetchContext = {\n readonly headers: Readonly<Record<string, string>>;\n readonly init: Readonly<Omit<RequestInit, 'headers'>>;\n readonly url: string;\n /** Returns a new `FetchContext` with the given header overrides merged in (lowercase keys). */\n withHeaders(updates: Record<string, string>): FetchContext;\n};\n\nfunction makeFetchContext(\n url: string,\n headers: Record<string, string>,\n init: Omit<RequestInit, 'headers'>,\n): FetchContext {\n const ctx: FetchContext = {\n headers,\n init,\n url,\n withHeaders(updates: Record<string, string>): FetchContext {\n const merged = { ...headers };\n\n for (const [k, v] of Object.entries(updates)) merged[k.toLowerCase()] = v;\n\n return makeFetchContext(url, merged, init);\n },\n };\n\n return ctx;\n}\n\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\nexport type Interceptor = (ctx: FetchContext, next: (ctx: FetchContext) => Promise<Response>) => Promise<Response>;\n\nexport type TransportOptions = {\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n headers?: Record<string, string>;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT = 30_000;\n\nexport function validateTimeout(timeoutMs: number): void {\n if ((timeoutMs <= 0 || !Number.isFinite(timeoutMs)) && timeoutMs !== Number.POSITIVE_INFINITY) {\n throw new CourierError('[courier] timeout must be a positive number or Infinity');\n }\n}\n\nexport function buildTimeoutSignal(timeoutMs: number, external?: AbortSignal | null): AbortSignal | undefined {\n if (timeoutMs === Number.POSITIVE_INFINITY) {\n return external ?? undefined;\n }\n\n return anySignal(AbortSignal.timeout(timeoutMs), external);\n}\n\n/**\n * Shared transport core: interceptor pipeline, header management, AbortController lifecycle.\n * HTTP requests and streams build on this to avoid duplicating infrastructure.\n */\nexport function createTransportCore(opts: TransportOptions = {}) {\n const {\n baseUrl = '',\n fetch: fetchFn = globalThis.fetch,\n headers: initialHeaders = {},\n timeout = DEFAULT_TIMEOUT,\n } = opts;\n\n validateTimeout(timeout);\n\n const globalHeaders: Record<string, string> = Object.fromEntries(\n Object.entries(initialHeaders).map(([k, v]) => [k.toLowerCase(), v]),\n );\n const activeControllers = new Set<AbortController>();\n const disposeController = new AbortController();\n const interceptors: Interceptor[] = [];\n let cachedPipeline: ((ctx: FetchContext) => Promise<Response>) | null = null;\n let disposed = false;\n\n function getPipeline(): (ctx: FetchContext) => Promise<Response> {\n if (cachedPipeline) return cachedPipeline;\n\n const base: (ctx: FetchContext) => Promise<Response> = (ctx) =>\n fetchFn(ctx.url, { ...ctx.init, headers: ctx.headers });\n\n cachedPipeline =\n interceptors.length === 0\n ? base\n : interceptors.reduceRight<(ctx: FetchContext) => Promise<Response>>(\n (next, interceptor) => (ctx) => interceptor(ctx, next),\n base,\n );\n\n return cachedPipeline;\n }\n\n function use(interceptor: Interceptor): () => void {\n interceptors.push(interceptor);\n cachedPipeline = null;\n\n return () => {\n const i = interceptors.indexOf(interceptor);\n\n if (i !== -1) {\n interceptors.splice(i, 1);\n cachedPipeline = null;\n }\n };\n }\n\n function headers(updates: Record<string, string | undefined>): void {\n for (const [key, value] of Object.entries(updates)) {\n const k = key.toLowerCase();\n\n if (value === undefined) delete globalHeaders[k];\n else globalHeaders[k] = value;\n }\n }\n\n /** Returns a read-only snapshot of the current global headers. */\n function getHeaders(): Readonly<Record<string, string>> {\n return { ...globalHeaders };\n }\n\n /**\n * Merge global headers with optional per-request and extra headers.\n * Per-request keys are normalised to lowercase.\n */\n function mergeHeaders(perRequest?: Record<string, string>, extra?: Record<string, string>): Record<string, string> {\n const normalized = perRequest\n ? Object.fromEntries(Object.entries(perRequest).map(([k, v]) => [k.toLowerCase(), v]))\n : undefined;\n\n return { ...globalHeaders, ...normalized, ...extra };\n }\n\n /** Register an AbortController for lifecycle tracking (cancelAll / dispose). Returns an untrack fn. */\n function track(ac: AbortController): () => void {\n activeControllers.add(ac);\n\n return () => activeControllers.delete(ac);\n }\n\n /**\n * Dispatch a request through the interceptor pipeline.\n * Accepts a plain `{ url, headers, init }` object and promotes it to a\n * full `FetchContext` with `withHeaders()` before entering the pipeline.\n */\n function dispatch(raw: {\n headers: Record<string, string>;\n init: Omit<RequestInit, 'headers'>;\n url: string;\n }): Promise<Response> {\n return getPipeline()(makeFetchContext(raw.url, raw.headers, raw.init));\n }\n\n function cancelAll(): void {\n for (const ac of [...activeControllers]) ac.abort();\n activeControllers.clear();\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n\n for (const ac of activeControllers) ac.abort();\n\n activeControllers.clear();\n interceptors.length = 0;\n cachedPipeline = null;\n }\n\n return {\n baseUrl,\n cancelAll,\n dispatch,\n get disposalSignal() {\n return disposeController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getHeaders,\n /** Returns the configured request timeout in ms. */\n getTimeout(): number {\n return timeout;\n },\n headers,\n mergeHeaders,\n track,\n use,\n };\n}\n\nexport type TransportCore = ReturnType<typeof createTransportCore>;\n"],"mappings":"gCA0BA,SAAS,EACP,EACA,EACA,EACc,CAcd,MAAO,CAZL,UACA,OACA,MACA,YAAY,EAA+C,CACzD,IAAM,EAAS,CAAE,GAAG,CAAQ,EAE5B,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,CAAO,EAAG,EAAO,EAAE,YAAY,GAAK,EAExE,OAAO,EAAiB,EAAK,EAAQ,CAAI,CAC3C,CAGK,CACT,CAEA,SAAgB,EAAU,GAAG,EAAiF,CAC5G,IAAM,EAAS,EAAQ,OAAQ,GAAwB,GAAK,IAAI,EAE5D,KAAO,SAAW,EAItB,OAFI,EAAO,SAAW,EAAU,EAAO,GAEhC,YAAY,IAAI,CAAM,CAC/B,CAUA,IAAa,EAAkB,IAE/B,SAAgB,EAAgB,EAAyB,CACvD,IAAK,GAAa,GAAK,CAAC,OAAO,SAAS,CAAS,IAAM,IAAc,IACnE,MAAM,IAAI,EAAA,aAAa,yDAAyD,CAEpF,CAEA,SAAgB,EAAmB,EAAmB,EAAwD,CAK5G,OAJI,IAAc,IACT,GAAY,IAAA,GAGd,EAAU,YAAY,QAAQ,CAAS,EAAG,CAAQ,CAC3D,CAMA,SAAgB,EAAoB,EAAyB,CAAC,EAAG,CAC/D,GAAM,CACJ,UAAU,GACV,MAAO,EAAU,WAAW,MAC5B,QAAS,EAAiB,CAAC,EAC3B,UAAU,GACR,EAEJ,EAAgB,CAAO,EAEvB,IAAM,EAAwC,OAAO,YACnD,OAAO,QAAQ,CAAc,CAAC,CAAC,KAAK,CAAC,EAAG,KAAO,CAAC,EAAE,YAAY,EAAG,CAAC,CAAC,CACrE,EACM,EAAoB,IAAI,IACxB,EAAoB,IAAI,gBACxB,EAA8B,CAAC,EACjC,EAAoE,KACpE,EAAW,GAEf,SAAS,GAAwD,CAC/D,GAAI,EAAgB,OAAO,EAE3B,IAAM,EAAkD,GACtD,EAAQ,EAAI,IAAK,CAAE,GAAG,EAAI,KAAM,QAAS,EAAI,OAAQ,CAAC,EAUxD,MARA,GACE,EAAa,SAAW,EACpB,EACA,EAAa,aACV,EAAM,IAAiB,GAAQ,EAAY,EAAK,CAAI,EACrD,CACF,EAEC,CACT,CAEA,SAAS,EAAI,EAAsC,CAIjD,OAHA,EAAa,KAAK,CAAW,EAC7B,EAAiB,SAEJ,CACX,IAAM,EAAI,EAAa,QAAQ,CAAW,EAEtC,IAAM,KACR,EAAa,OAAO,EAAG,CAAC,EACxB,EAAiB,KAErB,CACF,CAEA,SAAS,EAAQ,EAAmD,CAClE,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAO,EAAG,CAClD,IAAM,EAAI,EAAI,YAAY,EAEtB,IAAU,IAAA,GAAW,OAAO,EAAc,GACzC,EAAc,GAAK,CAC1B,CACF,CAGA,SAAS,GAA+C,CACtD,MAAO,CAAE,GAAG,CAAc,CAC5B,CAMA,SAAS,EAAa,EAAqC,EAAwD,CACjH,IAAM,EAAa,EACf,OAAO,YAAY,OAAO,QAAQ,CAAU,CAAC,CAAC,KAAK,CAAC,EAAG,KAAO,CAAC,EAAE,YAAY,EAAG,CAAC,CAAC,CAAC,EACnF,IAAA,GAEJ,MAAO,CAAE,GAAG,EAAe,GAAG,EAAY,GAAG,CAAM,CACrD,CAGA,SAAS,EAAM,EAAiC,CAG9C,OAFA,EAAkB,IAAI,CAAE,MAEX,EAAkB,OAAO,CAAE,CAC1C,CAOA,SAAS,EAAS,EAII,CACpB,OAAO,EAAY,CAAC,CAAC,EAAiB,EAAI,IAAK,EAAI,QAAS,EAAI,IAAI,CAAC,CACvE,CAEA,SAAS,GAAkB,CACzB,IAAK,IAAM,IAAM,CAAC,GAAG,CAAiB,EAAG,EAAG,MAAM,EAClD,EAAkB,MAAM,CAC1B,CAEA,SAAS,GAAgB,CACnB,MAGJ,CADA,EAAW,GACX,EAAkB,MAAM,EAExB,IAAK,IAAM,KAAM,EAAmB,EAAG,MAAM,EAE7C,EAAkB,MAAM,EACxB,EAAa,OAAS,EACtB,EAAiB,IANO,CAO1B,CAEA,MAAO,CACL,UACA,YACA,WACA,IAAI,gBAAiB,CACnB,OAAO,EAAkB,MAC3B,EACA,UACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,aAEA,YAAqB,CACnB,OAAO,CACT,EACA,UACA,eACA,QACA,KACF,CACF"}
|
package/dist/transport.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export declare function validateTimeout(timeoutMs: number): void;
|
|
|
34
34
|
export declare function buildTimeoutSignal(timeoutMs: number, external?: AbortSignal | null): AbortSignal | undefined;
|
|
35
35
|
/**
|
|
36
36
|
* Shared transport core: interceptor pipeline, header management, AbortController lifecycle.
|
|
37
|
-
*
|
|
37
|
+
* HTTP requests and streams build on this to avoid duplicating infrastructure.
|
|
38
38
|
*/
|
|
39
39
|
export declare function createTransportCore(opts?: TransportOptions): {
|
|
40
40
|
baseUrl: string;
|
package/dist/transport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","names":[],"sources":["../src/transport.ts"],"sourcesContent":["import { CourierError } from './errors';\n\n/**\n * Immutable request context passed through the interceptor pipeline.\n *\n * **Never mutate `init` or `headers` directly.** Use `ctx.withHeaders(updates)` to\n * produce a new context with merged headers — this is the safe, idiomatic pattern\n * for interceptors that need to add or override headers.\n *\n * @example\n * ```ts\n * // Correct — returns a new context\n * return next(ctx.withHeaders({ authorization: `Bearer ${token}` }));\n *\n * // Wrong — mutates shared state, risks stomping other interceptors\n * ctx.init.headers = { ...ctx.init.headers, authorization: `Bearer ${token}` };\n * ```\n */\nexport type FetchContext = {\n readonly headers: Readonly<Record<string, string>>;\n readonly init: Readonly<Omit<RequestInit, 'headers'>>;\n readonly url: string;\n /** Returns a new `FetchContext` with the given header overrides merged in (lowercase keys). */\n withHeaders(updates: Record<string, string>): FetchContext;\n};\n\nfunction makeFetchContext(\n url: string,\n headers: Record<string, string>,\n init: Omit<RequestInit, 'headers'>,\n): FetchContext {\n const ctx: FetchContext = {\n headers,\n init,\n url,\n withHeaders(updates: Record<string, string>): FetchContext {\n const merged = { ...headers };\n\n for (const [k, v] of Object.entries(updates)) merged[k.toLowerCase()] = v;\n\n return makeFetchContext(url, merged, init);\n },\n };\n\n return ctx;\n}\n\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\nexport type Interceptor = (ctx: FetchContext, next: (ctx: FetchContext) => Promise<Response>) => Promise<Response>;\n\nexport type TransportOptions = {\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n headers?: Record<string, string>;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT = 30_000;\n\nexport function validateTimeout(timeoutMs: number): void {\n if ((timeoutMs <= 0 || !Number.isFinite(timeoutMs)) && timeoutMs !== Number.POSITIVE_INFINITY) {\n throw new CourierError('[courier] timeout must be a positive number or Infinity');\n }\n}\n\nexport function buildTimeoutSignal(timeoutMs: number, external?: AbortSignal | null): AbortSignal | undefined {\n if (timeoutMs === Number.POSITIVE_INFINITY) {\n return external ?? undefined;\n }\n\n return anySignal(AbortSignal.timeout(timeoutMs), external);\n}\n\n/**\n * Shared transport core: interceptor pipeline, header management, AbortController lifecycle.\n *
|
|
1
|
+
{"version":3,"file":"transport.js","names":[],"sources":["../src/transport.ts"],"sourcesContent":["import { CourierError } from './errors';\n\n/**\n * Immutable request context passed through the interceptor pipeline.\n *\n * **Never mutate `init` or `headers` directly.** Use `ctx.withHeaders(updates)` to\n * produce a new context with merged headers — this is the safe, idiomatic pattern\n * for interceptors that need to add or override headers.\n *\n * @example\n * ```ts\n * // Correct — returns a new context\n * return next(ctx.withHeaders({ authorization: `Bearer ${token}` }));\n *\n * // Wrong — mutates shared state, risks stomping other interceptors\n * ctx.init.headers = { ...ctx.init.headers, authorization: `Bearer ${token}` };\n * ```\n */\nexport type FetchContext = {\n readonly headers: Readonly<Record<string, string>>;\n readonly init: Readonly<Omit<RequestInit, 'headers'>>;\n readonly url: string;\n /** Returns a new `FetchContext` with the given header overrides merged in (lowercase keys). */\n withHeaders(updates: Record<string, string>): FetchContext;\n};\n\nfunction makeFetchContext(\n url: string,\n headers: Record<string, string>,\n init: Omit<RequestInit, 'headers'>,\n): FetchContext {\n const ctx: FetchContext = {\n headers,\n init,\n url,\n withHeaders(updates: Record<string, string>): FetchContext {\n const merged = { ...headers };\n\n for (const [k, v] of Object.entries(updates)) merged[k.toLowerCase()] = v;\n\n return makeFetchContext(url, merged, init);\n },\n };\n\n return ctx;\n}\n\nexport function anySignal(...signals: ReadonlyArray<AbortSignal | null | undefined>): AbortSignal | undefined {\n const active = signals.filter((s): s is AbortSignal => s != null);\n\n if (active.length === 0) return undefined;\n\n if (active.length === 1) return active[0];\n\n return AbortSignal.any(active);\n}\nexport type Interceptor = (ctx: FetchContext, next: (ctx: FetchContext) => Promise<Response>) => Promise<Response>;\n\nexport type TransportOptions = {\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n headers?: Record<string, string>;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT = 30_000;\n\nexport function validateTimeout(timeoutMs: number): void {\n if ((timeoutMs <= 0 || !Number.isFinite(timeoutMs)) && timeoutMs !== Number.POSITIVE_INFINITY) {\n throw new CourierError('[courier] timeout must be a positive number or Infinity');\n }\n}\n\nexport function buildTimeoutSignal(timeoutMs: number, external?: AbortSignal | null): AbortSignal | undefined {\n if (timeoutMs === Number.POSITIVE_INFINITY) {\n return external ?? undefined;\n }\n\n return anySignal(AbortSignal.timeout(timeoutMs), external);\n}\n\n/**\n * Shared transport core: interceptor pipeline, header management, AbortController lifecycle.\n * HTTP requests and streams build on this to avoid duplicating infrastructure.\n */\nexport function createTransportCore(opts: TransportOptions = {}) {\n const {\n baseUrl = '',\n fetch: fetchFn = globalThis.fetch,\n headers: initialHeaders = {},\n timeout = DEFAULT_TIMEOUT,\n } = opts;\n\n validateTimeout(timeout);\n\n const globalHeaders: Record<string, string> = Object.fromEntries(\n Object.entries(initialHeaders).map(([k, v]) => [k.toLowerCase(), v]),\n );\n const activeControllers = new Set<AbortController>();\n const disposeController = new AbortController();\n const interceptors: Interceptor[] = [];\n let cachedPipeline: ((ctx: FetchContext) => Promise<Response>) | null = null;\n let disposed = false;\n\n function getPipeline(): (ctx: FetchContext) => Promise<Response> {\n if (cachedPipeline) return cachedPipeline;\n\n const base: (ctx: FetchContext) => Promise<Response> = (ctx) =>\n fetchFn(ctx.url, { ...ctx.init, headers: ctx.headers });\n\n cachedPipeline =\n interceptors.length === 0\n ? base\n : interceptors.reduceRight<(ctx: FetchContext) => Promise<Response>>(\n (next, interceptor) => (ctx) => interceptor(ctx, next),\n base,\n );\n\n return cachedPipeline;\n }\n\n function use(interceptor: Interceptor): () => void {\n interceptors.push(interceptor);\n cachedPipeline = null;\n\n return () => {\n const i = interceptors.indexOf(interceptor);\n\n if (i !== -1) {\n interceptors.splice(i, 1);\n cachedPipeline = null;\n }\n };\n }\n\n function headers(updates: Record<string, string | undefined>): void {\n for (const [key, value] of Object.entries(updates)) {\n const k = key.toLowerCase();\n\n if (value === undefined) delete globalHeaders[k];\n else globalHeaders[k] = value;\n }\n }\n\n /** Returns a read-only snapshot of the current global headers. */\n function getHeaders(): Readonly<Record<string, string>> {\n return { ...globalHeaders };\n }\n\n /**\n * Merge global headers with optional per-request and extra headers.\n * Per-request keys are normalised to lowercase.\n */\n function mergeHeaders(perRequest?: Record<string, string>, extra?: Record<string, string>): Record<string, string> {\n const normalized = perRequest\n ? Object.fromEntries(Object.entries(perRequest).map(([k, v]) => [k.toLowerCase(), v]))\n : undefined;\n\n return { ...globalHeaders, ...normalized, ...extra };\n }\n\n /** Register an AbortController for lifecycle tracking (cancelAll / dispose). Returns an untrack fn. */\n function track(ac: AbortController): () => void {\n activeControllers.add(ac);\n\n return () => activeControllers.delete(ac);\n }\n\n /**\n * Dispatch a request through the interceptor pipeline.\n * Accepts a plain `{ url, headers, init }` object and promotes it to a\n * full `FetchContext` with `withHeaders()` before entering the pipeline.\n */\n function dispatch(raw: {\n headers: Record<string, string>;\n init: Omit<RequestInit, 'headers'>;\n url: string;\n }): Promise<Response> {\n return getPipeline()(makeFetchContext(raw.url, raw.headers, raw.init));\n }\n\n function cancelAll(): void {\n for (const ac of [...activeControllers]) ac.abort();\n activeControllers.clear();\n }\n\n function dispose(): void {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n\n for (const ac of activeControllers) ac.abort();\n\n activeControllers.clear();\n interceptors.length = 0;\n cachedPipeline = null;\n }\n\n return {\n baseUrl,\n cancelAll,\n dispatch,\n get disposalSignal() {\n return disposeController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getHeaders,\n /** Returns the configured request timeout in ms. */\n getTimeout(): number {\n return timeout;\n },\n headers,\n mergeHeaders,\n track,\n use,\n };\n}\n\nexport type TransportCore = ReturnType<typeof createTransportCore>;\n"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,65 +1,57 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A single segment in a query key — JSON-safe primitives or a plain object of primitives.
|
|
3
|
-
* No `Date`, `bigint`, `Map`, `Set`, or nested arrays — these are not reliably serialisable.
|
|
4
|
-
*/
|
|
5
1
|
export type QueryKeyAtom = string | number | boolean | null | {
|
|
6
|
-
readonly [
|
|
2
|
+
readonly [key: string]: string | number | boolean | null;
|
|
7
3
|
};
|
|
8
|
-
/**
|
|
9
|
-
* Identifies a cached query. Must be a non-empty flat array of `QueryKeyAtom` values.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* ['users', userId]
|
|
14
|
-
* ['users', userId, 'posts', { page: 1, limit: 10 }]
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
4
|
export type QueryKey = readonly [QueryKeyAtom, ...QueryKeyAtom[]];
|
|
18
5
|
export type Unsubscribe = () => void;
|
|
19
|
-
|
|
20
|
-
* Minimal external-store contract for framework integrations.
|
|
21
|
-
*
|
|
22
|
-
* - React: useSyncExternalStore(store.subscribe, store.peek)
|
|
23
|
-
* - Vue: assign `store.peek()` into a shallowRef inside `store.subscribe`
|
|
24
|
-
* - Svelte: adapt to `readable` by forwarding `subscribe`
|
|
25
|
-
*/
|
|
26
|
-
export interface SyncStore<T> {
|
|
27
|
-
peek(): T;
|
|
28
|
-
subscribe(onStoreChange: () => void): Unsubscribe;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Lifecycle status for an async operation.
|
|
32
|
-
*
|
|
33
|
-
* - `'loading'` — no data yet; a fetch may or may not be in-flight.
|
|
34
|
-
* - `'success'` — data is available; `isFetching` may be `true` during background revalidation.
|
|
35
|
-
* - `'error'` — the last operation failed; stale `data` from a prior success may still be present.
|
|
36
|
-
*/
|
|
37
|
-
export type AsyncStatus = 'loading' | 'success' | 'error';
|
|
38
|
-
export type AsyncState<T = unknown> = {
|
|
39
|
-
/**
|
|
40
|
-
* `true` while a fetch is in-flight (including background revalidation).
|
|
41
|
-
* Orthogonal to `status` — a `'success'` entry can have `isFetching: true`.
|
|
42
|
-
*/
|
|
43
|
-
readonly isFetching: boolean;
|
|
44
|
-
/** Shorthand for `status === 'loading'`. Useful as a loading-spinner predicate. */
|
|
45
|
-
readonly isLoading: boolean;
|
|
46
|
-
} & ({
|
|
6
|
+
export type AsyncState<T> = {
|
|
47
7
|
readonly data: undefined;
|
|
48
8
|
readonly error: null;
|
|
9
|
+
readonly isFetching: boolean;
|
|
49
10
|
readonly status: 'loading';
|
|
50
11
|
readonly updatedAt: undefined;
|
|
51
12
|
} | {
|
|
52
13
|
readonly data: T;
|
|
53
14
|
readonly error: null;
|
|
15
|
+
readonly isFetching: boolean;
|
|
54
16
|
readonly status: 'success';
|
|
55
17
|
readonly updatedAt: number;
|
|
56
18
|
} | {
|
|
57
19
|
readonly data: T | undefined;
|
|
58
20
|
readonly error: Error;
|
|
21
|
+
readonly isFetching: false;
|
|
59
22
|
readonly status: 'error';
|
|
60
|
-
/** Timestamp (ms since epoch) of when the error was committed. May coexist with stale `data` from a prior successful fetch. */
|
|
61
23
|
readonly updatedAt: number;
|
|
62
|
-
}
|
|
63
|
-
export type
|
|
64
|
-
|
|
24
|
+
};
|
|
25
|
+
export type QueryContext = {
|
|
26
|
+
readonly key: QueryKey;
|
|
27
|
+
readonly signal: AbortSignal;
|
|
28
|
+
};
|
|
29
|
+
export type QueryDefinition<T> = {
|
|
30
|
+
fetch: (context: QueryContext) => Promise<T>;
|
|
31
|
+
key: QueryKey;
|
|
32
|
+
staleTime?: number;
|
|
33
|
+
};
|
|
34
|
+
export type QueryCache = {
|
|
35
|
+
clear(): void;
|
|
36
|
+
fetch<T>(definition: QueryDefinition<T>, options?: {
|
|
37
|
+
force?: boolean;
|
|
38
|
+
}): Promise<T>;
|
|
39
|
+
get<T>(key: QueryKey): T | undefined;
|
|
40
|
+
getSnapshot<T>(key: QueryKey): AsyncState<T> | null;
|
|
41
|
+
invalidate(key: QueryKey): void;
|
|
42
|
+
keys(): QueryKey[];
|
|
43
|
+
refetchStale(): void;
|
|
44
|
+
set<T>(key: QueryKey, data: T, options?: {
|
|
45
|
+
updatedAt?: number;
|
|
46
|
+
}): void;
|
|
47
|
+
subscribe(key: QueryKey, listener: () => void): Unsubscribe;
|
|
48
|
+
};
|
|
49
|
+
export type MutationContext = {
|
|
50
|
+
readonly signal: AbortSignal;
|
|
51
|
+
};
|
|
52
|
+
export type MutationOptions<T> = {
|
|
53
|
+
onSuccess?: (data: T, queries: QueryCache) => void | Promise<void>;
|
|
54
|
+
request: (context: MutationContext) => Promise<T>;
|
|
55
|
+
signal?: AbortSignal;
|
|
56
|
+
};
|
|
65
57
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/url.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url.cjs","names":[],"sources":["../src/url.ts"],"sourcesContent":["import type { ResponseType } from './response';\n\nimport { CourierParseError } from './errors';\n\ntype QueryScalar = string | number | boolean | null;\n\nexport type ParamValue = QueryScalar | readonly QueryScalar[] | undefined;\nexport type Params = Record<string, ParamValue>;\n\n// Type-safe path params: extracts {param} placeholders from a path string\ntype ExtractPathParams<P extends string> = P extends `${string}{${infer K}}${infer R}`\n ? K | ExtractPathParams<R>\n : never;\n\nexport type PathConfig<P extends string> = [ExtractPathParams<P>] extends [never]\n ? { params?: never }\n : { params: Record<ExtractPathParams<P>, string | number | boolean> };\n\nexport type CourierRequestConfig<P extends string = string, T = unknown> = PathConfig<P> & {\n /** Request body. Plain objects → JSON. BodyInit values passed as-is. */\n body?: unknown;\n
|
|
1
|
+
{"version":3,"file":"url.cjs","names":[],"sources":["../src/url.ts"],"sourcesContent":["import type { ResponseType } from './response';\n\nimport { CourierParseError } from './errors';\n\ntype QueryScalar = string | number | boolean | null;\n\nexport type ParamValue = QueryScalar | readonly QueryScalar[] | undefined;\nexport type Params = Record<string, ParamValue>;\n\n// Type-safe path params: extracts {param} placeholders from a path string\ntype ExtractPathParams<P extends string> = P extends `${string}{${infer K}}${infer R}`\n ? K | ExtractPathParams<R>\n : never;\n\nexport type PathConfig<P extends string> = [ExtractPathParams<P>] extends [never]\n ? { params?: never }\n : { params: Record<ExtractPathParams<P>, string | number | boolean> };\n\nexport type CourierRequestConfig<P extends string = string, T = unknown> = PathConfig<P> & {\n /** Request body. Plain objects → JSON. BodyInit values passed as-is. */\n body?: unknown;\n /** Query string parameters. */\n query?: Params;\n /** Response parsing strategy. */\n responseType?: ResponseType;\n /**\n * Optional schema for response validation. Any object with a `parse(data)` method\n * works — e.g. a `@vielzeug/spell` schema or a plain validator function wrapper.\n * Called after the response body is parsed; throws if validation fails.\n * The schema's return type must be assignable to `T`.\n */\n schema?: { parse(data: unknown): T };\n /** Request timeout in ms. Overrides client default. */\n timeout?: number;\n};\n\nexport type HttpRequestConfig<P extends string = string, T = unknown> = CourierRequestConfig<P, T> & {\n /** Raw fetch options for advanced use (credentials, cache, mode, referrer, etc.). */\n fetchInit?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'>;\n /** Per-request headers merged with (and overriding) global client headers. */\n headers?: Record<string, string>;\n /** External abort signal merged with internal cancellation. */\n signal?: AbortSignal;\n};\n\nexport function buildUrl(base: string, path: string, params?: Params, query?: Params): string {\n const baseClean = base.replace(/\\/+$/, '');\n let pathClean = path.replace(/^\\/+/, '');\n\n pathClean = pathClean.replace(/\\{([^}]+)\\}/g, (_, key: string) => {\n const val = params?.[key];\n\n if (val === undefined || val === null) throw new CourierParseError(`unresolved path param {${key}} in '${path}'`);\n\n return encodeURIComponent(String(val));\n });\n\n const url = baseClean && pathClean ? `${baseClean}/${pathClean}` : baseClean || pathClean;\n\n if (!query) return url;\n\n const qs = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) {\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (entry !== undefined) {\n qs.append(key, entry === null ? '' : String(entry));\n }\n }\n\n continue;\n }\n\n qs.append(key, value === null ? '' : String(value));\n }\n\n const queryString = qs.toString();\n\n return queryString ? `${url}${url.includes('?') ? '&' : '?'}${queryString}` : url;\n}\n"],"mappings":"gCA6CA,SAAgB,EAAS,EAAc,EAAc,EAAiB,EAAwB,CAC5F,IAAM,EAAY,EAAK,QAAQ,OAAQ,EAAE,EACrC,EAAY,EAAK,QAAQ,OAAQ,EAAE,EAEvC,EAAY,EAAU,QAAQ,gBAAiB,EAAG,IAAgB,CAChE,IAAM,EAAM,IAAS,GAErB,GAAI,GAA6B,KAAM,MAAM,IAAI,EAAA,kBAAkB,0BAA0B,EAAI,QAAQ,EAAK,EAAE,EAEhH,OAAO,mBAAmB,OAAO,CAAG,CAAC,CACvC,CAAC,EAED,IAAM,EAAM,GAAa,EAAY,GAAG,EAAU,GAAG,IAAc,GAAa,EAEhF,GAAI,CAAC,EAAO,OAAO,EAEnB,IAAM,EAAK,IAAI,gBAEf,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAK,EACzC,OAAU,IAAA,GAId,IAAI,MAAM,QAAQ,CAAK,EAAG,CACxB,IAAK,IAAM,KAAS,EACd,IAAU,IAAA,IACZ,EAAG,OAAO,EAAK,IAAU,KAAO,GAAK,OAAO,CAAK,CAAC,EAItD,QACF,CAEA,EAAG,OAAO,EAAK,IAAU,KAAO,GAAK,OAAO,CAAK,CAAC,CAFlD,CAKF,IAAM,EAAc,EAAG,SAAS,EAEhC,OAAO,EAAc,GAAG,IAAM,EAAI,SAAS,GAAG,EAAI,IAAM,MAAM,IAAgB,CAChF"}
|
package/dist/url.d.ts
CHANGED
|
@@ -11,15 +11,6 @@ export type PathConfig<P extends string> = [ExtractPathParams<P>] extends [never
|
|
|
11
11
|
export type CourierRequestConfig<P extends string = string, T = unknown> = PathConfig<P> & {
|
|
12
12
|
/** Request body. Plain objects → JSON. BodyInit values passed as-is. */
|
|
13
13
|
body?: unknown;
|
|
14
|
-
/**
|
|
15
|
-
* Set to `false` to bypass in-flight deduplication for this request.
|
|
16
|
-
* Safe + idempotent methods (GET, HEAD, OPTIONS) are deduplicated by default.
|
|
17
|
-
* DELETE is idempotent but has side-effects, so it does not auto-dedupe;
|
|
18
|
-
* pass an explicit `dedupeKey` to opt in.
|
|
19
|
-
*/
|
|
20
|
-
dedupe?: boolean;
|
|
21
|
-
/** Explicit deduplication key for non-idempotent requests. */
|
|
22
|
-
dedupeKey?: unknown;
|
|
23
14
|
/** Query string parameters. */
|
|
24
15
|
query?: Params;
|
|
25
16
|
/** Response parsing strategy. */
|
package/dist/url.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC;AAC1E,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAGhD,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,GAClF,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,GACxB,KAAK,CAAC;AAEV,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC7E;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAClB;IAAE,MAAM,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IACzF,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC;AAC1E,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAGhD,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,GAClF,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,GACxB,KAAK,CAAC;AAEV,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC7E;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAClB;IAAE,MAAM,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IACzF,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAA;KAAE,CAAC;IACrC,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,IAAI,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IACnG,qFAAqF;IACrF,SAAS,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACxE,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAuC5F"}
|