@vielzeug/sourcerer 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +86 -0
  2. package/dist/_dev.d.ts +2 -0
  3. package/dist/_dev.d.ts.map +1 -0
  4. package/dist/_utils.cjs +2 -0
  5. package/dist/_utils.cjs.map +1 -0
  6. package/dist/_utils.d.ts +8 -0
  7. package/dist/_utils.d.ts.map +1 -0
  8. package/dist/_utils.js +7 -0
  9. package/dist/_utils.js.map +1 -0
  10. package/dist/applyQuery.cjs +2 -0
  11. package/dist/applyQuery.cjs.map +1 -0
  12. package/dist/applyQuery.d.ts +20 -0
  13. package/dist/applyQuery.d.ts.map +1 -0
  14. package/dist/applyQuery.js +8 -0
  15. package/dist/applyQuery.js.map +1 -0
  16. package/dist/asyncSource.cjs +2 -0
  17. package/dist/asyncSource.cjs.map +1 -0
  18. package/dist/asyncSource.d.ts +62 -0
  19. package/dist/asyncSource.d.ts.map +1 -0
  20. package/dist/asyncSource.js +40 -0
  21. package/dist/asyncSource.js.map +1 -0
  22. package/dist/codecs.cjs +2 -0
  23. package/dist/codecs.cjs.map +1 -0
  24. package/dist/codecs.d.ts +30 -0
  25. package/dist/codecs.d.ts.map +1 -0
  26. package/dist/codecs.js +36 -0
  27. package/dist/codecs.js.map +1 -0
  28. package/dist/core.cjs +2 -0
  29. package/dist/core.cjs.map +1 -0
  30. package/dist/core.d.ts +47 -0
  31. package/dist/core.d.ts.map +1 -0
  32. package/dist/core.js +59 -0
  33. package/dist/core.js.map +1 -0
  34. package/dist/cursorSource.cjs +2 -0
  35. package/dist/cursorSource.cjs.map +1 -0
  36. package/dist/cursorSource.d.ts +4 -0
  37. package/dist/cursorSource.d.ts.map +1 -0
  38. package/dist/cursorSource.js +135 -0
  39. package/dist/cursorSource.js.map +1 -0
  40. package/dist/derive.cjs +2 -0
  41. package/dist/derive.cjs.map +1 -0
  42. package/dist/derive.d.ts +28 -0
  43. package/dist/derive.d.ts.map +1 -0
  44. package/dist/derive.js +40 -0
  45. package/dist/derive.js.map +1 -0
  46. package/dist/errors.cjs +2 -0
  47. package/dist/errors.cjs.map +1 -0
  48. package/dist/errors.d.ts +62 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +29 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/fetchManager.cjs +2 -0
  53. package/dist/fetchManager.cjs.map +1 -0
  54. package/dist/fetchManager.d.ts +27 -0
  55. package/dist/fetchManager.d.ts.map +1 -0
  56. package/dist/fetchManager.js +41 -0
  57. package/dist/fetchManager.js.map +1 -0
  58. package/dist/index.cjs +1 -0
  59. package/dist/index.d.ts +16 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +14 -0
  62. package/dist/infiniteSource.cjs +2 -0
  63. package/dist/infiniteSource.cjs.map +1 -0
  64. package/dist/infiniteSource.d.ts +4 -0
  65. package/dist/infiniteSource.d.ts.map +1 -0
  66. package/dist/infiniteSource.js +135 -0
  67. package/dist/infiniteSource.js.map +1 -0
  68. package/dist/localSource.cjs +2 -0
  69. package/dist/localSource.cjs.map +1 -0
  70. package/dist/localSource.d.ts +24 -0
  71. package/dist/localSource.d.ts.map +1 -0
  72. package/dist/localSource.js +130 -0
  73. package/dist/localSource.js.map +1 -0
  74. package/dist/merge.cjs +2 -0
  75. package/dist/merge.cjs.map +1 -0
  76. package/dist/merge.d.ts +30 -0
  77. package/dist/merge.d.ts.map +1 -0
  78. package/dist/merge.js +34 -0
  79. package/dist/merge.js.map +1 -0
  80. package/dist/pagination.cjs +2 -0
  81. package/dist/pagination.cjs.map +1 -0
  82. package/dist/pagination.d.ts +31 -0
  83. package/dist/pagination.d.ts.map +1 -0
  84. package/dist/pagination.js +34 -0
  85. package/dist/pagination.js.map +1 -0
  86. package/dist/prefetch.cjs +2 -0
  87. package/dist/prefetch.cjs.map +1 -0
  88. package/dist/prefetch.d.ts +32 -0
  89. package/dist/prefetch.d.ts.map +1 -0
  90. package/dist/prefetch.js +38 -0
  91. package/dist/prefetch.js.map +1 -0
  92. package/dist/presets.cjs +2 -0
  93. package/dist/presets.cjs.map +1 -0
  94. package/dist/presets.d.ts +12 -0
  95. package/dist/presets.d.ts.map +1 -0
  96. package/dist/presets.js +24 -0
  97. package/dist/presets.js.map +1 -0
  98. package/dist/remoteSource.cjs +2 -0
  99. package/dist/remoteSource.cjs.map +1 -0
  100. package/dist/remoteSource.d.ts +4 -0
  101. package/dist/remoteSource.d.ts.map +1 -0
  102. package/dist/remoteSource.js +175 -0
  103. package/dist/remoteSource.js.map +1 -0
  104. package/dist/sourcerer.cjs +2 -0
  105. package/dist/sourcerer.cjs.map +1 -0
  106. package/dist/sourcerer.iife.js +2 -0
  107. package/dist/sourcerer.iife.js.map +1 -0
  108. package/dist/sourcerer.js +2 -0
  109. package/dist/sourcerer.js.map +1 -0
  110. package/dist/state.cjs +2 -0
  111. package/dist/state.cjs.map +1 -0
  112. package/dist/state.d.ts +23 -0
  113. package/dist/state.d.ts.map +1 -0
  114. package/dist/state.js +14 -0
  115. package/dist/state.js.map +1 -0
  116. package/dist/types.d.ts +374 -0
  117. package/dist/types.d.ts.map +1 -0
  118. package/package.json +43 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourcerer.cjs","names":["#opts"],"sources":["../src/applyQuery.ts","../src/errors.ts","../src/codecs.ts","../src/_utils.ts","../src/core.ts","../src/fetchManager.ts","../src/asyncSource.ts","../src/cursorSource.ts","../src/derive.ts","../src/infiniteSource.ts","../src/pagination.ts","../src/localSource.ts","../src/merge.ts","../src/remoteSource.ts","../src/prefetch.ts","../src/presets.ts","../src/state.ts"],"sourcesContent":["import type { SourceQuery } from './types';\n\n/**\n * Generic query applicator — delegates to `source.patch(changes)`.\n * Use this with `decodeQuery()` output to restore URL state onto any source:\n *\n * @example\n * ```ts\n * const q = decodeQuery(new URL(location.href).searchParams, { defaultLimit: 20 });\n * await applyQuery(source, q);\n * ```\n *\n * ⚠️ `CursorSource` and `InfiniteSource` have no page-number concept (keyset/append navigation) —\n * their `patch()` only reads `limit`/`search` and silently ignores a `page` field. This compiles\n * (both accept `Partial<SourceQuery>`-shaped input) but a `page` value from `decodeQuery()` is a\n * no-op on those two source types.\n */\nexport function applyQuery<T extends { patch(changes: Partial<SourceQuery>): Promise<void> }>(\n source: T,\n changes: Partial<SourceQuery>,\n): Promise<void> {\n return source.patch(changes);\n}\n","/**\n * Structured context carried by `SourcererError`.\n * Discriminated on `kind` — narrow with `error.context?.kind` to access typed fields.\n *\n * @example\n * ```ts\n * if (source.meta.error?.context?.kind === 'remote') {\n * console.log('Failed page', source.meta.error.context.page);\n * }\n * ```\n */\nexport type SourcererErrorContext =\n | Readonly<{ kind: 'cursor'; limit: number; search?: string }>\n | Readonly<{ kind: 'infinite'; limit: number; page: number; search?: string }>\n | Readonly<{ kind: 'remote'; limit: number; page: number; search?: string }>;\n\n/**\n * Base class for all sourcerer errors. Catch with `instanceof SourcererError` to handle any\n * sourcerer-originated error regardless of specific subtype.\n *\n * Carries the original cause, structured context, and the attempt number.\n *\n * @example\n * ```ts\n * if (source.meta.error) {\n * console.error(source.meta.error.message, source.meta.error.context);\n * }\n * ```\n */\nexport class SourcererError extends Error {\n readonly #opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n };\n\n constructor(\n message: string,\n opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n } = {},\n ) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n this.#opts = opts;\n }\n\n get attempt(): number {\n return this.#opts.attempt ?? 0;\n }\n\n /** Context bag for the error — safe to log. */\n get context(): SourcererErrorContext | undefined {\n return this.#opts.context;\n }\n\n static is(err: unknown): err is SourcererError {\n return err instanceof SourcererError;\n }\n}\n\n/** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */\nexport class SourceTimeoutError extends SourcererError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Source.ready() timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown by `ready()` when the source is disposed while waiting. */\nexport class SourceDisposedError extends SourcererError {\n constructor() {\n super('Source disposed while waiting for ready()');\n }\n}\n","import type { QueryParams, QueryParamsInput, RemoteSourceQuery, SourceQuery } from './types';\n\nimport { SourcererError } from './errors';\n\n/**\n * Serialises a `SourceQuery` or `RemoteSourceQuery` into plain URL-safe string params.\n *\n * ⚠️ `filter` and `sort` are JSON-stringified without a try/catch. A circular object reference\n * throws a native `TypeError` (\"Converting circular structure to JSON\") straight out of this\n * function — ensure filter/sort values are plain serialisable objects, or wrap the call yourself.\n *\n * ⚠️ Round-trip fidelity: `page` and `limit` must be positive integers. `encodeQuery` will\n * serialise any number (including 0), but `decodeQuery` clamps non-positive values to 1/defaultLimit.\n *\n * `encodeQuery` + `decodeQuery` form a round-trip pair: filter/sort values are JSON-stringified\n * on encode and JSON-parsed on decode.\n */\nexport const encodeQuery = <TFilter = unknown, TSort = unknown>(\n query: RemoteSourceQuery<TFilter, TSort> | SourceQuery,\n): QueryParams => {\n const base: QueryParams = {\n ...(query.search && { search: query.search }),\n limit: String(query.limit),\n page: String(query.page),\n };\n\n const rq = query as RemoteSourceQuery<TFilter, TSort>;\n\n if (rq.filter !== undefined) {\n base['filter'] = JSON.stringify(rq.filter);\n }\n\n if (rq.sort !== undefined) {\n base['sort'] = JSON.stringify(rq.sort);\n }\n\n return base;\n};\n\nexport type DecodeQueryOptions = Readonly<{\n defaultLimit?: number;\n strict?: boolean;\n}>;\n\n/**\n * Parses URL query params into a `Partial<RemoteSourceQuery<unknown, unknown>>`.\n * Accepts either a plain `Record<string, string | string[] | undefined>` or a `URLSearchParams` instance.\n *\n * - `filter` and `sort` are JSON-parsed and typed as `unknown` — validate and narrow them\n * with a runtime schema (e.g. Zod) before passing to `applyQuery`.\n * - `search` is omitted from the result when the param is absent (rather than defaulting to `''`).\n * - `limit` and `page` are parsed as positive integers; invalid values fall back to defaults.\n */\nexport const decodeQuery = (\n params: QueryParamsInput | URLSearchParams,\n options: DecodeQueryOptions = {},\n): Partial<RemoteSourceQuery<unknown, unknown>> => {\n const raw: QueryParamsInput =\n params instanceof URLSearchParams ? (Object.fromEntries(params.entries()) as QueryParamsInput) : params;\n\n const { defaultLimit = 20, strict = false } = options;\n\n const parsePositiveInt = (value: string | string[] | undefined, fallback: number): number => {\n if (value === undefined) return fallback;\n\n const str = Array.isArray(value) ? value[0] : value;\n const n = Number(str);\n\n return Number.isInteger(n) && n > 0 ? n : fallback;\n };\n\n const parseJson = (key: string, value: string | string[] | undefined): unknown => {\n if (value === undefined) return undefined;\n\n const str = Array.isArray(value) ? value[0] : value;\n\n try {\n return JSON.parse(str) as unknown;\n } catch {\n if (strict) throw new SourcererError(`Invalid query param \"${key}\": ${str}`);\n\n return undefined;\n }\n };\n\n const rawLimit = raw['limit'];\n const rawPage = raw['page'];\n const rawSearch = raw['search'];\n const rawFilter = raw['filter'];\n const rawSort = raw['sort'];\n\n const filter = rawFilter !== undefined ? parseJson('filter', rawFilter) : undefined;\n const sort = rawSort !== undefined ? parseJson('sort', rawSort) : undefined;\n\n const result: Partial<RemoteSourceQuery<unknown, unknown>> = {\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(rawSearch !== undefined && { search: Array.isArray(rawSearch) ? rawSearch[0] : rawSearch }),\n limit: parsePositiveInt(rawLimit, defaultLimit),\n page: parsePositiveInt(rawPage, 1),\n };\n\n return result;\n};\n","import { backoff, hash, retry } from '@vielzeug/arsenal';\n\nexport { retry };\n\n// Internal stable-key helper — used by source factories, not exposed as public API.\nexport const defaultKeyOf = hash;\n\n/** Extracts a user-facing error message from a caught exception. */\nexport const extractError = (reason: unknown): string => {\n if (reason instanceof Error) return reason.message;\n\n if (typeof reason === 'string' && reason.length > 0) return reason;\n\n return 'Request failed';\n};\n\n/** Default exponential backoff: 1 s, 2 s, 4 s, … capped at 30 s. Receives a 0-indexed failure count. */\nexport const defaultRetryDelay = (attempt: number): number => backoff(attempt);\n","import { SourceDisposedError, SourceTimeoutError } from './errors';\n\n/**\n * Shared source infrastructure: listener management, debounce scheduling, and ready() polling.\n * All source factories compose this core rather than duplicating the same patterns.\n */\nexport type SourceCore = {\n /** Cancels any pending timer without invoking the callback. */\n cancelTimer(): void;\n\n /** `AbortSignal` that is aborted when `dispose()` is called. */\n readonly disposalSignal: AbortSignal;\n\n /**\n * Clears all listeners, cancels any pending timer, and marks the core as disposed.\n * Subsequent method calls on the core become no-ops.\n */\n dispose(): void;\n\n /**\n * If a timer is scheduled, cancels it and immediately invokes `fn`.\n * Returns a resolved Promise when no timer is pending.\n */\n flush(fn: () => Promise<void>): Promise<void>;\n\n /** Whether `dispose()` has been called. All methods become no-ops after disposal. */\n readonly isDisposed: boolean;\n\n /** Whether a debounce timer is currently scheduled (drives `meta.isSearchPending`). */\n readonly isScheduled: boolean;\n\n /**\n * Fire all registered listeners and wake any `ready()` waiters.\n * When provided, `onBefore` runs once before listeners are called — use it to\n * refresh cached meta/current before subscribers observe the new state.\n */\n notify(onBefore?: () => void): void;\n\n /**\n * Returns a Promise that resolves when `isIdle()` returns true.\n * Resolves synchronously if already idle.\n * Rejects with a `TimeoutError` after `timeoutMs` ms if still not idle.\n */\n ready(isIdle: () => boolean, timeoutMs?: number): Promise<void>;\n\n /**\n * Schedules a debounced callback, replacing any pending timer.\n * The callback is invoked after `delayMs` ms of inactivity.\n */\n schedule(fn: () => void, delayMs: number): void;\n\n /** Subscribe to change notifications. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n};\n\nexport function createSourceCore(opts?: { onBeforeNotify?: () => void }): SourceCore {\n const listeners = new Set<() => void>();\n const readyWaiters = new Set<{\n check: () => void;\n reject: (err: unknown) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n }>();\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let disposed = false;\n\n return {\n cancelTimer() {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n },\n\n get disposalSignal() {\n return controller.signal;\n },\n\n dispose() {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n\n for (const waiter of readyWaiters) {\n if (waiter.timeoutId !== undefined) clearTimeout(waiter.timeoutId);\n\n waiter.reject(new SourceDisposedError());\n }\n\n readyWaiters.clear();\n listeners.clear();\n disposed = true;\n controller.abort();\n },\n\n flush(fn) {\n if (!timer) return Promise.resolve();\n\n clearTimeout(timer);\n timer = undefined;\n\n return fn();\n },\n\n get isDisposed() {\n return disposed;\n },\n\n get isScheduled() {\n return timer !== undefined;\n },\n\n notify(onBefore?: () => void) {\n if (disposed) return;\n\n (onBefore ?? opts?.onBeforeNotify)?.();\n\n for (const listener of listeners) {\n listener();\n }\n\n for (const waiter of readyWaiters) {\n waiter.check();\n }\n },\n\n ready(isIdle, timeoutMs) {\n if (disposed) return Promise.reject(new SourceDisposedError());\n\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n const waiter: { check: () => void; reject: (err: unknown) => void; timeoutId?: ReturnType<typeof setTimeout> } =\n {\n check: () => {\n if (isIdle()) {\n if (waiter.timeoutId !== undefined) clearTimeout(waiter.timeoutId);\n\n readyWaiters.delete(waiter);\n resolve();\n }\n },\n reject,\n };\n\n if (timeoutMs !== undefined) {\n waiter.timeoutId = setTimeout(() => {\n readyWaiters.delete(waiter);\n reject(new SourceTimeoutError(timeoutMs));\n }, timeoutMs);\n }\n\n readyWaiters.add(waiter);\n });\n },\n\n schedule(fn, delayMs) {\n if (disposed) return;\n\n if (timer) clearTimeout(timer);\n\n timer = setTimeout(() => {\n timer = undefined;\n fn();\n }, delayMs);\n },\n\n subscribe(listener) {\n if (disposed) return () => {};\n\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n}\n","/**\n * Internal fetch deduplication and in-flight request manager.\n *\n * - Deduplicates identical concurrent queries: joiners await the existing in-flight promise.\n * - Aborts superseded in-flight requests when a new query key arrives.\n * - Tracks pending count so callers can reflect loading state.\n *\n * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,\n * cursorSource, and infiniteSource.\n */\n\ntype InFlightEntry = { controller: AbortController; promise: Promise<void> };\n\nexport type FetchManager<TQuery> = {\n /** Abort all in-flight requests and clear the registry. */\n dispose(): void;\n /** Number of currently in-flight requests (including joiners). */\n readonly pendingCount: number;\n /**\n * Run a fetch for query `q`.\n *\n * - If an identical key is already in-flight, joins as a waiter.\n * - If a different key is in-flight, aborts it and starts a new request.\n * - Calls `onPendingChange()` whenever `pendingCount` changes.\n * - Passes an `isLatest()` predicate to `execute` so it can skip stale responses.\n */\n run(\n q: TQuery,\n execute: (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>,\n onPendingChange: () => void,\n ): Promise<void>;\n};\n\nexport function createFetchManager<TQuery>(keyOf: (q: TQuery) => string): FetchManager<TQuery> {\n const inflight = new Map<string, InFlightEntry>();\n let latestKey = '';\n let _pendingCount = 0;\n\n return {\n dispose() {\n for (const entry of inflight.values()) {\n entry.controller.abort();\n }\n\n inflight.clear();\n _pendingCount = 0;\n },\n\n get pendingCount() {\n return _pendingCount;\n },\n\n async run(q, execute, onPendingChange) {\n const key = keyOf(q);\n\n latestKey = key;\n\n // Abort and remove all superseded in-flight requests.\n for (const [k, entry] of inflight) {\n if (k !== key) {\n entry.controller.abort();\n inflight.delete(k);\n }\n }\n\n // Join an identical in-flight request rather than issuing a duplicate.\n const existing = inflight.get(key);\n\n if (existing) {\n _pendingCount++;\n onPendingChange();\n\n try {\n await existing.promise;\n } finally {\n _pendingCount--;\n onPendingChange();\n }\n\n return;\n }\n\n // New request.\n const controller = new AbortController();\n\n _pendingCount++;\n onPendingChange();\n\n const promise = execute(q, controller.signal, () => key === latestKey).finally(() => {\n inflight.delete(key);\n _pendingCount--;\n onPendingChange();\n });\n\n inflight.set(key, { controller, promise });\n await promise;\n },\n };\n}\n","import type { SourceCore } from './core';\n\nimport { defaultKeyOf, defaultRetryDelay } from './_utils';\nimport { createSourceCore } from './core';\nimport { createFetchManager } from './fetchManager';\n\n/**\n * Configuration subset common to all async source types.\n * Extracted to eliminate duplication across remote, cursor, and infinite sources.\n */\nexport type AsyncSourceConfig<TQuery> = Readonly<{\n autoFetch?: boolean;\n debounceMs?: number;\n queryKey?: (q: TQuery) => string;\n refreshInterval?: number;\n retry?: { attempts?: number; delay?: (attempt: number) => number };\n}>;\n\nexport type FetchExecutor<TQuery> = (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>;\n\nexport type AsyncSourceInfra<TQuery> = {\n [Symbol.dispose](): void;\n /** `true` when `autoFetch !== false`. */\n readonly autoFetch: boolean;\n /** The underlying SourceCore — use directly for subscribe, schedule, cancelTimer, flush, isScheduled. */\n readonly core: SourceCore;\n /** Debounce delay in ms. */\n readonly debounceMs: number;\n /** `AbortSignal` aborted when `dispose()` is called. */\n readonly disposalSignal: AbortSignal;\n /** Dispose lifecycle: cancel refresh interval, dispose FM, dispose core. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()`. */\n readonly disposed: boolean;\n /**\n * Run a deduplicated fetch for query `q`.\n * Calls `onCommit()` whenever pending count changes (for subscriber notification).\n */\n fetch(q: TQuery, execute: FetchExecutor<TQuery>, onCommit: () => void): Promise<void>;\n /** Number of currently in-flight requests. */\n pendingCount(): number;\n /** Resolves when idle (no in-flight requests, no pending debounce). */\n ready(timeout?: number): Promise<void>;\n /** Number of retry attempts after the first failure. */\n readonly retryAttempts: number;\n /** Returns the delay in ms before the nth retry. */\n readonly retryDelay: (attempt: number) => number;\n /**\n * Wire the refresh interval. Must be called once after construction, passing\n * the source-level `doUpdate` callback. Does nothing when `refreshInterval`\n * is unset or ≤ 0.\n */\n startRefreshInterval(doUpdate: () => void): void;\n};\n\n/**\n * Creates the shared infrastructure used by `createRemoteSource`, `createCursorSource`,\n * and `createInfiniteSource`. Eliminates duplication of lifecycle, config-defaults,\n * fetch-manager setup, dispose, ready, flush, and refresh-interval logic.\n *\n * Each source factory composes this and adds its own query state, meta shape, and\n * source-specific methods. Pass `onBeforeNotify` to run `refreshMeta()` before\n * subscribers observe each notification — this eliminates the need for a parallel\n * listeners Set in each source factory.\n */\nexport function createAsyncSource<TQuery>(\n cfg: AsyncSourceConfig<TQuery>,\n keyOf: (q: TQuery) => string = defaultKeyOf as (q: TQuery) => string,\n onBeforeNotify?: () => void,\n): AsyncSourceInfra<TQuery> {\n const core = createSourceCore({ onBeforeNotify });\n const fm = createFetchManager<TQuery>(keyOf);\n const debounceMs = cfg.debounceMs ?? 300;\n const retryAttempts = cfg.retry?.attempts ?? 0;\n const retryDelay = cfg.retry?.delay ?? defaultRetryDelay;\n const autoFetch = cfg.autoFetch !== false;\n\n let refreshTimer: ReturnType<typeof setInterval> | undefined;\n\n const dispose = (): void => {\n if (core.isDisposed) return;\n\n if (refreshTimer !== undefined) {\n clearInterval(refreshTimer);\n refreshTimer = undefined;\n }\n\n fm.dispose();\n core.dispose();\n };\n\n return {\n autoFetch,\n core,\n debounceMs,\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose,\n\n get disposed() {\n return core.isDisposed;\n },\n\n fetch(q, execute, onCommit) {\n return fm.run(q, execute, onCommit);\n },\n\n pendingCount: () => fm.pendingCount,\n\n ready(timeout) {\n return core.ready(() => fm.pendingCount === 0 && !core.isScheduled, timeout);\n },\n retryAttempts,\n\n retryDelay,\n\n startRefreshInterval(doUpdate) {\n if (cfg.refreshInterval !== undefined && cfg.refreshInterval > 0) {\n refreshTimer = setInterval(() => {\n if (!core.isDisposed) doUpdate();\n }, cfg.refreshInterval);\n }\n },\n\n [Symbol.dispose]: dispose,\n };\n}\n","import type { CursorConfig, CursorMeta, CursorSource, CursorSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\n/** Creates a cursor-based (keyset-pagination) source that fetches data from a network endpoint. */\nexport function createCursorSource<T, TCursor = string>(cfg: CursorConfig<T, TCursor>): CursorSource<T, TCursor> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let search = '';\n let afterCursor: TCursor | undefined;\n let beforeCursor: TCursor | undefined;\n let nextCursor: TCursor | undefined;\n let prevCursor: TCursor | undefined;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let pendingSearch: PendingSearch | null = null;\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta: CursorMeta = {\n error: null,\n hasNextPage: false,\n hasPrevPage: false,\n isLoading: false,\n isSearchPending: false,\n pageSize: limit,\n totalItems: 0,\n };\n\n const refreshMeta = () => {\n cachedCurrent = items;\n cachedMeta = {\n error,\n hasNextPage: nextCursor !== undefined,\n hasPrevPage: prevCursor !== undefined,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageSize: limit,\n totalItems: total,\n };\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<CursorSourceQuery<TCursor>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): CursorSourceQuery<TCursor> => ({\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n });\n\n const fetchQuery = (q: CursorSourceQuery<TCursor>): Promise<void> => {\n error = null;\n\n return base.fetch(\n q,\n async (q, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n items = result.items;\n total = result.total ?? result.items.length;\n nextCursor = result.nextCursor;\n prevCursor = result.prevCursor;\n error = null;\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n items = [];\n total = 0;\n nextCursor = undefined;\n prevCursor = undefined;\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'cursor', limit: q.limit, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n if (base.pendingCount() === 0) resolvePendingSearch();\n },\n );\n };\n\n const doUpdate = () => fetchQuery(toRemoteQuery());\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doUpdate();\n\n base.startRefreshInterval(() => void doUpdate());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n resolvePendingSearch();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n get meta() {\n return cachedMeta;\n },\n\n next() {\n if (!nextCursor) return Promise.resolve();\n\n afterCursor = nextCursor;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n base.core.cancelTimer();\n resolvePendingSearch();\n\n return doUpdate();\n },\n\n prev() {\n if (!prevCursor) return Promise.resolve();\n\n beforeCursor = prevCursor;\n afterCursor = undefined;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n base.core.cancelTimer();\n resolvePendingSearch();\n search = '';\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n base.core.cancelTimer();\n resolvePendingSearch();\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n resolvePendingSearch();\n\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n base.core.schedule(() => void doUpdate(), debounceMs);\n notifyListeners();\n\n return promise;\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n resolvePendingSearch();\n base.dispose();\n },\n };\n}\n","import type { DerivedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Creates a reactive read-only projection derived from a parent source.\n *\n * The derived source mirrors the parent's `meta` and applies `transform` to `current`\n * whenever the parent changes. This is useful for client-side projections (e.g. additional\n * filtering, mapping, or sorting) over a remote source without re-fetching.\n *\n * Call `dispose()` to stop listening to the parent.\n *\n * @example\n * ```ts\n * const source = createRemoteSource<User>({ fetch: fetchUsers, limit: 20 });\n *\n * // Show only active users without re-fetching.\n * const activeUsers = deriveSource(source, (items) => items.filter((u) => u.active));\n *\n * activeUsers.subscribe(() => {\n * console.log(activeUsers.current, activeUsers.meta.totalItems);\n * });\n * ```\n */\nexport function deriveSource<T, U, TMeta>(\n parent: {\n readonly current: readonly T[];\n readonly meta: TMeta;\n subscribe(listener: () => void): () => void;\n },\n transform: (items: readonly T[]) => readonly U[],\n): DerivedSource<U, TMeta> {\n const core = createSourceCore();\n let cachedCurrent: readonly U[] = [];\n\n const applyTransform = () => {\n cachedCurrent = transform(parent.current);\n };\n\n applyTransform();\n\n const unsubscribeParent = parent.subscribe(() => {\n applyTransform();\n core.notify();\n });\n\n if ('disposalSignal' in parent && parent.disposalSignal instanceof AbortSignal) {\n parent.disposalSignal.addEventListener('abort', () => core.dispose(), { once: true });\n }\n\n const source: DerivedSource<U, TMeta> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n unsubscribeParent();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n get meta() {\n return parent.meta;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n","import type { InfiniteConfig, InfiniteMeta, InfiniteSource, InfiniteSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\n/** Creates an infinite-scroll source that appends pages on `loadMore()`. */\nexport function createInfiniteSource<T>(cfg: InfiniteConfig<T>): InfiniteSource<T> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let search = '';\n let currentPage = 1;\n let loadedPages = 0;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let isLoadingMore = false;\n let pendingSearch: PendingSearch | null = null;\n\n // ── Cached accessors ─────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta: InfiniteMeta = {\n error: null,\n hasMore: false,\n isLoading: false,\n isLoadingMore: false,\n isSearchPending: false,\n loadedPages: 0,\n pageSize: limit,\n totalItems: 0,\n };\n\n const refreshMeta = () => {\n cachedCurrent = items;\n cachedMeta = {\n error,\n hasMore: items.length < total,\n isLoading: base.pendingCount() > 0 && !isLoadingMore,\n isLoadingMore,\n isSearchPending: base.core.isScheduled,\n loadedPages,\n pageSize: limit,\n totalItems: total,\n };\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<InfiniteSourceQuery>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const fetchPage = (targetPage: number, append: boolean): Promise<void> => {\n const q: InfiniteSourceQuery = {\n ...(search && { search }),\n limit,\n page: targetPage,\n };\n\n error = null;\n\n return base.fetch(\n q,\n async (q, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n if (append) {\n items = [...items, ...result.items];\n } else {\n items = result.items;\n }\n\n total = result.total;\n currentPage = targetPage;\n loadedPages = append ? loadedPages + 1 : 1;\n error = null;\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n if (!append) items = [];\n\n total = append ? total : 0;\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'infinite', limit: q.limit, page: q.page, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n } finally {\n isLoadingMore = false;\n }\n },\n () => {\n notifyListeners();\n\n if (base.pendingCount() === 0) resolvePendingSearch();\n },\n );\n };\n\n const doFetch = () => fetchPage(1, false);\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doFetch();\n\n base.startRefreshInterval(() => void doFetch());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n resolvePendingSearch();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n loadMore() {\n if (!cachedMeta.hasMore || base.pendingCount() > 0 || isLoadingMore) return Promise.resolve();\n\n isLoadingMore = true;\n refreshMeta();\n\n return fetchPage(currentPage + 1, true);\n },\n\n get meta() {\n return cachedMeta;\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n base.core.cancelTimer();\n resolvePendingSearch();\n\n items = [];\n total = 0;\n currentPage = 1;\n loadedPages = 0;\n\n return doFetch();\n },\n\n get query() {\n return {\n ...(search && { search }),\n limit,\n page: currentPage,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n reset() {\n base.core.cancelTimer();\n resolvePendingSearch();\n currentPage = 1;\n loadedPages = 0;\n items = [];\n total = 0;\n search = '';\n\n return doFetch();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n base.core.cancelTimer();\n resolvePendingSearch();\n search = q;\n items = [];\n total = 0;\n loadedPages = 0;\n currentPage = 1;\n\n return doFetch();\n }\n\n if (q === search) return Promise.resolve();\n\n resolvePendingSearch();\n\n search = q;\n items = [];\n total = 0;\n loadedPages = 0;\n currentPage = 1;\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n base.core.schedule(() => void doFetch(), debounceMs);\n notifyListeners();\n\n return promise;\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n resolvePendingSearch();\n base.dispose();\n },\n };\n}\n","import type { SourcererError, SourceMeta } from './types';\n\nconst clampInt = (value: number, minimum: number) => {\n const parsed = Math.trunc(value);\n\n if (!Number.isFinite(parsed)) {\n return minimum;\n }\n\n return Math.max(minimum, parsed);\n};\n\nexport const pageCount = (total: number, limit: number) => {\n const safeTotal = clampInt(total, 0);\n const safeLimit = clampInt(limit, 1);\n\n return Math.max(1, Math.ceil(safeTotal / safeLimit));\n};\n\nexport const clampPage = (page: number, pages: number) => {\n return Math.max(1, Math.min(clampInt(page, 1), Math.max(1, pages)));\n};\n\nexport const createMeta = (state: {\n error: SourcererError | null;\n isLoading: boolean;\n isSearchPending: boolean;\n pageNumber: number;\n pageSize: number;\n totalItems: number;\n}): SourceMeta => {\n const safeLimit = clampInt(state.pageSize, 1);\n const safeTotal = clampInt(state.totalItems, 0);\n const pages = pageCount(safeTotal, safeLimit);\n const page = clampPage(state.pageNumber, pages);\n\n return {\n error: state.error,\n isLoading: state.isLoading,\n isSearchPending: state.isSearchPending,\n pageCount: pages,\n pageNumber: page,\n pageSize: safeLimit,\n totalItems: safeTotal,\n };\n};\n\n/**\n * Computes the 1-based display range of items on the current page.\n * Returns `{ start: 0, end: 0 }` when there are no items.\n *\n * @example\n * ```ts\n * const { start, end } = itemRange(source.meta);\n * // \"Showing 11–20 of 50\"\n * console.log(`Showing ${start}–${end} of ${source.meta.totalItems}`);\n * ```\n */\nexport function itemRange(meta: Readonly<{ pageNumber: number; pageSize: number; totalItems: number }>): {\n end: number;\n start: number;\n} {\n if (meta.totalItems === 0) return { end: 0, start: 0 };\n\n const start = (meta.pageNumber - 1) * meta.pageSize + 1;\n\n return {\n end: Math.min(meta.pageNumber * meta.pageSize, meta.totalItems),\n start,\n };\n}\n","import type { LocalSource, LocalSourceConfig, LocalSourceQuery, SearchOptions, SourceQuery } from './types';\n\nimport { extractError } from './_utils';\nimport { createSourceCore } from './core';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\nconst DEFAULT_LIMIT = 20;\nconst DEFAULT_DEBOUNCE_MS = 300;\n\nfunction defaultSearch<T>(items: readonly T[], query: string): readonly T[] {\n const q = query.toLowerCase();\n\n return items.filter((item) => JSON.stringify(item).toLowerCase().includes(q));\n}\n\n/**\n * Creates an in-memory paginated source with optional async filter/sort pipelines.\n *\n * When neither `filterAsync` nor `sortAsync` are configured, all operations\n * complete synchronously — there is no loading state and `ready()` resolves immediately.\n * Provide async ops to offload heavy computation (e.g. to a Web Worker).\n *\n * @example\n * ```ts\n * // Synchronous (common case)\n * const source = createLocalSource([1, 2, 3, 4, 5], { limit: 2 });\n * await source.goTo(2);\n * console.log(source.current); // [3, 4]\n *\n * // Async pipeline (Web Worker offloading)\n * const source = createLocalSource(users, {\n * filterAsync: (items, signal) => myWorker.filter(items, signal),\n * sortAsync: (items, signal) => myWorker.sort(items, signal),\n * });\n * ```\n */\nexport function createLocalSource<T>(data: readonly T[], cfg: LocalSourceConfig<T> = {}): LocalSource<T> {\n const core = createSourceCore();\n const isAsync = cfg.filterAsync !== undefined || cfg.sortAsync !== undefined;\n const debounceMs = cfg.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n\n let allData: readonly T[] = cfg.initialData ?? data;\n let search = '';\n let filter = cfg.filter;\n let sort = cfg.sort;\n let limit = cfg.limit ?? DEFAULT_LIMIT;\n let page = 1;\n let processed: readonly T[] = [];\n let isLoading = false;\n let isSearchPending = false;\n let error: SourcererError | null = null;\n let asyncController: AbortController | null = null;\n let pendingSearch: PendingSearch | null = null;\n\n const commit = () => core.notify();\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Sync pipeline (used when no async ops) ──────────────────────────────────\n const runSyncPipeline = (): readonly T[] => {\n let result: readonly T[] = allData;\n\n if (search) result = (cfg.searchFn ?? defaultSearch)(result, search);\n\n if (filter) result = result.filter(filter);\n\n if (sort) result = [...result].sort(sort);\n\n return result;\n };\n\n const recompute = () => {\n processed = runSyncPipeline();\n\n const pages = pageCount(processed.length, limit);\n\n page = clampPage(page, pages);\n };\n\n // ── Async pipeline ──────────────────────────────────────────────────────────\n const runAsyncPipeline = async (): Promise<void> => {\n asyncController?.abort();\n\n const controller = new AbortController();\n\n asyncController = controller;\n isLoading = true;\n error = null;\n commit();\n\n try {\n let result: readonly T[] = allData;\n\n if (search) result = (cfg.searchFn ?? defaultSearch)(result, search);\n\n if (filter) result = result.filter(filter);\n\n if (cfg.filterAsync && !controller.signal.aborted) {\n result = await cfg.filterAsync(result, controller.signal);\n }\n\n if (sort) result = [...result].sort(sort);\n\n if (cfg.sortAsync && !controller.signal.aborted) {\n result = await cfg.sortAsync(result, controller.signal);\n }\n\n if (controller.signal.aborted) return;\n\n processed = result;\n isLoading = false;\n isSearchPending = false;\n\n const pages = pageCount(processed.length, limit);\n\n page = clampPage(page, pages);\n } catch (reason: unknown) {\n if (!controller.signal.aborted) {\n isLoading = false;\n isSearchPending = false;\n error = new SourcererError(extractError(reason), { cause: reason });\n }\n } finally {\n if (asyncController === controller) asyncController = null;\n }\n\n commit();\n resolvePendingSearch();\n };\n\n // Internal flush — not part of public API\n const flushInternal = (): Promise<void> => {\n if (isAsync) return runAsyncPipeline();\n\n isSearchPending = false;\n recompute();\n commit();\n resolvePendingSearch();\n\n return Promise.resolve();\n };\n\n // ── Initial state ───────────────────────────────────────────────────────────\n if (isAsync) {\n // Async: seed with a sync pass so `current` isn't empty while the async pass runs.\n processed = runSyncPipeline();\n void runAsyncPipeline();\n } else {\n recompute();\n }\n\n const source: LocalSource<T> = {\n get current() {\n const start = (page - 1) * limit;\n\n return processed.slice(start, start + limit);\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n core.cancelTimer();\n asyncController?.abort();\n asyncController = null;\n resolvePendingSearch();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n goTo(n) {\n const pages = pageCount(processed.length, limit);\n const clamped = clampPage(n, pages);\n\n if (clamped === page) return Promise.resolve();\n\n page = clamped;\n commit();\n\n return Promise.resolve();\n },\n\n goToLast() {\n return source.goTo(pageCount(processed.length, limit));\n },\n\n get meta() {\n return createMeta({\n error,\n isLoading,\n isSearchPending,\n pageNumber: page,\n pageSize: limit,\n totalItems: processed.length,\n });\n },\n\n next() {\n return source.goTo(page + 1);\n },\n\n patch(changes: LocalSourceQuery<T>) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n if ('filter' in changes) {\n filter = changes.filter;\n changed = true;\n }\n\n if ('sort' in changes) {\n sort = changes.sort;\n changed = true;\n }\n\n // `|| core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (changes.page !== undefined) {\n const next = clampPage(changes.page, pageCount(processed.length, limit));\n\n if (next !== page) {\n page = next;\n changed = true;\n }\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic recompute — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant recompute afterwards.\n core.cancelTimer();\n resolvePendingSearch();\n\n // Reset page only when non-page query fields changed without an explicit page\n if (\n (changes.limit !== undefined || 'filter' in changes || 'sort' in changes || 'search' in changes) &&\n changes.page === undefined\n ) {\n page = 1;\n }\n\n return flushInternal();\n },\n\n prev() {\n return source.goTo(page - 1);\n },\n\n get query(): SourceQuery {\n return {\n limit,\n page,\n ...(search && { search }),\n };\n },\n\n ready(timeout?: number) {\n return core.ready(() => !isLoading && !core.isScheduled, timeout);\n },\n\n reset() {\n core.cancelTimer();\n isSearchPending = false;\n search = '';\n filter = cfg.filter;\n sort = cfg.sort;\n page = 1;\n resolvePendingSearch();\n\n return flushInternal();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the recompute already happened.\n if (q === search && !core.isScheduled) return Promise.resolve();\n\n core.cancelTimer();\n isSearchPending = false;\n search = q;\n page = 1;\n resolvePendingSearch();\n\n return flushInternal();\n }\n\n if (q === search) return Promise.resolve();\n\n // Cancel any previous pending search promise\n resolvePendingSearch();\n\n search = q;\n page = 1;\n isSearchPending = true;\n commit();\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n core.schedule(() => {\n isSearchPending = false;\n void flushInternal();\n }, debounceMs);\n\n return promise;\n },\n\n setData(d) {\n allData = d;\n page = 1;\n\n return flushInternal();\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n","import type { MergedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Combines multiple reactive sources into a single read-only view by applying\n * a user-supplied `combine` function over all sources' `current` arrays.\n *\n * The merged source recalculates whenever any parent source changes. Useful for\n * offline/online hybrid UIs (local + remote merged), multi-dataset views, or\n * pagination across multiple backends.\n *\n * Call `dispose()` to unsubscribe from all parent sources.\n *\n * @example\n * ```ts\n * const local = createLocalSource(localCache, { limit: 50 });\n * const remote = createRemoteSource({ fetch: fetchUsers, limit: 50 });\n *\n * // Show local cache items first, then remote items (deduped by id).\n * const merged = mergeSource([local, remote], (all) => {\n * const seen = new Set<number>();\n * return all.flat().filter((u) => !seen.has(u.id) && seen.add(u.id));\n * });\n *\n * merged.subscribe(() => console.log(merged.current));\n * ```\n */\nexport function mergeSource<T>(\n sources: ReadonlyArray<{\n readonly current: readonly T[];\n subscribe(listener: () => void): () => void;\n }>,\n combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[],\n): MergedSource<T> {\n const core = createSourceCore();\n let cachedCurrent: readonly T[] = combine(sources.map((s) => s.current));\n\n const unsubscribeFns = sources.map((source) =>\n source.subscribe(() => {\n cachedCurrent = combine(sources.map((s) => s.current));\n core.notify();\n }),\n );\n\n const source: MergedSource<T> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n for (const unsub of unsubscribeFns) {\n unsub();\n }\n\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n","import type { RemoteConfig, RemoteSource, RemoteSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\n/** Creates a remote page-based source that fetches data from a network endpoint. */\nexport function createRemoteSource<T, TFilter = unknown, TSort = unknown>(\n cfg: RemoteConfig<T, TFilter, TSort>,\n): RemoteSource<T, TFilter, TSort> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Config defaults ─────────────────────────────────────────────────────────\n const staleTimeMs = cfg.staleTime ?? 0;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let page = 1;\n let search = '';\n let filter: TFilter | undefined = cfg.filter;\n let sort: TSort | undefined = cfg.sort;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let lastFetchTime = 0;\n let lastFetchKey = '';\n let pendingSearch: PendingSearch | null = null;\n\n // Optimistic update state.\n let optimistic: { active: boolean; prevItems: readonly T[]; prevTotal: number } | null = null;\n\n // ── Snapshot pre-population ─────────────────────────────────────────────────\n if (cfg.snapshot) {\n items = cfg.snapshot.items;\n total = cfg.snapshot.total;\n\n if (cfg.snapshot.page !== undefined) page = cfg.snapshot.page;\n\n if (cfg.snapshot.search) search = cfg.snapshot.search;\n\n lastFetchTime = Date.now();\n }\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta = createMeta({\n error: null,\n isLoading: false,\n isSearchPending: false,\n pageNumber: 1,\n pageSize: limit,\n totalItems: 0,\n });\n\n const refreshMeta = () => {\n const pages = pageCount(total, limit);\n const safePage = clampPage(page, pages);\n\n // items is already the current page from the server — no local slicing needed.\n cachedCurrent = items;\n cachedMeta = createMeta({\n error,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageNumber: safePage,\n pageSize: limit,\n totalItems: total,\n });\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<RemoteSourceQuery<TFilter, TSort>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n // Initialise cache before first fetch.\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n\n // ── Assign result ───────────────────────────────────────────────────────────\n const assign = (result: Readonly<{ items: readonly T[]; total: number }>) => {\n if (optimistic?.active) {\n optimistic.active = false;\n optimistic = null;\n }\n\n items = result.items;\n total = result.total;\n page = clampPage(page, pageCount(total, limit));\n };\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): RemoteSourceQuery<TFilter, TSort> => ({\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n });\n\n const fetchQuery = (q: RemoteSourceQuery<TFilter, TSort>): Promise<void> => {\n const key = keyOf(q);\n\n if (staleTimeMs > 0 && key === lastFetchKey && total > 0 && Date.now() - lastFetchTime < staleTimeMs) {\n return Promise.resolve();\n }\n\n error = null;\n\n return base.fetch(\n q,\n async (q: RemoteSourceQuery<TFilter, TSort>, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n assign(result);\n error = null;\n lastFetchKey = key;\n lastFetchTime = Date.now();\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n if (optimistic?.active) {\n optimistic.active = false;\n items = optimistic.prevItems;\n total = optimistic.prevTotal;\n optimistic = null;\n } else {\n items = [];\n total = 0;\n }\n\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'remote', limit: q.limit, page: q.page, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n if (base.pendingCount() === 0) resolvePendingSearch();\n },\n );\n };\n\n const doUpdate = () => fetchQuery(toRemoteQuery());\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doUpdate();\n\n base.startRefreshInterval(() => void doUpdate());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n resolvePendingSearch();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n goTo(target) {\n const clamped = total > 0 ? clampPage(target, pageCount(total, limit)) : Math.max(1, Math.trunc(target));\n\n if (clamped === page) return Promise.resolve();\n\n page = clamped;\n\n return doUpdate();\n },\n\n goToLast() {\n const last = pageCount(total, limit);\n\n if (page === last) return Promise.resolve();\n\n page = last;\n\n return doUpdate();\n },\n\n get meta() {\n return cachedMeta;\n },\n\n next() {\n const pages = pageCount(total, limit);\n\n if (page >= pages) return Promise.resolve();\n\n page++;\n\n return doUpdate();\n },\n\n optimisticUpdate(mutator, options) {\n if (optimistic?.active) {\n throw new SourcererError(\n 'An optimistic update is already active. Rollback the previous update before starting another.',\n );\n }\n\n const prevItems = items;\n const prevTotal = total;\n\n optimistic = { active: true, prevItems, prevTotal };\n\n try {\n items = mutator(items);\n } catch (e: unknown) {\n optimistic = null;\n throw e;\n }\n\n total = options?.total ?? total;\n notifyListeners();\n\n return () => {\n if (optimistic?.active) {\n optimistic.active = false;\n items = prevItems;\n total = prevTotal;\n optimistic = null;\n notifyListeners();\n }\n };\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n if ('filter' in changes) {\n filter = changes.filter;\n changed = true;\n }\n\n if ('sort' in changes) {\n sort = changes.sort;\n changed = true;\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (changes.page !== undefined) {\n const next =\n total > 0 ? clampPage(changes.page, pageCount(total, limit)) : Math.max(1, Math.trunc(changes.page));\n\n if (next !== page) {\n page = next;\n changed = true;\n }\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n base.core.cancelTimer();\n resolvePendingSearch();\n\n // Reset to page 1 when non-page query fields changed without explicit page\n if (\n (changes.limit !== undefined || 'filter' in changes || 'sort' in changes || 'search' in changes) &&\n changes.page === undefined\n ) {\n page = 1;\n }\n\n return doUpdate();\n },\n\n prev() {\n if (page <= 1) return Promise.resolve();\n\n page--;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n base.core.cancelTimer();\n resolvePendingSearch();\n search = '';\n page = 1;\n filter = cfg.filter;\n sort = cfg.sort;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n base.core.cancelTimer();\n resolvePendingSearch();\n search = q;\n page = 1;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n resolvePendingSearch();\n\n search = q;\n page = 1;\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n base.core.schedule(() => void doUpdate(), debounceMs);\n notifyListeners();\n\n return promise;\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n resolvePendingSearch();\n base.dispose();\n },\n };\n}\n","import type { RemoteConfig, RemoteSource, SourceSnapshot } from './types';\n\nimport { createRemoteSource } from './remoteSource';\n\ntype PrefetchConfig<T, TFilter, TSort> = Omit<RemoteConfig<T, TFilter, TSort>, 'autoFetch' | 'refreshInterval'>;\n\nconst buildSnapshot = <T, TFilter, TSort>(source: RemoteSource<T, TFilter, TSort>): SourceSnapshot<T> => {\n const q = source.query;\n\n return {\n items: source.current,\n ...(q.search && { search: q.search }),\n page: source.meta.pageNumber,\n total: source.meta.totalItems,\n };\n};\n\n/**\n * Fetches a single page of data and returns a serialisable `SourceSnapshot`.\n * The underlying source is created, fetched, then immediately disposed.\n * Use for SSR / static-generation where you only need the data, not a live source.\n *\n * @example\n * ```ts\n * const snapshot = await prefetchSource({ fetch: fetchUsers, limit: 20 });\n * ```\n */\nexport async function prefetchSource<T, TFilter = unknown, TSort = unknown>(\n cfg: PrefetchConfig<T, TFilter, TSort>,\n): Promise<SourceSnapshot<T>> {\n const source = createRemoteSource<T, TFilter, TSort>({ ...cfg, autoFetch: false });\n\n try {\n await source.refresh();\n\n if (source.meta.error) throw source.meta.error;\n\n return buildSnapshot(source);\n } finally {\n source.dispose();\n }\n}\n\n/**\n * Fetches a single page of data and returns both a serialisable `SourceSnapshot` and the\n * still-live `RemoteSource`. Useful when you need the snapshot for SSR serialisation\n * **and** the live source for subsequent client-side updates without a double-fetch.\n *\n * **The caller is responsible for disposing the returned source.**\n *\n * @example\n * ```ts\n * const { snapshot, source } = await prefetchSourceAndKeep({ fetch: fetchUsers, limit: 20 });\n * // hydrate HTML with snapshot, pass source to client\n * ```\n */\nexport async function prefetchSourceAndKeep<T, TFilter = unknown, TSort = unknown>(\n cfg: PrefetchConfig<T, TFilter, TSort>,\n): Promise<{ snapshot: SourceSnapshot<T>; source: RemoteSource<T, TFilter, TSort> }> {\n const source = createRemoteSource<T, TFilter, TSort>({ ...cfg, autoFetch: false });\n\n await source.refresh();\n\n if (source.meta.error) {\n source.dispose();\n throw source.meta.error;\n }\n\n return { snapshot: buildSnapshot(source), source };\n}\n","import type { Predicate, Sorter } from './types';\n\ntype Direction = 'asc' | 'desc';\n\ntype PrimitiveComparable = number | string | Date;\n\nexport const filterContains = <T>(\n getValue: (item: T) => string | null | undefined,\n query: string,\n caseSensitive = false,\n): Predicate<T> => {\n const needle = caseSensitive ? query : query.toLowerCase();\n\n return (item) => {\n const value = getValue(item) ?? '';\n const haystack = caseSensitive ? value : value.toLowerCase();\n\n return haystack.includes(needle);\n };\n};\n\nexport const filterEquals = <T, V>(getValue: (item: T) => V, expected: V): Predicate<T> => {\n return (item) => Object.is(getValue(item), expected);\n};\n\nexport const filterRange = <T>(\n getValue: (item: T) => Date | number,\n bounds: Readonly<{ max?: Date | number; min?: Date | number }>,\n): Predicate<T> => {\n const toNum = (v: Date | number): number => (v instanceof Date ? v.getTime() : v);\n\n const min = bounds.min !== undefined ? toNum(bounds.min) : undefined;\n const max = bounds.max !== undefined ? toNum(bounds.max) : undefined;\n\n return (item) => {\n const value = toNum(getValue(item));\n\n if (min !== undefined && value < min) return false;\n\n if (max !== undefined && value > max) return false;\n\n return true;\n };\n};\n\nexport const sortBy = <T, V extends PrimitiveComparable>(\n getValue: (item: T) => V,\n direction: Direction = 'asc',\n): Sorter<T> => {\n const sign = direction === 'asc' ? 1 : -1;\n\n return (a, b) => {\n const left = getValue(a);\n const right = getValue(b);\n\n if (left instanceof Date && right instanceof Date) {\n return (left.getTime() - right.getTime()) * sign;\n }\n\n if (left < right) return -1 * sign;\n\n if (left > right) return 1 * sign;\n\n return 0;\n };\n};\n","import type { SourcererError, SourceState } from './types';\n\n/**\n * Derives a `SourceState<T>` discriminated union from a reactive source.\n * Works with any object that exposes `current`, `meta.isLoading`, `meta.error`, and optionally `meta.isSearchPending`.\n * Returns `'loading'` when `isLoading` or `isSearchPending` is true, so callers see a spinner during debounce windows.\n *\n * @example\n * ```ts\n * const state = sourceState(source);\n * if (state.status === 'loading') return <Spinner />;\n * if (state.status === 'error') return <p>{state.error.message}</p>;\n * return <List items={state.items} />;\n * ```\n */\nexport function sourceState<T>(source: {\n readonly current: readonly T[];\n readonly meta: {\n readonly error: SourcererError | null;\n readonly isLoading: boolean;\n readonly isSearchPending?: boolean;\n };\n}): SourceState<T> {\n if (source.meta.isLoading || source.meta.isSearchPending) return { status: 'loading' };\n\n if (source.meta.error) return { error: source.meta.error, status: 'error' };\n\n return { items: source.current, status: 'success' };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var Sourcerer=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function n(e,t){return e.patch(t)}var r=class e extends Error{#e;constructor(e,t={}){super(e,t.cause===void 0?void 0:{cause:t.cause}),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype),this.#e=t}get attempt(){return this.#e.attempt??0}get context(){return this.#e.context}static is(t){return t instanceof e}},i=class extends r{timeoutMs;constructor(e){super(`Source.ready() timed out after ${e}ms`),this.timeoutMs=e}},a=class extends r{constructor(){super(`Source disposed while waiting for ready()`)}},o=e=>{let t={...e.search&&{search:e.search},limit:String(e.limit),page:String(e.page)},n=e;return n.filter!==void 0&&(t.filter=JSON.stringify(n.filter)),n.sort!==void 0&&(t.sort=JSON.stringify(n.sort)),t},s=(e,t={})=>{let n=e instanceof URLSearchParams?Object.fromEntries(e.entries()):e,{defaultLimit:i=20,strict:a=!1}=t,o=(e,t)=>{if(e===void 0)return t;let n=Array.isArray(e)?e[0]:e,r=Number(n);return Number.isInteger(r)&&r>0?r:t},s=(e,t)=>{if(t===void 0)return;let n=Array.isArray(t)?t[0]:t;try{return JSON.parse(n)}catch{if(a)throw new r(`Invalid query param "${e}": ${n}`);return}},c=n.limit,l=n.page,u=n.search,d=n.filter,f=n.sort,p=d===void 0?void 0:s(`filter`,d),m=f===void 0?void 0:s(`sort`,f);return{...p!==void 0&&{filter:p},...m!==void 0&&{sort:m},...u!==void 0&&{search:Array.isArray(u)?u[0]:u},limit:o(c,i),page:o(l,1)}},c=t.hash,l=e=>e instanceof Error?e.message:typeof e==`string`&&e.length>0?e:`Request failed`,u=e=>(0,t.backoff)(e);function d(e){let t=new Set,n=new Set,r=new AbortController,o,s=!1;return{cancelTimer(){o&&=(clearTimeout(o),void 0)},get disposalSignal(){return r.signal},dispose(){o&&=(clearTimeout(o),void 0);for(let e of n)e.timeoutId!==void 0&&clearTimeout(e.timeoutId),e.reject(new a);n.clear(),t.clear(),s=!0,r.abort()},flush(e){return o?(clearTimeout(o),o=void 0,e()):Promise.resolve()},get isDisposed(){return s},get isScheduled(){return o!==void 0},notify(r){if(!s){(r??e?.onBeforeNotify)?.();for(let e of t)e();for(let e of n)e.check()}},ready(e,t){return s?Promise.reject(new a):e()?Promise.resolve():new Promise((r,a)=>{let o={check:()=>{e()&&(o.timeoutId!==void 0&&clearTimeout(o.timeoutId),n.delete(o),r())},reject:a};t!==void 0&&(o.timeoutId=setTimeout(()=>{n.delete(o),a(new i(t))},t)),n.add(o)})},schedule(e,t){s||(o&&clearTimeout(o),o=setTimeout(()=>{o=void 0,e()},t))},subscribe(e){return s?()=>{}:(t.add(e),()=>t.delete(e))}}}function f(e){let t=new Map,n=``,r=0;return{dispose(){for(let e of t.values())e.controller.abort();t.clear(),r=0},get pendingCount(){return r},async run(i,a,o){let s=e(i);n=s;for(let[e,n]of t)e!==s&&(n.controller.abort(),t.delete(e));let c=t.get(s);if(c){r++,o();try{await c.promise}finally{r--,o()}return}let l=new AbortController;r++,o();let u=a(i,l.signal,()=>s===n).finally(()=>{t.delete(s),r--,o()});t.set(s,{controller:l,promise:u}),await u}}}function p(e,t=c,n){let r=d({onBeforeNotify:n}),i=f(t),a=e.debounceMs??300,o=e.retry?.attempts??0,s=e.retry?.delay??u,l=e.autoFetch!==!1,p,m=()=>{r.isDisposed||(p!==void 0&&(clearInterval(p),p=void 0),i.dispose(),r.dispose())};return{autoFetch:l,core:r,debounceMs:a,get disposalSignal(){return r.disposalSignal},dispose:m,get disposed(){return r.isDisposed},fetch(e,t,n){return i.run(e,t,n)},pendingCount:()=>i.pendingCount,ready(e){return r.ready(()=>i.pendingCount===0&&!r.isScheduled,e)},retryAttempts:o,retryDelay:s,startRefreshInterval(t){e.refreshInterval!==void 0&&e.refreshInterval>0&&(p=setInterval(()=>{r.isDisposed||t()},e.refreshInterval))},[Symbol.dispose]:m}}function m(e){let n=e.queryKey??c,i=Math.max(1,Math.trunc(e.limit??20)),a=``,o,s,u,d,f=[],m=0,h=null,g=null,_=[],v={error:null,hasNextPage:!1,hasPrevPage:!1,isLoading:!1,isSearchPending:!1,pageSize:i,totalItems:0},y=()=>{_=f,v={error:h,hasNextPage:u!==void 0,hasPrevPage:d!==void 0,isLoading:b.pendingCount()>0,isSearchPending:b.core.isScheduled,pageSize:i,totalItems:m}},b=p(e,n,y),{autoFetch:x,debounceMs:S,retryAttempts:C,retryDelay:w}=b;y();let T=()=>{b.disposed||b.core.notify()},E=()=>{g&&=(g.resolve(),null)},D=()=>({...o!==void 0&&{after:o},...s!==void 0&&{before:s},...a&&{search:a},limit:i}),O=n=>(h=null,b.fetch(n,async(n,i,a)=>{let o=Date.now();try{let r=await(0,t.retry)(t=>e.fetch(n,t),{delay:w,signal:i,times:C+1});a()&&(f=r.items,m=r.total??r.items.length,u=r.nextCursor,d=r.prevCursor,h=null,e.onFetch?.({durationMs:Date.now()-o,query:n,status:`success`}))}catch(t){if(i.aborted)return;a()&&(f=[],m=0,u=void 0,d=void 0,h=new r(l(t),{cause:t,context:{kind:`cursor`,limit:n.limit,...n.search&&{search:n.search}}}),e.onFetch?.({durationMs:Date.now()-o,error:h,query:n,status:`error`}))}},()=>{T(),b.pendingCount()===0&&E()})),k=()=>O(D());return x&&k(),b.startRefreshInterval(()=>void k()),{get current(){return _},get disposalSignal(){return b.disposalSignal},dispose:()=>{E(),b.dispose()},get disposed(){return b.disposed},get meta(){return v},next(){return u?(o=u,s=void 0,k()):Promise.resolve()},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==i&&(i=n,o=void 0,s=void 0,t=!0)}return`search`in e&&(e.search!==a||b.core.isScheduled)&&(a=e.search??``,o=void 0,s=void 0,t=!0),t?(b.core.cancelTimer(),E(),k()):Promise.resolve()},prev(){return d?(s=d,o=void 0,k()):Promise.resolve()},get query(){return{...o!==void 0&&{after:o},...s!==void 0&&{before:s},...a&&{search:a},limit:i}},ready(e){return b.ready(e)},refresh(){return k()},reset(){return b.core.cancelTimer(),E(),a=``,o=void 0,s=void 0,k()},search(e,t){if(t?.immediate)return e===a&&!b.core.isScheduled?Promise.resolve():(b.core.cancelTimer(),E(),a=e,o=void 0,s=void 0,k());if(e===a)return Promise.resolve();E(),a=e,o=void 0,s=void 0;let n,r=new Promise(e=>{n=e});return g={promise:r,resolve:n},b.core.schedule(()=>void k(),S),T(),r},subscribe:e=>b.core.subscribe(e),[Symbol.dispose]:()=>{E(),b.dispose()}}}function h(e,t){let n=d(),r=[],i=()=>{r=t(e.current)};i();let a=e.subscribe(()=>{i(),n.notify()});`disposalSignal`in e&&e.disposalSignal instanceof AbortSignal&&e.disposalSignal.addEventListener(`abort`,()=>n.dispose(),{once:!0});let o={get current(){return r},get disposalSignal(){return n.disposalSignal},dispose(){n.isDisposed||(a(),n.dispose())},get disposed(){return n.isDisposed},get meta(){return e.meta},subscribe(e){return n.subscribe(e)},[Symbol.dispose](){o.dispose()}};return o}function g(e){let n=e.queryKey??c,i=Math.max(1,Math.trunc(e.limit??20)),a=``,o=1,s=0,u=[],d=0,f=null,m=!1,h=null,g=[],_={error:null,hasMore:!1,isLoading:!1,isLoadingMore:!1,isSearchPending:!1,loadedPages:0,pageSize:i,totalItems:0},v=()=>{g=u,_={error:f,hasMore:u.length<d,isLoading:y.pendingCount()>0&&!m,isLoadingMore:m,isSearchPending:y.core.isScheduled,loadedPages:s,pageSize:i,totalItems:d}},y=p(e,n,v),{autoFetch:b,debounceMs:x,retryAttempts:S,retryDelay:C}=y;v();let w=()=>{y.disposed||y.core.notify()},T=()=>{h&&=(h.resolve(),null)},E=(n,c)=>{let p={...a&&{search:a},limit:i,page:n};return f=null,y.fetch(p,async(i,a,p)=>{let h=Date.now();try{let r=await(0,t.retry)(t=>e.fetch(i,t),{delay:C,signal:a,times:S+1});p()&&(u=c?[...u,...r.items]:r.items,d=r.total,o=n,s=c?s+1:1,f=null,e.onFetch?.({durationMs:Date.now()-h,query:i,status:`success`}))}catch(t){if(a.aborted)return;p()&&(c||(u=[]),d=c?d:0,f=new r(l(t),{cause:t,context:{kind:`infinite`,limit:i.limit,page:i.page,...i.search&&{search:i.search}}}),e.onFetch?.({durationMs:Date.now()-h,error:f,query:i,status:`error`}))}finally{m=!1}},()=>{w(),y.pendingCount()===0&&T()})},D=()=>E(1,!1);return b&&D(),y.startRefreshInterval(()=>void D()),{get current(){return g},get disposalSignal(){return y.disposalSignal},dispose:()=>{T(),y.dispose()},get disposed(){return y.disposed},loadMore(){return!_.hasMore||y.pendingCount()>0||m?Promise.resolve():(m=!0,v(),E(o+1,!0))},get meta(){return _},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==i&&(i=n,t=!0)}return`search`in e&&(e.search!==a||y.core.isScheduled)&&(a=e.search??``,t=!0),t?(y.core.cancelTimer(),T(),u=[],d=0,o=1,s=0,D()):Promise.resolve()},get query(){return{...a&&{search:a},limit:i,page:o}},ready(e){return y.ready(e)},reset(){return y.core.cancelTimer(),T(),o=1,s=0,u=[],d=0,a=``,D()},search(e,t){if(t?.immediate)return e===a&&!y.core.isScheduled?Promise.resolve():(y.core.cancelTimer(),T(),a=e,u=[],d=0,s=0,o=1,D());if(e===a)return Promise.resolve();T(),a=e,u=[],d=0,s=0,o=1;let n,r=new Promise(e=>{n=e});return h={promise:r,resolve:n},y.core.schedule(()=>void D(),x),w(),r},subscribe:e=>y.core.subscribe(e),[Symbol.dispose]:()=>{T(),y.dispose()}}}var _=(e,t)=>{let n=Math.trunc(e);return Number.isFinite(n)?Math.max(t,n):t},v=(e,t)=>{let n=_(e,0),r=_(t,1);return Math.max(1,Math.ceil(n/r))},y=(e,t)=>Math.max(1,Math.min(_(e,1),Math.max(1,t))),b=e=>{let t=_(e.pageSize,1),n=_(e.totalItems,0),r=v(n,t),i=y(e.pageNumber,r);return{error:e.error,isLoading:e.isLoading,isSearchPending:e.isSearchPending,pageCount:r,pageNumber:i,pageSize:t,totalItems:n}};function x(e){if(e.totalItems===0)return{end:0,start:0};let t=(e.pageNumber-1)*e.pageSize+1;return{end:Math.min(e.pageNumber*e.pageSize,e.totalItems),start:t}}var S=20,C=300;function w(e,t){let n=t.toLowerCase();return e.filter(e=>JSON.stringify(e).toLowerCase().includes(n))}function T(e,t={}){let n=d(),i=t.filterAsync!==void 0||t.sortAsync!==void 0,a=t.debounceMs??C,o=t.initialData??e,s=``,c=t.filter,u=t.sort,f=t.limit??S,p=1,m=[],h=!1,g=!1,_=null,x=null,T=null,E=()=>n.notify(),D=()=>{T&&=(T.resolve(),null)},O=()=>{let e=o;return s&&(e=(t.searchFn??w)(e,s)),c&&(e=e.filter(c)),u&&(e=[...e].sort(u)),e},k=()=>{m=O();let e=v(m.length,f);p=y(p,e)},A=async()=>{x?.abort();let e=new AbortController;x=e,h=!0,_=null,E();try{let n=o;if(s&&(n=(t.searchFn??w)(n,s)),c&&(n=n.filter(c)),t.filterAsync&&!e.signal.aborted&&(n=await t.filterAsync(n,e.signal)),u&&(n=[...n].sort(u)),t.sortAsync&&!e.signal.aborted&&(n=await t.sortAsync(n,e.signal)),e.signal.aborted)return;m=n,h=!1,g=!1;let r=v(m.length,f);p=y(p,r)}catch(t){e.signal.aborted||(h=!1,g=!1,_=new r(l(t),{cause:t}))}finally{x===e&&(x=null)}E(),D()},j=()=>i?A():(g=!1,k(),E(),D(),Promise.resolve());i?(m=O(),A()):k();let M={get current(){let e=(p-1)*f;return m.slice(e,e+f)},get disposalSignal(){return n.disposalSignal},dispose(){n.isDisposed||(n.cancelTimer(),x?.abort(),x=null,D(),n.dispose())},get disposed(){return n.isDisposed},goTo(e){let t=y(e,v(m.length,f));return t===p?Promise.resolve():(p=t,E(),Promise.resolve())},goToLast(){return M.goTo(v(m.length,f))},get meta(){return b({error:_,isLoading:h,isSearchPending:g,pageNumber:p,pageSize:f,totalItems:m.length})},next(){return M.goTo(p+1)},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==f&&(f=n,t=!0)}if(`filter`in e&&(c=e.filter,t=!0),`sort`in e&&(u=e.sort,t=!0),`search`in e&&(e.search!==s||n.isScheduled)&&(s=e.search??``,t=!0),e.page!==void 0){let n=y(e.page,v(m.length,f));n!==p&&(p=n,t=!0)}return t?(n.cancelTimer(),D(),(e.limit!==void 0||`filter`in e||`sort`in e||`search`in e)&&e.page===void 0&&(p=1),j()):Promise.resolve()},prev(){return M.goTo(p-1)},get query(){return{limit:f,page:p,...s&&{search:s}}},ready(e){return n.ready(()=>!h&&!n.isScheduled,e)},reset(){return n.cancelTimer(),g=!1,s=``,c=t.filter,u=t.sort,p=1,D(),j()},search(e,t){if(t?.immediate)return e===s&&!n.isScheduled?Promise.resolve():(n.cancelTimer(),g=!1,s=e,p=1,D(),j());if(e===s)return Promise.resolve();D(),s=e,p=1,g=!0,E();let r,i=new Promise(e=>{r=e});return T={promise:i,resolve:r},n.schedule(()=>{g=!1,j()},a),i},setData(e){return o=e,p=1,j()},subscribe(e){return n.subscribe(e)},[Symbol.dispose](){M.dispose()}};return M}function E(e,t){let n=d(),r=t(e.map(e=>e.current)),i=e.map(i=>i.subscribe(()=>{r=t(e.map(e=>e.current)),n.notify()})),a={get current(){return r},get disposalSignal(){return n.disposalSignal},dispose(){if(!n.isDisposed){for(let e of i)e();n.dispose()}},get disposed(){return n.isDisposed},subscribe(e){return n.subscribe(e)},[Symbol.dispose](){a.dispose()}};return a}function D(e){let n=e.queryKey??c,i=e.staleTime??0,a=Math.max(1,Math.trunc(e.limit??20)),o=1,s=``,u=e.filter,d=e.sort,f=[],m=0,h=null,g=0,_=``,x=null,S=null;e.snapshot&&(f=e.snapshot.items,m=e.snapshot.total,e.snapshot.page!==void 0&&(o=e.snapshot.page),e.snapshot.search&&(s=e.snapshot.search),g=Date.now());let C=[],w=b({error:null,isLoading:!1,isSearchPending:!1,pageNumber:1,pageSize:a,totalItems:0}),T=()=>{let e=v(m,a),t=y(o,e);C=f,w=b({error:h,isLoading:E.pendingCount()>0,isSearchPending:E.core.isScheduled,pageNumber:t,pageSize:a,totalItems:m})},E=p(e,n,T),{autoFetch:D,debounceMs:O,retryAttempts:k,retryDelay:A}=E;T();let j=()=>{E.disposed||E.core.notify()},M=e=>{S?.active&&(S.active=!1,S=null),f=e.items,m=e.total,o=y(o,v(m,a))},N=()=>{x&&=(x.resolve(),null)},P=()=>({...u!==void 0&&{filter:u},...d!==void 0&&{sort:d},...s&&{search:s},limit:a,page:o}),F=a=>{let o=n(a);return i>0&&o===_&&m>0&&Date.now()-g<i?Promise.resolve():(h=null,E.fetch(a,async(n,i,a)=>{let s=Date.now();try{let r=await(0,t.retry)(t=>e.fetch(n,t),{delay:A,signal:i,times:k+1});a()&&(M(r),h=null,_=o,g=Date.now(),e.onFetch?.({durationMs:Date.now()-s,query:n,status:`success`}))}catch(t){if(i.aborted)return;a()&&(S?.active?(S.active=!1,f=S.prevItems,m=S.prevTotal,S=null):(f=[],m=0),h=new r(l(t),{cause:t,context:{kind:`remote`,limit:n.limit,page:n.page,...n.search&&{search:n.search}}}),e.onFetch?.({durationMs:Date.now()-s,error:h,query:n,status:`error`}))}},()=>{j(),E.pendingCount()===0&&N()}))},I=()=>F(P());return D&&I(),E.startRefreshInterval(()=>void I()),{get current(){return C},get disposalSignal(){return E.disposalSignal},dispose:()=>{N(),E.dispose()},get disposed(){return E.disposed},goTo(e){let t=m>0?y(e,v(m,a)):Math.max(1,Math.trunc(e));return t===o?Promise.resolve():(o=t,I())},goToLast(){let e=v(m,a);return o===e?Promise.resolve():(o=e,I())},get meta(){return w},next(){let e=v(m,a);return o>=e?Promise.resolve():(o++,I())},optimisticUpdate(e,t){if(S?.active)throw new r(`An optimistic update is already active. Rollback the previous update before starting another.`);let n=f,i=m;S={active:!0,prevItems:n,prevTotal:i};try{f=e(f)}catch(e){throw S=null,e}return m=t?.total??m,j(),()=>{S?.active&&(S.active=!1,f=n,m=i,S=null,j())}},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==a&&(a=n,t=!0)}if(`filter`in e&&(u=e.filter,t=!0),`sort`in e&&(d=e.sort,t=!0),`search`in e&&(e.search!==s||E.core.isScheduled)&&(s=e.search??``,t=!0),e.page!==void 0){let n=m>0?y(e.page,v(m,a)):Math.max(1,Math.trunc(e.page));n!==o&&(o=n,t=!0)}return t?(E.core.cancelTimer(),N(),(e.limit!==void 0||`filter`in e||`sort`in e||`search`in e)&&e.page===void 0&&(o=1),I()):Promise.resolve()},prev(){return o<=1?Promise.resolve():(o--,I())},get query(){return{...u!==void 0&&{filter:u},...d!==void 0&&{sort:d},...s&&{search:s},limit:a,page:o}},ready(e){return E.ready(e)},refresh(){return I()},reset(){return E.core.cancelTimer(),N(),s=``,o=1,u=e.filter,d=e.sort,I()},search(e,t){if(t?.immediate)return e===s&&!E.core.isScheduled?Promise.resolve():(E.core.cancelTimer(),N(),s=e,o=1,I());if(e===s)return Promise.resolve();N(),s=e,o=1;let n,r=new Promise(e=>{n=e});return x={promise:r,resolve:n},E.core.schedule(()=>void I(),O),j(),r},subscribe:e=>E.core.subscribe(e),[Symbol.dispose]:()=>{N(),E.dispose()}}}var O=e=>{let t=e.query;return{items:e.current,...t.search&&{search:t.search},page:e.meta.pageNumber,total:e.meta.totalItems}};async function k(e){let t=D({...e,autoFetch:!1});try{if(await t.refresh(),t.meta.error)throw t.meta.error;return O(t)}finally{t.dispose()}}async function A(e){let t=D({...e,autoFetch:!1});if(await t.refresh(),t.meta.error)throw t.dispose(),t.meta.error;return{snapshot:O(t),source:t}}var j=(e,t,n=!1)=>{let r=n?t:t.toLowerCase();return t=>{let i=e(t)??``;return(n?i:i.toLowerCase()).includes(r)}},M=(e,t)=>n=>Object.is(e(n),t),N=(e,t)=>{let n=e=>e instanceof Date?e.getTime():e,r=t.min===void 0?void 0:n(t.min),i=t.max===void 0?void 0:n(t.max);return t=>{let a=n(e(t));return!(r!==void 0&&a<r||i!==void 0&&a>i)}},P=(e,t=`asc`)=>{let n=t===`asc`?1:-1;return(t,r)=>{let i=e(t),a=e(r);return i instanceof Date&&a instanceof Date?(i.getTime()-a.getTime())*n:i<a?-1*n:i>a?1*n:0}};function F(e){return e.meta.isLoading||e.meta.isSearchPending?{status:`loading`}:e.meta.error?{error:e.meta.error,status:`error`}:{items:e.current,status:`success`}}return e.SourceDisposedError=a,e.SourceTimeoutError=i,e.SourcererError=r,e.applyQuery=n,e.createCursorSource=m,e.createInfiniteSource=g,e.createLocalSource=T,e.createRemoteSource=D,e.decodeQuery=s,e.deriveSource=h,e.encodeQuery=o,e.filterContains=j,e.filterEquals=M,e.filterRange=N,e.itemRange=x,e.mergeSource=E,e.prefetchSource=k,e.prefetchSourceAndKeep=A,e.sortBy=P,e.sourceState=F,e})({},Arsenal);
2
+ //# sourceMappingURL=sourcerer.iife.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourcerer.iife.js","names":["#opts"],"sources":["../src/applyQuery.ts","../src/errors.ts","../src/codecs.ts","../src/_utils.ts","../src/core.ts","../src/fetchManager.ts","../src/asyncSource.ts","../src/cursorSource.ts","../src/derive.ts","../src/infiniteSource.ts","../src/pagination.ts","../src/localSource.ts","../src/merge.ts","../src/remoteSource.ts","../src/prefetch.ts","../src/presets.ts","../src/state.ts"],"sourcesContent":["import type { SourceQuery } from './types';\n\n/**\n * Generic query applicator — delegates to `source.patch(changes)`.\n * Use this with `decodeQuery()` output to restore URL state onto any source:\n *\n * @example\n * ```ts\n * const q = decodeQuery(new URL(location.href).searchParams, { defaultLimit: 20 });\n * await applyQuery(source, q);\n * ```\n *\n * ⚠️ `CursorSource` and `InfiniteSource` have no page-number concept (keyset/append navigation) —\n * their `patch()` only reads `limit`/`search` and silently ignores a `page` field. This compiles\n * (both accept `Partial<SourceQuery>`-shaped input) but a `page` value from `decodeQuery()` is a\n * no-op on those two source types.\n */\nexport function applyQuery<T extends { patch(changes: Partial<SourceQuery>): Promise<void> }>(\n source: T,\n changes: Partial<SourceQuery>,\n): Promise<void> {\n return source.patch(changes);\n}\n","/**\n * Structured context carried by `SourcererError`.\n * Discriminated on `kind` — narrow with `error.context?.kind` to access typed fields.\n *\n * @example\n * ```ts\n * if (source.meta.error?.context?.kind === 'remote') {\n * console.log('Failed page', source.meta.error.context.page);\n * }\n * ```\n */\nexport type SourcererErrorContext =\n | Readonly<{ kind: 'cursor'; limit: number; search?: string }>\n | Readonly<{ kind: 'infinite'; limit: number; page: number; search?: string }>\n | Readonly<{ kind: 'remote'; limit: number; page: number; search?: string }>;\n\n/**\n * Base class for all sourcerer errors. Catch with `instanceof SourcererError` to handle any\n * sourcerer-originated error regardless of specific subtype.\n *\n * Carries the original cause, structured context, and the attempt number.\n *\n * @example\n * ```ts\n * if (source.meta.error) {\n * console.error(source.meta.error.message, source.meta.error.context);\n * }\n * ```\n */\nexport class SourcererError extends Error {\n readonly #opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n };\n\n constructor(\n message: string,\n opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n } = {},\n ) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n this.#opts = opts;\n }\n\n get attempt(): number {\n return this.#opts.attempt ?? 0;\n }\n\n /** Context bag for the error — safe to log. */\n get context(): SourcererErrorContext | undefined {\n return this.#opts.context;\n }\n\n static is(err: unknown): err is SourcererError {\n return err instanceof SourcererError;\n }\n}\n\n/** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */\nexport class SourceTimeoutError extends SourcererError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Source.ready() timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown by `ready()` when the source is disposed while waiting. */\nexport class SourceDisposedError extends SourcererError {\n constructor() {\n super('Source disposed while waiting for ready()');\n }\n}\n","import type { QueryParams, QueryParamsInput, RemoteSourceQuery, SourceQuery } from './types';\n\nimport { SourcererError } from './errors';\n\n/**\n * Serialises a `SourceQuery` or `RemoteSourceQuery` into plain URL-safe string params.\n *\n * ⚠️ `filter` and `sort` are JSON-stringified without a try/catch. A circular object reference\n * throws a native `TypeError` (\"Converting circular structure to JSON\") straight out of this\n * function — ensure filter/sort values are plain serialisable objects, or wrap the call yourself.\n *\n * ⚠️ Round-trip fidelity: `page` and `limit` must be positive integers. `encodeQuery` will\n * serialise any number (including 0), but `decodeQuery` clamps non-positive values to 1/defaultLimit.\n *\n * `encodeQuery` + `decodeQuery` form a round-trip pair: filter/sort values are JSON-stringified\n * on encode and JSON-parsed on decode.\n */\nexport const encodeQuery = <TFilter = unknown, TSort = unknown>(\n query: RemoteSourceQuery<TFilter, TSort> | SourceQuery,\n): QueryParams => {\n const base: QueryParams = {\n ...(query.search && { search: query.search }),\n limit: String(query.limit),\n page: String(query.page),\n };\n\n const rq = query as RemoteSourceQuery<TFilter, TSort>;\n\n if (rq.filter !== undefined) {\n base['filter'] = JSON.stringify(rq.filter);\n }\n\n if (rq.sort !== undefined) {\n base['sort'] = JSON.stringify(rq.sort);\n }\n\n return base;\n};\n\nexport type DecodeQueryOptions = Readonly<{\n defaultLimit?: number;\n strict?: boolean;\n}>;\n\n/**\n * Parses URL query params into a `Partial<RemoteSourceQuery<unknown, unknown>>`.\n * Accepts either a plain `Record<string, string | string[] | undefined>` or a `URLSearchParams` instance.\n *\n * - `filter` and `sort` are JSON-parsed and typed as `unknown` — validate and narrow them\n * with a runtime schema (e.g. Zod) before passing to `applyQuery`.\n * - `search` is omitted from the result when the param is absent (rather than defaulting to `''`).\n * - `limit` and `page` are parsed as positive integers; invalid values fall back to defaults.\n */\nexport const decodeQuery = (\n params: QueryParamsInput | URLSearchParams,\n options: DecodeQueryOptions = {},\n): Partial<RemoteSourceQuery<unknown, unknown>> => {\n const raw: QueryParamsInput =\n params instanceof URLSearchParams ? (Object.fromEntries(params.entries()) as QueryParamsInput) : params;\n\n const { defaultLimit = 20, strict = false } = options;\n\n const parsePositiveInt = (value: string | string[] | undefined, fallback: number): number => {\n if (value === undefined) return fallback;\n\n const str = Array.isArray(value) ? value[0] : value;\n const n = Number(str);\n\n return Number.isInteger(n) && n > 0 ? n : fallback;\n };\n\n const parseJson = (key: string, value: string | string[] | undefined): unknown => {\n if (value === undefined) return undefined;\n\n const str = Array.isArray(value) ? value[0] : value;\n\n try {\n return JSON.parse(str) as unknown;\n } catch {\n if (strict) throw new SourcererError(`Invalid query param \"${key}\": ${str}`);\n\n return undefined;\n }\n };\n\n const rawLimit = raw['limit'];\n const rawPage = raw['page'];\n const rawSearch = raw['search'];\n const rawFilter = raw['filter'];\n const rawSort = raw['sort'];\n\n const filter = rawFilter !== undefined ? parseJson('filter', rawFilter) : undefined;\n const sort = rawSort !== undefined ? parseJson('sort', rawSort) : undefined;\n\n const result: Partial<RemoteSourceQuery<unknown, unknown>> = {\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(rawSearch !== undefined && { search: Array.isArray(rawSearch) ? rawSearch[0] : rawSearch }),\n limit: parsePositiveInt(rawLimit, defaultLimit),\n page: parsePositiveInt(rawPage, 1),\n };\n\n return result;\n};\n","import { backoff, hash, retry } from '@vielzeug/arsenal';\n\nexport { retry };\n\n// Internal stable-key helper — used by source factories, not exposed as public API.\nexport const defaultKeyOf = hash;\n\n/** Extracts a user-facing error message from a caught exception. */\nexport const extractError = (reason: unknown): string => {\n if (reason instanceof Error) return reason.message;\n\n if (typeof reason === 'string' && reason.length > 0) return reason;\n\n return 'Request failed';\n};\n\n/** Default exponential backoff: 1 s, 2 s, 4 s, … capped at 30 s. Receives a 0-indexed failure count. */\nexport const defaultRetryDelay = (attempt: number): number => backoff(attempt);\n","import { SourceDisposedError, SourceTimeoutError } from './errors';\n\n/**\n * Shared source infrastructure: listener management, debounce scheduling, and ready() polling.\n * All source factories compose this core rather than duplicating the same patterns.\n */\nexport type SourceCore = {\n /** Cancels any pending timer without invoking the callback. */\n cancelTimer(): void;\n\n /** `AbortSignal` that is aborted when `dispose()` is called. */\n readonly disposalSignal: AbortSignal;\n\n /**\n * Clears all listeners, cancels any pending timer, and marks the core as disposed.\n * Subsequent method calls on the core become no-ops.\n */\n dispose(): void;\n\n /**\n * If a timer is scheduled, cancels it and immediately invokes `fn`.\n * Returns a resolved Promise when no timer is pending.\n */\n flush(fn: () => Promise<void>): Promise<void>;\n\n /** Whether `dispose()` has been called. All methods become no-ops after disposal. */\n readonly isDisposed: boolean;\n\n /** Whether a debounce timer is currently scheduled (drives `meta.isSearchPending`). */\n readonly isScheduled: boolean;\n\n /**\n * Fire all registered listeners and wake any `ready()` waiters.\n * When provided, `onBefore` runs once before listeners are called — use it to\n * refresh cached meta/current before subscribers observe the new state.\n */\n notify(onBefore?: () => void): void;\n\n /**\n * Returns a Promise that resolves when `isIdle()` returns true.\n * Resolves synchronously if already idle.\n * Rejects with a `TimeoutError` after `timeoutMs` ms if still not idle.\n */\n ready(isIdle: () => boolean, timeoutMs?: number): Promise<void>;\n\n /**\n * Schedules a debounced callback, replacing any pending timer.\n * The callback is invoked after `delayMs` ms of inactivity.\n */\n schedule(fn: () => void, delayMs: number): void;\n\n /** Subscribe to change notifications. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n};\n\nexport function createSourceCore(opts?: { onBeforeNotify?: () => void }): SourceCore {\n const listeners = new Set<() => void>();\n const readyWaiters = new Set<{\n check: () => void;\n reject: (err: unknown) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n }>();\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let disposed = false;\n\n return {\n cancelTimer() {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n },\n\n get disposalSignal() {\n return controller.signal;\n },\n\n dispose() {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n\n for (const waiter of readyWaiters) {\n if (waiter.timeoutId !== undefined) clearTimeout(waiter.timeoutId);\n\n waiter.reject(new SourceDisposedError());\n }\n\n readyWaiters.clear();\n listeners.clear();\n disposed = true;\n controller.abort();\n },\n\n flush(fn) {\n if (!timer) return Promise.resolve();\n\n clearTimeout(timer);\n timer = undefined;\n\n return fn();\n },\n\n get isDisposed() {\n return disposed;\n },\n\n get isScheduled() {\n return timer !== undefined;\n },\n\n notify(onBefore?: () => void) {\n if (disposed) return;\n\n (onBefore ?? opts?.onBeforeNotify)?.();\n\n for (const listener of listeners) {\n listener();\n }\n\n for (const waiter of readyWaiters) {\n waiter.check();\n }\n },\n\n ready(isIdle, timeoutMs) {\n if (disposed) return Promise.reject(new SourceDisposedError());\n\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n const waiter: { check: () => void; reject: (err: unknown) => void; timeoutId?: ReturnType<typeof setTimeout> } =\n {\n check: () => {\n if (isIdle()) {\n if (waiter.timeoutId !== undefined) clearTimeout(waiter.timeoutId);\n\n readyWaiters.delete(waiter);\n resolve();\n }\n },\n reject,\n };\n\n if (timeoutMs !== undefined) {\n waiter.timeoutId = setTimeout(() => {\n readyWaiters.delete(waiter);\n reject(new SourceTimeoutError(timeoutMs));\n }, timeoutMs);\n }\n\n readyWaiters.add(waiter);\n });\n },\n\n schedule(fn, delayMs) {\n if (disposed) return;\n\n if (timer) clearTimeout(timer);\n\n timer = setTimeout(() => {\n timer = undefined;\n fn();\n }, delayMs);\n },\n\n subscribe(listener) {\n if (disposed) return () => {};\n\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n}\n","/**\n * Internal fetch deduplication and in-flight request manager.\n *\n * - Deduplicates identical concurrent queries: joiners await the existing in-flight promise.\n * - Aborts superseded in-flight requests when a new query key arrives.\n * - Tracks pending count so callers can reflect loading state.\n *\n * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,\n * cursorSource, and infiniteSource.\n */\n\ntype InFlightEntry = { controller: AbortController; promise: Promise<void> };\n\nexport type FetchManager<TQuery> = {\n /** Abort all in-flight requests and clear the registry. */\n dispose(): void;\n /** Number of currently in-flight requests (including joiners). */\n readonly pendingCount: number;\n /**\n * Run a fetch for query `q`.\n *\n * - If an identical key is already in-flight, joins as a waiter.\n * - If a different key is in-flight, aborts it and starts a new request.\n * - Calls `onPendingChange()` whenever `pendingCount` changes.\n * - Passes an `isLatest()` predicate to `execute` so it can skip stale responses.\n */\n run(\n q: TQuery,\n execute: (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>,\n onPendingChange: () => void,\n ): Promise<void>;\n};\n\nexport function createFetchManager<TQuery>(keyOf: (q: TQuery) => string): FetchManager<TQuery> {\n const inflight = new Map<string, InFlightEntry>();\n let latestKey = '';\n let _pendingCount = 0;\n\n return {\n dispose() {\n for (const entry of inflight.values()) {\n entry.controller.abort();\n }\n\n inflight.clear();\n _pendingCount = 0;\n },\n\n get pendingCount() {\n return _pendingCount;\n },\n\n async run(q, execute, onPendingChange) {\n const key = keyOf(q);\n\n latestKey = key;\n\n // Abort and remove all superseded in-flight requests.\n for (const [k, entry] of inflight) {\n if (k !== key) {\n entry.controller.abort();\n inflight.delete(k);\n }\n }\n\n // Join an identical in-flight request rather than issuing a duplicate.\n const existing = inflight.get(key);\n\n if (existing) {\n _pendingCount++;\n onPendingChange();\n\n try {\n await existing.promise;\n } finally {\n _pendingCount--;\n onPendingChange();\n }\n\n return;\n }\n\n // New request.\n const controller = new AbortController();\n\n _pendingCount++;\n onPendingChange();\n\n const promise = execute(q, controller.signal, () => key === latestKey).finally(() => {\n inflight.delete(key);\n _pendingCount--;\n onPendingChange();\n });\n\n inflight.set(key, { controller, promise });\n await promise;\n },\n };\n}\n","import type { SourceCore } from './core';\n\nimport { defaultKeyOf, defaultRetryDelay } from './_utils';\nimport { createSourceCore } from './core';\nimport { createFetchManager } from './fetchManager';\n\n/**\n * Configuration subset common to all async source types.\n * Extracted to eliminate duplication across remote, cursor, and infinite sources.\n */\nexport type AsyncSourceConfig<TQuery> = Readonly<{\n autoFetch?: boolean;\n debounceMs?: number;\n queryKey?: (q: TQuery) => string;\n refreshInterval?: number;\n retry?: { attempts?: number; delay?: (attempt: number) => number };\n}>;\n\nexport type FetchExecutor<TQuery> = (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>;\n\nexport type AsyncSourceInfra<TQuery> = {\n [Symbol.dispose](): void;\n /** `true` when `autoFetch !== false`. */\n readonly autoFetch: boolean;\n /** The underlying SourceCore — use directly for subscribe, schedule, cancelTimer, flush, isScheduled. */\n readonly core: SourceCore;\n /** Debounce delay in ms. */\n readonly debounceMs: number;\n /** `AbortSignal` aborted when `dispose()` is called. */\n readonly disposalSignal: AbortSignal;\n /** Dispose lifecycle: cancel refresh interval, dispose FM, dispose core. Idempotent. */\n dispose(): void;\n /** `true` after `dispose()`. */\n readonly disposed: boolean;\n /**\n * Run a deduplicated fetch for query `q`.\n * Calls `onCommit()` whenever pending count changes (for subscriber notification).\n */\n fetch(q: TQuery, execute: FetchExecutor<TQuery>, onCommit: () => void): Promise<void>;\n /** Number of currently in-flight requests. */\n pendingCount(): number;\n /** Resolves when idle (no in-flight requests, no pending debounce). */\n ready(timeout?: number): Promise<void>;\n /** Number of retry attempts after the first failure. */\n readonly retryAttempts: number;\n /** Returns the delay in ms before the nth retry. */\n readonly retryDelay: (attempt: number) => number;\n /**\n * Wire the refresh interval. Must be called once after construction, passing\n * the source-level `doUpdate` callback. Does nothing when `refreshInterval`\n * is unset or ≤ 0.\n */\n startRefreshInterval(doUpdate: () => void): void;\n};\n\n/**\n * Creates the shared infrastructure used by `createRemoteSource`, `createCursorSource`,\n * and `createInfiniteSource`. Eliminates duplication of lifecycle, config-defaults,\n * fetch-manager setup, dispose, ready, flush, and refresh-interval logic.\n *\n * Each source factory composes this and adds its own query state, meta shape, and\n * source-specific methods. Pass `onBeforeNotify` to run `refreshMeta()` before\n * subscribers observe each notification — this eliminates the need for a parallel\n * listeners Set in each source factory.\n */\nexport function createAsyncSource<TQuery>(\n cfg: AsyncSourceConfig<TQuery>,\n keyOf: (q: TQuery) => string = defaultKeyOf as (q: TQuery) => string,\n onBeforeNotify?: () => void,\n): AsyncSourceInfra<TQuery> {\n const core = createSourceCore({ onBeforeNotify });\n const fm = createFetchManager<TQuery>(keyOf);\n const debounceMs = cfg.debounceMs ?? 300;\n const retryAttempts = cfg.retry?.attempts ?? 0;\n const retryDelay = cfg.retry?.delay ?? defaultRetryDelay;\n const autoFetch = cfg.autoFetch !== false;\n\n let refreshTimer: ReturnType<typeof setInterval> | undefined;\n\n const dispose = (): void => {\n if (core.isDisposed) return;\n\n if (refreshTimer !== undefined) {\n clearInterval(refreshTimer);\n refreshTimer = undefined;\n }\n\n fm.dispose();\n core.dispose();\n };\n\n return {\n autoFetch,\n core,\n debounceMs,\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose,\n\n get disposed() {\n return core.isDisposed;\n },\n\n fetch(q, execute, onCommit) {\n return fm.run(q, execute, onCommit);\n },\n\n pendingCount: () => fm.pendingCount,\n\n ready(timeout) {\n return core.ready(() => fm.pendingCount === 0 && !core.isScheduled, timeout);\n },\n retryAttempts,\n\n retryDelay,\n\n startRefreshInterval(doUpdate) {\n if (cfg.refreshInterval !== undefined && cfg.refreshInterval > 0) {\n refreshTimer = setInterval(() => {\n if (!core.isDisposed) doUpdate();\n }, cfg.refreshInterval);\n }\n },\n\n [Symbol.dispose]: dispose,\n };\n}\n","import type { CursorConfig, CursorMeta, CursorSource, CursorSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\n/** Creates a cursor-based (keyset-pagination) source that fetches data from a network endpoint. */\nexport function createCursorSource<T, TCursor = string>(cfg: CursorConfig<T, TCursor>): CursorSource<T, TCursor> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let search = '';\n let afterCursor: TCursor | undefined;\n let beforeCursor: TCursor | undefined;\n let nextCursor: TCursor | undefined;\n let prevCursor: TCursor | undefined;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let pendingSearch: PendingSearch | null = null;\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta: CursorMeta = {\n error: null,\n hasNextPage: false,\n hasPrevPage: false,\n isLoading: false,\n isSearchPending: false,\n pageSize: limit,\n totalItems: 0,\n };\n\n const refreshMeta = () => {\n cachedCurrent = items;\n cachedMeta = {\n error,\n hasNextPage: nextCursor !== undefined,\n hasPrevPage: prevCursor !== undefined,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageSize: limit,\n totalItems: total,\n };\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<CursorSourceQuery<TCursor>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): CursorSourceQuery<TCursor> => ({\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n });\n\n const fetchQuery = (q: CursorSourceQuery<TCursor>): Promise<void> => {\n error = null;\n\n return base.fetch(\n q,\n async (q, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n items = result.items;\n total = result.total ?? result.items.length;\n nextCursor = result.nextCursor;\n prevCursor = result.prevCursor;\n error = null;\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n items = [];\n total = 0;\n nextCursor = undefined;\n prevCursor = undefined;\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'cursor', limit: q.limit, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n if (base.pendingCount() === 0) resolvePendingSearch();\n },\n );\n };\n\n const doUpdate = () => fetchQuery(toRemoteQuery());\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doUpdate();\n\n base.startRefreshInterval(() => void doUpdate());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n resolvePendingSearch();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n get meta() {\n return cachedMeta;\n },\n\n next() {\n if (!nextCursor) return Promise.resolve();\n\n afterCursor = nextCursor;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n base.core.cancelTimer();\n resolvePendingSearch();\n\n return doUpdate();\n },\n\n prev() {\n if (!prevCursor) return Promise.resolve();\n\n beforeCursor = prevCursor;\n afterCursor = undefined;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n base.core.cancelTimer();\n resolvePendingSearch();\n search = '';\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n base.core.cancelTimer();\n resolvePendingSearch();\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n resolvePendingSearch();\n\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n base.core.schedule(() => void doUpdate(), debounceMs);\n notifyListeners();\n\n return promise;\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n resolvePendingSearch();\n base.dispose();\n },\n };\n}\n","import type { DerivedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Creates a reactive read-only projection derived from a parent source.\n *\n * The derived source mirrors the parent's `meta` and applies `transform` to `current`\n * whenever the parent changes. This is useful for client-side projections (e.g. additional\n * filtering, mapping, or sorting) over a remote source without re-fetching.\n *\n * Call `dispose()` to stop listening to the parent.\n *\n * @example\n * ```ts\n * const source = createRemoteSource<User>({ fetch: fetchUsers, limit: 20 });\n *\n * // Show only active users without re-fetching.\n * const activeUsers = deriveSource(source, (items) => items.filter((u) => u.active));\n *\n * activeUsers.subscribe(() => {\n * console.log(activeUsers.current, activeUsers.meta.totalItems);\n * });\n * ```\n */\nexport function deriveSource<T, U, TMeta>(\n parent: {\n readonly current: readonly T[];\n readonly meta: TMeta;\n subscribe(listener: () => void): () => void;\n },\n transform: (items: readonly T[]) => readonly U[],\n): DerivedSource<U, TMeta> {\n const core = createSourceCore();\n let cachedCurrent: readonly U[] = [];\n\n const applyTransform = () => {\n cachedCurrent = transform(parent.current);\n };\n\n applyTransform();\n\n const unsubscribeParent = parent.subscribe(() => {\n applyTransform();\n core.notify();\n });\n\n if ('disposalSignal' in parent && parent.disposalSignal instanceof AbortSignal) {\n parent.disposalSignal.addEventListener('abort', () => core.dispose(), { once: true });\n }\n\n const source: DerivedSource<U, TMeta> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n unsubscribeParent();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n get meta() {\n return parent.meta;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n","import type { InfiniteConfig, InfiniteMeta, InfiniteSource, InfiniteSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\n/** Creates an infinite-scroll source that appends pages on `loadMore()`. */\nexport function createInfiniteSource<T>(cfg: InfiniteConfig<T>): InfiniteSource<T> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let search = '';\n let currentPage = 1;\n let loadedPages = 0;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let isLoadingMore = false;\n let pendingSearch: PendingSearch | null = null;\n\n // ── Cached accessors ─────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta: InfiniteMeta = {\n error: null,\n hasMore: false,\n isLoading: false,\n isLoadingMore: false,\n isSearchPending: false,\n loadedPages: 0,\n pageSize: limit,\n totalItems: 0,\n };\n\n const refreshMeta = () => {\n cachedCurrent = items;\n cachedMeta = {\n error,\n hasMore: items.length < total,\n isLoading: base.pendingCount() > 0 && !isLoadingMore,\n isLoadingMore,\n isSearchPending: base.core.isScheduled,\n loadedPages,\n pageSize: limit,\n totalItems: total,\n };\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<InfiniteSourceQuery>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const fetchPage = (targetPage: number, append: boolean): Promise<void> => {\n const q: InfiniteSourceQuery = {\n ...(search && { search }),\n limit,\n page: targetPage,\n };\n\n error = null;\n\n return base.fetch(\n q,\n async (q, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n if (append) {\n items = [...items, ...result.items];\n } else {\n items = result.items;\n }\n\n total = result.total;\n currentPage = targetPage;\n loadedPages = append ? loadedPages + 1 : 1;\n error = null;\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n if (!append) items = [];\n\n total = append ? total : 0;\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'infinite', limit: q.limit, page: q.page, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n } finally {\n isLoadingMore = false;\n }\n },\n () => {\n notifyListeners();\n\n if (base.pendingCount() === 0) resolvePendingSearch();\n },\n );\n };\n\n const doFetch = () => fetchPage(1, false);\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doFetch();\n\n base.startRefreshInterval(() => void doFetch());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n resolvePendingSearch();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n loadMore() {\n if (!cachedMeta.hasMore || base.pendingCount() > 0 || isLoadingMore) return Promise.resolve();\n\n isLoadingMore = true;\n refreshMeta();\n\n return fetchPage(currentPage + 1, true);\n },\n\n get meta() {\n return cachedMeta;\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n base.core.cancelTimer();\n resolvePendingSearch();\n\n items = [];\n total = 0;\n currentPage = 1;\n loadedPages = 0;\n\n return doFetch();\n },\n\n get query() {\n return {\n ...(search && { search }),\n limit,\n page: currentPage,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n reset() {\n base.core.cancelTimer();\n resolvePendingSearch();\n currentPage = 1;\n loadedPages = 0;\n items = [];\n total = 0;\n search = '';\n\n return doFetch();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n base.core.cancelTimer();\n resolvePendingSearch();\n search = q;\n items = [];\n total = 0;\n loadedPages = 0;\n currentPage = 1;\n\n return doFetch();\n }\n\n if (q === search) return Promise.resolve();\n\n resolvePendingSearch();\n\n search = q;\n items = [];\n total = 0;\n loadedPages = 0;\n currentPage = 1;\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n base.core.schedule(() => void doFetch(), debounceMs);\n notifyListeners();\n\n return promise;\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n resolvePendingSearch();\n base.dispose();\n },\n };\n}\n","import type { SourcererError, SourceMeta } from './types';\n\nconst clampInt = (value: number, minimum: number) => {\n const parsed = Math.trunc(value);\n\n if (!Number.isFinite(parsed)) {\n return minimum;\n }\n\n return Math.max(minimum, parsed);\n};\n\nexport const pageCount = (total: number, limit: number) => {\n const safeTotal = clampInt(total, 0);\n const safeLimit = clampInt(limit, 1);\n\n return Math.max(1, Math.ceil(safeTotal / safeLimit));\n};\n\nexport const clampPage = (page: number, pages: number) => {\n return Math.max(1, Math.min(clampInt(page, 1), Math.max(1, pages)));\n};\n\nexport const createMeta = (state: {\n error: SourcererError | null;\n isLoading: boolean;\n isSearchPending: boolean;\n pageNumber: number;\n pageSize: number;\n totalItems: number;\n}): SourceMeta => {\n const safeLimit = clampInt(state.pageSize, 1);\n const safeTotal = clampInt(state.totalItems, 0);\n const pages = pageCount(safeTotal, safeLimit);\n const page = clampPage(state.pageNumber, pages);\n\n return {\n error: state.error,\n isLoading: state.isLoading,\n isSearchPending: state.isSearchPending,\n pageCount: pages,\n pageNumber: page,\n pageSize: safeLimit,\n totalItems: safeTotal,\n };\n};\n\n/**\n * Computes the 1-based display range of items on the current page.\n * Returns `{ start: 0, end: 0 }` when there are no items.\n *\n * @example\n * ```ts\n * const { start, end } = itemRange(source.meta);\n * // \"Showing 11–20 of 50\"\n * console.log(`Showing ${start}–${end} of ${source.meta.totalItems}`);\n * ```\n */\nexport function itemRange(meta: Readonly<{ pageNumber: number; pageSize: number; totalItems: number }>): {\n end: number;\n start: number;\n} {\n if (meta.totalItems === 0) return { end: 0, start: 0 };\n\n const start = (meta.pageNumber - 1) * meta.pageSize + 1;\n\n return {\n end: Math.min(meta.pageNumber * meta.pageSize, meta.totalItems),\n start,\n };\n}\n","import type { LocalSource, LocalSourceConfig, LocalSourceQuery, SearchOptions, SourceQuery } from './types';\n\nimport { extractError } from './_utils';\nimport { createSourceCore } from './core';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\nconst DEFAULT_LIMIT = 20;\nconst DEFAULT_DEBOUNCE_MS = 300;\n\nfunction defaultSearch<T>(items: readonly T[], query: string): readonly T[] {\n const q = query.toLowerCase();\n\n return items.filter((item) => JSON.stringify(item).toLowerCase().includes(q));\n}\n\n/**\n * Creates an in-memory paginated source with optional async filter/sort pipelines.\n *\n * When neither `filterAsync` nor `sortAsync` are configured, all operations\n * complete synchronously — there is no loading state and `ready()` resolves immediately.\n * Provide async ops to offload heavy computation (e.g. to a Web Worker).\n *\n * @example\n * ```ts\n * // Synchronous (common case)\n * const source = createLocalSource([1, 2, 3, 4, 5], { limit: 2 });\n * await source.goTo(2);\n * console.log(source.current); // [3, 4]\n *\n * // Async pipeline (Web Worker offloading)\n * const source = createLocalSource(users, {\n * filterAsync: (items, signal) => myWorker.filter(items, signal),\n * sortAsync: (items, signal) => myWorker.sort(items, signal),\n * });\n * ```\n */\nexport function createLocalSource<T>(data: readonly T[], cfg: LocalSourceConfig<T> = {}): LocalSource<T> {\n const core = createSourceCore();\n const isAsync = cfg.filterAsync !== undefined || cfg.sortAsync !== undefined;\n const debounceMs = cfg.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n\n let allData: readonly T[] = cfg.initialData ?? data;\n let search = '';\n let filter = cfg.filter;\n let sort = cfg.sort;\n let limit = cfg.limit ?? DEFAULT_LIMIT;\n let page = 1;\n let processed: readonly T[] = [];\n let isLoading = false;\n let isSearchPending = false;\n let error: SourcererError | null = null;\n let asyncController: AbortController | null = null;\n let pendingSearch: PendingSearch | null = null;\n\n const commit = () => core.notify();\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Sync pipeline (used when no async ops) ──────────────────────────────────\n const runSyncPipeline = (): readonly T[] => {\n let result: readonly T[] = allData;\n\n if (search) result = (cfg.searchFn ?? defaultSearch)(result, search);\n\n if (filter) result = result.filter(filter);\n\n if (sort) result = [...result].sort(sort);\n\n return result;\n };\n\n const recompute = () => {\n processed = runSyncPipeline();\n\n const pages = pageCount(processed.length, limit);\n\n page = clampPage(page, pages);\n };\n\n // ── Async pipeline ──────────────────────────────────────────────────────────\n const runAsyncPipeline = async (): Promise<void> => {\n asyncController?.abort();\n\n const controller = new AbortController();\n\n asyncController = controller;\n isLoading = true;\n error = null;\n commit();\n\n try {\n let result: readonly T[] = allData;\n\n if (search) result = (cfg.searchFn ?? defaultSearch)(result, search);\n\n if (filter) result = result.filter(filter);\n\n if (cfg.filterAsync && !controller.signal.aborted) {\n result = await cfg.filterAsync(result, controller.signal);\n }\n\n if (sort) result = [...result].sort(sort);\n\n if (cfg.sortAsync && !controller.signal.aborted) {\n result = await cfg.sortAsync(result, controller.signal);\n }\n\n if (controller.signal.aborted) return;\n\n processed = result;\n isLoading = false;\n isSearchPending = false;\n\n const pages = pageCount(processed.length, limit);\n\n page = clampPage(page, pages);\n } catch (reason: unknown) {\n if (!controller.signal.aborted) {\n isLoading = false;\n isSearchPending = false;\n error = new SourcererError(extractError(reason), { cause: reason });\n }\n } finally {\n if (asyncController === controller) asyncController = null;\n }\n\n commit();\n resolvePendingSearch();\n };\n\n // Internal flush — not part of public API\n const flushInternal = (): Promise<void> => {\n if (isAsync) return runAsyncPipeline();\n\n isSearchPending = false;\n recompute();\n commit();\n resolvePendingSearch();\n\n return Promise.resolve();\n };\n\n // ── Initial state ───────────────────────────────────────────────────────────\n if (isAsync) {\n // Async: seed with a sync pass so `current` isn't empty while the async pass runs.\n processed = runSyncPipeline();\n void runAsyncPipeline();\n } else {\n recompute();\n }\n\n const source: LocalSource<T> = {\n get current() {\n const start = (page - 1) * limit;\n\n return processed.slice(start, start + limit);\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n core.cancelTimer();\n asyncController?.abort();\n asyncController = null;\n resolvePendingSearch();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n goTo(n) {\n const pages = pageCount(processed.length, limit);\n const clamped = clampPage(n, pages);\n\n if (clamped === page) return Promise.resolve();\n\n page = clamped;\n commit();\n\n return Promise.resolve();\n },\n\n goToLast() {\n return source.goTo(pageCount(processed.length, limit));\n },\n\n get meta() {\n return createMeta({\n error,\n isLoading,\n isSearchPending,\n pageNumber: page,\n pageSize: limit,\n totalItems: processed.length,\n });\n },\n\n next() {\n return source.goTo(page + 1);\n },\n\n patch(changes: LocalSourceQuery<T>) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n if ('filter' in changes) {\n filter = changes.filter;\n changed = true;\n }\n\n if ('sort' in changes) {\n sort = changes.sort;\n changed = true;\n }\n\n // `|| core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (changes.page !== undefined) {\n const next = clampPage(changes.page, pageCount(processed.length, limit));\n\n if (next !== page) {\n page = next;\n changed = true;\n }\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic recompute — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant recompute afterwards.\n core.cancelTimer();\n resolvePendingSearch();\n\n // Reset page only when non-page query fields changed without an explicit page\n if (\n (changes.limit !== undefined || 'filter' in changes || 'sort' in changes || 'search' in changes) &&\n changes.page === undefined\n ) {\n page = 1;\n }\n\n return flushInternal();\n },\n\n prev() {\n return source.goTo(page - 1);\n },\n\n get query(): SourceQuery {\n return {\n limit,\n page,\n ...(search && { search }),\n };\n },\n\n ready(timeout?: number) {\n return core.ready(() => !isLoading && !core.isScheduled, timeout);\n },\n\n reset() {\n core.cancelTimer();\n isSearchPending = false;\n search = '';\n filter = cfg.filter;\n sort = cfg.sort;\n page = 1;\n resolvePendingSearch();\n\n return flushInternal();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the recompute already happened.\n if (q === search && !core.isScheduled) return Promise.resolve();\n\n core.cancelTimer();\n isSearchPending = false;\n search = q;\n page = 1;\n resolvePendingSearch();\n\n return flushInternal();\n }\n\n if (q === search) return Promise.resolve();\n\n // Cancel any previous pending search promise\n resolvePendingSearch();\n\n search = q;\n page = 1;\n isSearchPending = true;\n commit();\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n core.schedule(() => {\n isSearchPending = false;\n void flushInternal();\n }, debounceMs);\n\n return promise;\n },\n\n setData(d) {\n allData = d;\n page = 1;\n\n return flushInternal();\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n","import type { MergedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Combines multiple reactive sources into a single read-only view by applying\n * a user-supplied `combine` function over all sources' `current` arrays.\n *\n * The merged source recalculates whenever any parent source changes. Useful for\n * offline/online hybrid UIs (local + remote merged), multi-dataset views, or\n * pagination across multiple backends.\n *\n * Call `dispose()` to unsubscribe from all parent sources.\n *\n * @example\n * ```ts\n * const local = createLocalSource(localCache, { limit: 50 });\n * const remote = createRemoteSource({ fetch: fetchUsers, limit: 50 });\n *\n * // Show local cache items first, then remote items (deduped by id).\n * const merged = mergeSource([local, remote], (all) => {\n * const seen = new Set<number>();\n * return all.flat().filter((u) => !seen.has(u.id) && seen.add(u.id));\n * });\n *\n * merged.subscribe(() => console.log(merged.current));\n * ```\n */\nexport function mergeSource<T>(\n sources: ReadonlyArray<{\n readonly current: readonly T[];\n subscribe(listener: () => void): () => void;\n }>,\n combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[],\n): MergedSource<T> {\n const core = createSourceCore();\n let cachedCurrent: readonly T[] = combine(sources.map((s) => s.current));\n\n const unsubscribeFns = sources.map((source) =>\n source.subscribe(() => {\n cachedCurrent = combine(sources.map((s) => s.current));\n core.notify();\n }),\n );\n\n const source: MergedSource<T> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n for (const unsub of unsubscribeFns) {\n unsub();\n }\n\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n","import type { RemoteConfig, RemoteSource, RemoteSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\n\ntype PendingSearch = { promise: Promise<void>; resolve: () => void };\n\n/** Creates a remote page-based source that fetches data from a network endpoint. */\nexport function createRemoteSource<T, TFilter = unknown, TSort = unknown>(\n cfg: RemoteConfig<T, TFilter, TSort>,\n): RemoteSource<T, TFilter, TSort> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Config defaults ─────────────────────────────────────────────────────────\n const staleTimeMs = cfg.staleTime ?? 0;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let page = 1;\n let search = '';\n let filter: TFilter | undefined = cfg.filter;\n let sort: TSort | undefined = cfg.sort;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let lastFetchTime = 0;\n let lastFetchKey = '';\n let pendingSearch: PendingSearch | null = null;\n\n // Optimistic update state.\n let optimistic: { active: boolean; prevItems: readonly T[]; prevTotal: number } | null = null;\n\n // ── Snapshot pre-population ─────────────────────────────────────────────────\n if (cfg.snapshot) {\n items = cfg.snapshot.items;\n total = cfg.snapshot.total;\n\n if (cfg.snapshot.page !== undefined) page = cfg.snapshot.page;\n\n if (cfg.snapshot.search) search = cfg.snapshot.search;\n\n lastFetchTime = Date.now();\n }\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta = createMeta({\n error: null,\n isLoading: false,\n isSearchPending: false,\n pageNumber: 1,\n pageSize: limit,\n totalItems: 0,\n });\n\n const refreshMeta = () => {\n const pages = pageCount(total, limit);\n const safePage = clampPage(page, pages);\n\n // items is already the current page from the server — no local slicing needed.\n cachedCurrent = items;\n cachedMeta = createMeta({\n error,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageNumber: safePage,\n pageSize: limit,\n totalItems: total,\n });\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<RemoteSourceQuery<TFilter, TSort>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n // Initialise cache before first fetch.\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n\n // ── Assign result ───────────────────────────────────────────────────────────\n const assign = (result: Readonly<{ items: readonly T[]; total: number }>) => {\n if (optimistic?.active) {\n optimistic.active = false;\n optimistic = null;\n }\n\n items = result.items;\n total = result.total;\n page = clampPage(page, pageCount(total, limit));\n };\n\n const resolvePendingSearch = () => {\n if (pendingSearch) {\n pendingSearch.resolve();\n pendingSearch = null;\n }\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): RemoteSourceQuery<TFilter, TSort> => ({\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n });\n\n const fetchQuery = (q: RemoteSourceQuery<TFilter, TSort>): Promise<void> => {\n const key = keyOf(q);\n\n if (staleTimeMs > 0 && key === lastFetchKey && total > 0 && Date.now() - lastFetchTime < staleTimeMs) {\n return Promise.resolve();\n }\n\n error = null;\n\n return base.fetch(\n q,\n async (q: RemoteSourceQuery<TFilter, TSort>, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n assign(result);\n error = null;\n lastFetchKey = key;\n lastFetchTime = Date.now();\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n if (optimistic?.active) {\n optimistic.active = false;\n items = optimistic.prevItems;\n total = optimistic.prevTotal;\n optimistic = null;\n } else {\n items = [];\n total = 0;\n }\n\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'remote', limit: q.limit, page: q.page, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n if (base.pendingCount() === 0) resolvePendingSearch();\n },\n );\n };\n\n const doUpdate = () => fetchQuery(toRemoteQuery());\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doUpdate();\n\n base.startRefreshInterval(() => void doUpdate());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n resolvePendingSearch();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n goTo(target) {\n const clamped = total > 0 ? clampPage(target, pageCount(total, limit)) : Math.max(1, Math.trunc(target));\n\n if (clamped === page) return Promise.resolve();\n\n page = clamped;\n\n return doUpdate();\n },\n\n goToLast() {\n const last = pageCount(total, limit);\n\n if (page === last) return Promise.resolve();\n\n page = last;\n\n return doUpdate();\n },\n\n get meta() {\n return cachedMeta;\n },\n\n next() {\n const pages = pageCount(total, limit);\n\n if (page >= pages) return Promise.resolve();\n\n page++;\n\n return doUpdate();\n },\n\n optimisticUpdate(mutator, options) {\n if (optimistic?.active) {\n throw new SourcererError(\n 'An optimistic update is already active. Rollback the previous update before starting another.',\n );\n }\n\n const prevItems = items;\n const prevTotal = total;\n\n optimistic = { active: true, prevItems, prevTotal };\n\n try {\n items = mutator(items);\n } catch (e: unknown) {\n optimistic = null;\n throw e;\n }\n\n total = options?.total ?? total;\n notifyListeners();\n\n return () => {\n if (optimistic?.active) {\n optimistic.active = false;\n items = prevItems;\n total = prevTotal;\n optimistic = null;\n notifyListeners();\n }\n };\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n if ('filter' in changes) {\n filter = changes.filter;\n changed = true;\n }\n\n if ('sort' in changes) {\n sort = changes.sort;\n changed = true;\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (changes.page !== undefined) {\n const next =\n total > 0 ? clampPage(changes.page, pageCount(total, limit)) : Math.max(1, Math.trunc(changes.page));\n\n if (next !== page) {\n page = next;\n changed = true;\n }\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n base.core.cancelTimer();\n resolvePendingSearch();\n\n // Reset to page 1 when non-page query fields changed without explicit page\n if (\n (changes.limit !== undefined || 'filter' in changes || 'sort' in changes || 'search' in changes) &&\n changes.page === undefined\n ) {\n page = 1;\n }\n\n return doUpdate();\n },\n\n prev() {\n if (page <= 1) return Promise.resolve();\n\n page--;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n base.core.cancelTimer();\n resolvePendingSearch();\n search = '';\n page = 1;\n filter = cfg.filter;\n sort = cfg.sort;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n base.core.cancelTimer();\n resolvePendingSearch();\n search = q;\n page = 1;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n resolvePendingSearch();\n\n search = q;\n page = 1;\n\n let resolveSearch!: () => void;\n const promise = new Promise<void>((res) => {\n resolveSearch = res;\n });\n\n pendingSearch = { promise, resolve: resolveSearch };\n\n base.core.schedule(() => void doUpdate(), debounceMs);\n notifyListeners();\n\n return promise;\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n resolvePendingSearch();\n base.dispose();\n },\n };\n}\n","import type { RemoteConfig, RemoteSource, SourceSnapshot } from './types';\n\nimport { createRemoteSource } from './remoteSource';\n\ntype PrefetchConfig<T, TFilter, TSort> = Omit<RemoteConfig<T, TFilter, TSort>, 'autoFetch' | 'refreshInterval'>;\n\nconst buildSnapshot = <T, TFilter, TSort>(source: RemoteSource<T, TFilter, TSort>): SourceSnapshot<T> => {\n const q = source.query;\n\n return {\n items: source.current,\n ...(q.search && { search: q.search }),\n page: source.meta.pageNumber,\n total: source.meta.totalItems,\n };\n};\n\n/**\n * Fetches a single page of data and returns a serialisable `SourceSnapshot`.\n * The underlying source is created, fetched, then immediately disposed.\n * Use for SSR / static-generation where you only need the data, not a live source.\n *\n * @example\n * ```ts\n * const snapshot = await prefetchSource({ fetch: fetchUsers, limit: 20 });\n * ```\n */\nexport async function prefetchSource<T, TFilter = unknown, TSort = unknown>(\n cfg: PrefetchConfig<T, TFilter, TSort>,\n): Promise<SourceSnapshot<T>> {\n const source = createRemoteSource<T, TFilter, TSort>({ ...cfg, autoFetch: false });\n\n try {\n await source.refresh();\n\n if (source.meta.error) throw source.meta.error;\n\n return buildSnapshot(source);\n } finally {\n source.dispose();\n }\n}\n\n/**\n * Fetches a single page of data and returns both a serialisable `SourceSnapshot` and the\n * still-live `RemoteSource`. Useful when you need the snapshot for SSR serialisation\n * **and** the live source for subsequent client-side updates without a double-fetch.\n *\n * **The caller is responsible for disposing the returned source.**\n *\n * @example\n * ```ts\n * const { snapshot, source } = await prefetchSourceAndKeep({ fetch: fetchUsers, limit: 20 });\n * // hydrate HTML with snapshot, pass source to client\n * ```\n */\nexport async function prefetchSourceAndKeep<T, TFilter = unknown, TSort = unknown>(\n cfg: PrefetchConfig<T, TFilter, TSort>,\n): Promise<{ snapshot: SourceSnapshot<T>; source: RemoteSource<T, TFilter, TSort> }> {\n const source = createRemoteSource<T, TFilter, TSort>({ ...cfg, autoFetch: false });\n\n await source.refresh();\n\n if (source.meta.error) {\n source.dispose();\n throw source.meta.error;\n }\n\n return { snapshot: buildSnapshot(source), source };\n}\n","import type { Predicate, Sorter } from './types';\n\ntype Direction = 'asc' | 'desc';\n\ntype PrimitiveComparable = number | string | Date;\n\nexport const filterContains = <T>(\n getValue: (item: T) => string | null | undefined,\n query: string,\n caseSensitive = false,\n): Predicate<T> => {\n const needle = caseSensitive ? query : query.toLowerCase();\n\n return (item) => {\n const value = getValue(item) ?? '';\n const haystack = caseSensitive ? value : value.toLowerCase();\n\n return haystack.includes(needle);\n };\n};\n\nexport const filterEquals = <T, V>(getValue: (item: T) => V, expected: V): Predicate<T> => {\n return (item) => Object.is(getValue(item), expected);\n};\n\nexport const filterRange = <T>(\n getValue: (item: T) => Date | number,\n bounds: Readonly<{ max?: Date | number; min?: Date | number }>,\n): Predicate<T> => {\n const toNum = (v: Date | number): number => (v instanceof Date ? v.getTime() : v);\n\n const min = bounds.min !== undefined ? toNum(bounds.min) : undefined;\n const max = bounds.max !== undefined ? toNum(bounds.max) : undefined;\n\n return (item) => {\n const value = toNum(getValue(item));\n\n if (min !== undefined && value < min) return false;\n\n if (max !== undefined && value > max) return false;\n\n return true;\n };\n};\n\nexport const sortBy = <T, V extends PrimitiveComparable>(\n getValue: (item: T) => V,\n direction: Direction = 'asc',\n): Sorter<T> => {\n const sign = direction === 'asc' ? 1 : -1;\n\n return (a, b) => {\n const left = getValue(a);\n const right = getValue(b);\n\n if (left instanceof Date && right instanceof Date) {\n return (left.getTime() - right.getTime()) * sign;\n }\n\n if (left < right) return -1 * sign;\n\n if (left > right) return 1 * sign;\n\n return 0;\n };\n};\n","import type { SourcererError, SourceState } from './types';\n\n/**\n * Derives a `SourceState<T>` discriminated union from a reactive source.\n * Works with any object that exposes `current`, `meta.isLoading`, `meta.error`, and optionally `meta.isSearchPending`.\n * Returns `'loading'` when `isLoading` or `isSearchPending` is true, so callers see a spinner during debounce windows.\n *\n * @example\n * ```ts\n * const state = sourceState(source);\n * if (state.status === 'loading') return <Spinner />;\n * if (state.status === 'error') return <p>{state.error.message}</p>;\n * return <List items={state.items} />;\n * ```\n */\nexport function sourceState<T>(source: {\n readonly current: readonly T[];\n readonly meta: {\n readonly error: SourcererError | null;\n readonly isLoading: boolean;\n readonly isSearchPending?: boolean;\n };\n}): SourceState<T> {\n if (source.meta.isLoading || source.meta.isSearchPending) return { status: 'loading' };\n\n if (source.meta.error) return { error: source.meta.error, status: 'error' };\n\n return { items: source.current, status: 'success' };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{backoff as e,hash as t,retry as n}from"@vielzeug/arsenal";function r(e,t){return e.patch(t)}var i=class e extends Error{#e;constructor(e,t={}){super(e,t.cause===void 0?void 0:{cause:t.cause}),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype),this.#e=t}get attempt(){return this.#e.attempt??0}get context(){return this.#e.context}static is(t){return t instanceof e}},a=class extends i{timeoutMs;constructor(e){super(`Source.ready() timed out after ${e}ms`),this.timeoutMs=e}},o=class extends i{constructor(){super(`Source disposed while waiting for ready()`)}},s=e=>{let t={...e.search&&{search:e.search},limit:String(e.limit),page:String(e.page)},n=e;return n.filter!==void 0&&(t.filter=JSON.stringify(n.filter)),n.sort!==void 0&&(t.sort=JSON.stringify(n.sort)),t},c=(e,t={})=>{let n=e instanceof URLSearchParams?Object.fromEntries(e.entries()):e,{defaultLimit:r=20,strict:a=!1}=t,o=(e,t)=>{if(e===void 0)return t;let n=Array.isArray(e)?e[0]:e,r=Number(n);return Number.isInteger(r)&&r>0?r:t},s=(e,t)=>{if(t===void 0)return;let n=Array.isArray(t)?t[0]:t;try{return JSON.parse(n)}catch{if(a)throw new i(`Invalid query param "${e}": ${n}`);return}},c=n.limit,l=n.page,u=n.search,d=n.filter,f=n.sort,p=d===void 0?void 0:s(`filter`,d),m=f===void 0?void 0:s(`sort`,f);return{...p!==void 0&&{filter:p},...m!==void 0&&{sort:m},...u!==void 0&&{search:Array.isArray(u)?u[0]:u},limit:o(c,r),page:o(l,1)}},l=t,u=e=>e instanceof Error?e.message:typeof e==`string`&&e.length>0?e:`Request failed`,d=t=>e(t);function f(e){let t=new Set,n=new Set,r=new AbortController,i,s=!1;return{cancelTimer(){i&&=(clearTimeout(i),void 0)},get disposalSignal(){return r.signal},dispose(){i&&=(clearTimeout(i),void 0);for(let e of n)e.timeoutId!==void 0&&clearTimeout(e.timeoutId),e.reject(new o);n.clear(),t.clear(),s=!0,r.abort()},flush(e){return i?(clearTimeout(i),i=void 0,e()):Promise.resolve()},get isDisposed(){return s},get isScheduled(){return i!==void 0},notify(r){if(!s){(r??e?.onBeforeNotify)?.();for(let e of t)e();for(let e of n)e.check()}},ready(e,t){return s?Promise.reject(new o):e()?Promise.resolve():new Promise((r,i)=>{let o={check:()=>{e()&&(o.timeoutId!==void 0&&clearTimeout(o.timeoutId),n.delete(o),r())},reject:i};t!==void 0&&(o.timeoutId=setTimeout(()=>{n.delete(o),i(new a(t))},t)),n.add(o)})},schedule(e,t){s||(i&&clearTimeout(i),i=setTimeout(()=>{i=void 0,e()},t))},subscribe(e){return s?()=>{}:(t.add(e),()=>t.delete(e))}}}function p(e){let t=new Map,n=``,r=0;return{dispose(){for(let e of t.values())e.controller.abort();t.clear(),r=0},get pendingCount(){return r},async run(i,a,o){let s=e(i);n=s;for(let[e,n]of t)e!==s&&(n.controller.abort(),t.delete(e));let c=t.get(s);if(c){r++,o();try{await c.promise}finally{r--,o()}return}let l=new AbortController;r++,o();let u=a(i,l.signal,()=>s===n).finally(()=>{t.delete(s),r--,o()});t.set(s,{controller:l,promise:u}),await u}}}function m(e,t=l,n){let r=f({onBeforeNotify:n}),i=p(t),a=e.debounceMs??300,o=e.retry?.attempts??0,s=e.retry?.delay??d,c=e.autoFetch!==!1,u,m=()=>{r.isDisposed||(u!==void 0&&(clearInterval(u),u=void 0),i.dispose(),r.dispose())};return{autoFetch:c,core:r,debounceMs:a,get disposalSignal(){return r.disposalSignal},dispose:m,get disposed(){return r.isDisposed},fetch(e,t,n){return i.run(e,t,n)},pendingCount:()=>i.pendingCount,ready(e){return r.ready(()=>i.pendingCount===0&&!r.isScheduled,e)},retryAttempts:o,retryDelay:s,startRefreshInterval(t){e.refreshInterval!==void 0&&e.refreshInterval>0&&(u=setInterval(()=>{r.isDisposed||t()},e.refreshInterval))},[Symbol.dispose]:m}}function h(e){let t=e.queryKey??l,r=Math.max(1,Math.trunc(e.limit??20)),a=``,o,s,c,d,f=[],p=0,h=null,g=null,_=[],v={error:null,hasNextPage:!1,hasPrevPage:!1,isLoading:!1,isSearchPending:!1,pageSize:r,totalItems:0},y=()=>{_=f,v={error:h,hasNextPage:c!==void 0,hasPrevPage:d!==void 0,isLoading:b.pendingCount()>0,isSearchPending:b.core.isScheduled,pageSize:r,totalItems:p}},b=m(e,t,y),{autoFetch:x,debounceMs:S,retryAttempts:C,retryDelay:w}=b;y();let T=()=>{b.disposed||b.core.notify()},E=()=>{g&&=(g.resolve(),null)},D=()=>({...o!==void 0&&{after:o},...s!==void 0&&{before:s},...a&&{search:a},limit:r}),O=t=>(h=null,b.fetch(t,async(t,r,a)=>{let o=Date.now();try{let i=await n(n=>e.fetch(t,n),{delay:w,signal:r,times:C+1});a()&&(f=i.items,p=i.total??i.items.length,c=i.nextCursor,d=i.prevCursor,h=null,e.onFetch?.({durationMs:Date.now()-o,query:t,status:`success`}))}catch(n){if(r.aborted)return;a()&&(f=[],p=0,c=void 0,d=void 0,h=new i(u(n),{cause:n,context:{kind:`cursor`,limit:t.limit,...t.search&&{search:t.search}}}),e.onFetch?.({durationMs:Date.now()-o,error:h,query:t,status:`error`}))}},()=>{T(),b.pendingCount()===0&&E()})),k=()=>O(D());return x&&k(),b.startRefreshInterval(()=>void k()),{get current(){return _},get disposalSignal(){return b.disposalSignal},dispose:()=>{E(),b.dispose()},get disposed(){return b.disposed},get meta(){return v},next(){return c?(o=c,s=void 0,k()):Promise.resolve()},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==r&&(r=n,o=void 0,s=void 0,t=!0)}return`search`in e&&(e.search!==a||b.core.isScheduled)&&(a=e.search??``,o=void 0,s=void 0,t=!0),t?(b.core.cancelTimer(),E(),k()):Promise.resolve()},prev(){return d?(s=d,o=void 0,k()):Promise.resolve()},get query(){return{...o!==void 0&&{after:o},...s!==void 0&&{before:s},...a&&{search:a},limit:r}},ready(e){return b.ready(e)},refresh(){return k()},reset(){return b.core.cancelTimer(),E(),a=``,o=void 0,s=void 0,k()},search(e,t){if(t?.immediate)return e===a&&!b.core.isScheduled?Promise.resolve():(b.core.cancelTimer(),E(),a=e,o=void 0,s=void 0,k());if(e===a)return Promise.resolve();E(),a=e,o=void 0,s=void 0;let n,r=new Promise(e=>{n=e});return g={promise:r,resolve:n},b.core.schedule(()=>void k(),S),T(),r},subscribe:e=>b.core.subscribe(e),[Symbol.dispose]:()=>{E(),b.dispose()}}}function g(e,t){let n=f(),r=[],i=()=>{r=t(e.current)};i();let a=e.subscribe(()=>{i(),n.notify()});`disposalSignal`in e&&e.disposalSignal instanceof AbortSignal&&e.disposalSignal.addEventListener(`abort`,()=>n.dispose(),{once:!0});let o={get current(){return r},get disposalSignal(){return n.disposalSignal},dispose(){n.isDisposed||(a(),n.dispose())},get disposed(){return n.isDisposed},get meta(){return e.meta},subscribe(e){return n.subscribe(e)},[Symbol.dispose](){o.dispose()}};return o}function _(e){let t=e.queryKey??l,r=Math.max(1,Math.trunc(e.limit??20)),a=``,o=1,s=0,c=[],d=0,f=null,p=!1,h=null,g=[],_={error:null,hasMore:!1,isLoading:!1,isLoadingMore:!1,isSearchPending:!1,loadedPages:0,pageSize:r,totalItems:0},v=()=>{g=c,_={error:f,hasMore:c.length<d,isLoading:y.pendingCount()>0&&!p,isLoadingMore:p,isSearchPending:y.core.isScheduled,loadedPages:s,pageSize:r,totalItems:d}},y=m(e,t,v),{autoFetch:b,debounceMs:x,retryAttempts:S,retryDelay:C}=y;v();let w=()=>{y.disposed||y.core.notify()},T=()=>{h&&=(h.resolve(),null)},E=(t,l)=>{let m={...a&&{search:a},limit:r,page:t};return f=null,y.fetch(m,async(r,a,m)=>{let h=Date.now();try{let i=await n(t=>e.fetch(r,t),{delay:C,signal:a,times:S+1});m()&&(c=l?[...c,...i.items]:i.items,d=i.total,o=t,s=l?s+1:1,f=null,e.onFetch?.({durationMs:Date.now()-h,query:r,status:`success`}))}catch(t){if(a.aborted)return;m()&&(l||(c=[]),d=l?d:0,f=new i(u(t),{cause:t,context:{kind:`infinite`,limit:r.limit,page:r.page,...r.search&&{search:r.search}}}),e.onFetch?.({durationMs:Date.now()-h,error:f,query:r,status:`error`}))}finally{p=!1}},()=>{w(),y.pendingCount()===0&&T()})},D=()=>E(1,!1);return b&&D(),y.startRefreshInterval(()=>void D()),{get current(){return g},get disposalSignal(){return y.disposalSignal},dispose:()=>{T(),y.dispose()},get disposed(){return y.disposed},loadMore(){return!_.hasMore||y.pendingCount()>0||p?Promise.resolve():(p=!0,v(),E(o+1,!0))},get meta(){return _},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==r&&(r=n,t=!0)}return`search`in e&&(e.search!==a||y.core.isScheduled)&&(a=e.search??``,t=!0),t?(y.core.cancelTimer(),T(),c=[],d=0,o=1,s=0,D()):Promise.resolve()},get query(){return{...a&&{search:a},limit:r,page:o}},ready(e){return y.ready(e)},reset(){return y.core.cancelTimer(),T(),o=1,s=0,c=[],d=0,a=``,D()},search(e,t){if(t?.immediate)return e===a&&!y.core.isScheduled?Promise.resolve():(y.core.cancelTimer(),T(),a=e,c=[],d=0,s=0,o=1,D());if(e===a)return Promise.resolve();T(),a=e,c=[],d=0,s=0,o=1;let n,r=new Promise(e=>{n=e});return h={promise:r,resolve:n},y.core.schedule(()=>void D(),x),w(),r},subscribe:e=>y.core.subscribe(e),[Symbol.dispose]:()=>{T(),y.dispose()}}}var v=(e,t)=>{let n=Math.trunc(e);return Number.isFinite(n)?Math.max(t,n):t},y=(e,t)=>{let n=v(e,0),r=v(t,1);return Math.max(1,Math.ceil(n/r))},b=(e,t)=>Math.max(1,Math.min(v(e,1),Math.max(1,t))),x=e=>{let t=v(e.pageSize,1),n=v(e.totalItems,0),r=y(n,t),i=b(e.pageNumber,r);return{error:e.error,isLoading:e.isLoading,isSearchPending:e.isSearchPending,pageCount:r,pageNumber:i,pageSize:t,totalItems:n}};function S(e){if(e.totalItems===0)return{end:0,start:0};let t=(e.pageNumber-1)*e.pageSize+1;return{end:Math.min(e.pageNumber*e.pageSize,e.totalItems),start:t}}var C=20,w=300;function T(e,t){let n=t.toLowerCase();return e.filter(e=>JSON.stringify(e).toLowerCase().includes(n))}function E(e,t={}){let n=f(),r=t.filterAsync!==void 0||t.sortAsync!==void 0,a=t.debounceMs??w,o=t.initialData??e,s=``,c=t.filter,l=t.sort,d=t.limit??C,p=1,m=[],h=!1,g=!1,_=null,v=null,S=null,E=()=>n.notify(),D=()=>{S&&=(S.resolve(),null)},O=()=>{let e=o;return s&&(e=(t.searchFn??T)(e,s)),c&&(e=e.filter(c)),l&&(e=[...e].sort(l)),e},k=()=>{m=O();let e=y(m.length,d);p=b(p,e)},A=async()=>{v?.abort();let e=new AbortController;v=e,h=!0,_=null,E();try{let n=o;if(s&&(n=(t.searchFn??T)(n,s)),c&&(n=n.filter(c)),t.filterAsync&&!e.signal.aborted&&(n=await t.filterAsync(n,e.signal)),l&&(n=[...n].sort(l)),t.sortAsync&&!e.signal.aborted&&(n=await t.sortAsync(n,e.signal)),e.signal.aborted)return;m=n,h=!1,g=!1;let r=y(m.length,d);p=b(p,r)}catch(t){e.signal.aborted||(h=!1,g=!1,_=new i(u(t),{cause:t}))}finally{v===e&&(v=null)}E(),D()},j=()=>r?A():(g=!1,k(),E(),D(),Promise.resolve());r?(m=O(),A()):k();let M={get current(){let e=(p-1)*d;return m.slice(e,e+d)},get disposalSignal(){return n.disposalSignal},dispose(){n.isDisposed||(n.cancelTimer(),v?.abort(),v=null,D(),n.dispose())},get disposed(){return n.isDisposed},goTo(e){let t=b(e,y(m.length,d));return t===p?Promise.resolve():(p=t,E(),Promise.resolve())},goToLast(){return M.goTo(y(m.length,d))},get meta(){return x({error:_,isLoading:h,isSearchPending:g,pageNumber:p,pageSize:d,totalItems:m.length})},next(){return M.goTo(p+1)},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==d&&(d=n,t=!0)}if(`filter`in e&&(c=e.filter,t=!0),`sort`in e&&(l=e.sort,t=!0),`search`in e&&(e.search!==s||n.isScheduled)&&(s=e.search??``,t=!0),e.page!==void 0){let n=b(e.page,y(m.length,d));n!==p&&(p=n,t=!0)}return t?(n.cancelTimer(),D(),(e.limit!==void 0||`filter`in e||`sort`in e||`search`in e)&&e.page===void 0&&(p=1),j()):Promise.resolve()},prev(){return M.goTo(p-1)},get query(){return{limit:d,page:p,...s&&{search:s}}},ready(e){return n.ready(()=>!h&&!n.isScheduled,e)},reset(){return n.cancelTimer(),g=!1,s=``,c=t.filter,l=t.sort,p=1,D(),j()},search(e,t){if(t?.immediate)return e===s&&!n.isScheduled?Promise.resolve():(n.cancelTimer(),g=!1,s=e,p=1,D(),j());if(e===s)return Promise.resolve();D(),s=e,p=1,g=!0,E();let r,i=new Promise(e=>{r=e});return S={promise:i,resolve:r},n.schedule(()=>{g=!1,j()},a),i},setData(e){return o=e,p=1,j()},subscribe(e){return n.subscribe(e)},[Symbol.dispose](){M.dispose()}};return M}function D(e,t){let n=f(),r=t(e.map(e=>e.current)),i=e.map(i=>i.subscribe(()=>{r=t(e.map(e=>e.current)),n.notify()})),a={get current(){return r},get disposalSignal(){return n.disposalSignal},dispose(){if(!n.isDisposed){for(let e of i)e();n.dispose()}},get disposed(){return n.isDisposed},subscribe(e){return n.subscribe(e)},[Symbol.dispose](){a.dispose()}};return a}function O(e){let t=e.queryKey??l,r=e.staleTime??0,a=Math.max(1,Math.trunc(e.limit??20)),o=1,s=``,c=e.filter,d=e.sort,f=[],p=0,h=null,g=0,_=``,v=null,S=null;e.snapshot&&(f=e.snapshot.items,p=e.snapshot.total,e.snapshot.page!==void 0&&(o=e.snapshot.page),e.snapshot.search&&(s=e.snapshot.search),g=Date.now());let C=[],w=x({error:null,isLoading:!1,isSearchPending:!1,pageNumber:1,pageSize:a,totalItems:0}),T=()=>{let e=y(p,a),t=b(o,e);C=f,w=x({error:h,isLoading:E.pendingCount()>0,isSearchPending:E.core.isScheduled,pageNumber:t,pageSize:a,totalItems:p})},E=m(e,t,T),{autoFetch:D,debounceMs:O,retryAttempts:k,retryDelay:A}=E;T();let j=()=>{E.disposed||E.core.notify()},M=e=>{S?.active&&(S.active=!1,S=null),f=e.items,p=e.total,o=b(o,y(p,a))},N=()=>{v&&=(v.resolve(),null)},P=()=>({...c!==void 0&&{filter:c},...d!==void 0&&{sort:d},...s&&{search:s},limit:a,page:o}),F=a=>{let o=t(a);return r>0&&o===_&&p>0&&Date.now()-g<r?Promise.resolve():(h=null,E.fetch(a,async(t,r,a)=>{let s=Date.now();try{let i=await n(n=>e.fetch(t,n),{delay:A,signal:r,times:k+1});a()&&(M(i),h=null,_=o,g=Date.now(),e.onFetch?.({durationMs:Date.now()-s,query:t,status:`success`}))}catch(n){if(r.aborted)return;a()&&(S?.active?(S.active=!1,f=S.prevItems,p=S.prevTotal,S=null):(f=[],p=0),h=new i(u(n),{cause:n,context:{kind:`remote`,limit:t.limit,page:t.page,...t.search&&{search:t.search}}}),e.onFetch?.({durationMs:Date.now()-s,error:h,query:t,status:`error`}))}},()=>{j(),E.pendingCount()===0&&N()}))},I=()=>F(P());return D&&I(),E.startRefreshInterval(()=>void I()),{get current(){return C},get disposalSignal(){return E.disposalSignal},dispose:()=>{N(),E.dispose()},get disposed(){return E.disposed},goTo(e){let t=p>0?b(e,y(p,a)):Math.max(1,Math.trunc(e));return t===o?Promise.resolve():(o=t,I())},goToLast(){let e=y(p,a);return o===e?Promise.resolve():(o=e,I())},get meta(){return w},next(){let e=y(p,a);return o>=e?Promise.resolve():(o++,I())},optimisticUpdate(e,t){if(S?.active)throw new i(`An optimistic update is already active. Rollback the previous update before starting another.`);let n=f,r=p;S={active:!0,prevItems:n,prevTotal:r};try{f=e(f)}catch(e){throw S=null,e}return p=t?.total??p,j(),()=>{S?.active&&(S.active=!1,f=n,p=r,S=null,j())}},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==a&&(a=n,t=!0)}if(`filter`in e&&(c=e.filter,t=!0),`sort`in e&&(d=e.sort,t=!0),`search`in e&&(e.search!==s||E.core.isScheduled)&&(s=e.search??``,t=!0),e.page!==void 0){let n=p>0?b(e.page,y(p,a)):Math.max(1,Math.trunc(e.page));n!==o&&(o=n,t=!0)}return t?(E.core.cancelTimer(),N(),(e.limit!==void 0||`filter`in e||`sort`in e||`search`in e)&&e.page===void 0&&(o=1),I()):Promise.resolve()},prev(){return o<=1?Promise.resolve():(o--,I())},get query(){return{...c!==void 0&&{filter:c},...d!==void 0&&{sort:d},...s&&{search:s},limit:a,page:o}},ready(e){return E.ready(e)},refresh(){return I()},reset(){return E.core.cancelTimer(),N(),s=``,o=1,c=e.filter,d=e.sort,I()},search(e,t){if(t?.immediate)return e===s&&!E.core.isScheduled?Promise.resolve():(E.core.cancelTimer(),N(),s=e,o=1,I());if(e===s)return Promise.resolve();N(),s=e,o=1;let n,r=new Promise(e=>{n=e});return v={promise:r,resolve:n},E.core.schedule(()=>void I(),O),j(),r},subscribe:e=>E.core.subscribe(e),[Symbol.dispose]:()=>{N(),E.dispose()}}}var k=e=>{let t=e.query;return{items:e.current,...t.search&&{search:t.search},page:e.meta.pageNumber,total:e.meta.totalItems}};async function A(e){let t=O({...e,autoFetch:!1});try{if(await t.refresh(),t.meta.error)throw t.meta.error;return k(t)}finally{t.dispose()}}async function j(e){let t=O({...e,autoFetch:!1});if(await t.refresh(),t.meta.error)throw t.dispose(),t.meta.error;return{snapshot:k(t),source:t}}var M=(e,t,n=!1)=>{let r=n?t:t.toLowerCase();return t=>{let i=e(t)??``;return(n?i:i.toLowerCase()).includes(r)}},N=(e,t)=>n=>Object.is(e(n),t),P=(e,t)=>{let n=e=>e instanceof Date?e.getTime():e,r=t.min===void 0?void 0:n(t.min),i=t.max===void 0?void 0:n(t.max);return t=>{let a=n(e(t));return!(r!==void 0&&a<r||i!==void 0&&a>i)}},F=(e,t=`asc`)=>{let n=t===`asc`?1:-1;return(t,r)=>{let i=e(t),a=e(r);return i instanceof Date&&a instanceof Date?(i.getTime()-a.getTime())*n:i<a?-1*n:i>a?1*n:0}};function I(e){return e.meta.isLoading||e.meta.isSearchPending?{status:`loading`}:e.meta.error?{error:e.meta.error,status:`error`}:{items:e.current,status:`success`}}export{o as SourceDisposedError,a as SourceTimeoutError,i as SourcererError,r as applyQuery,h as createCursorSource,_ as createInfiniteSource,E as createLocalSource,O as createRemoteSource,c as decodeQuery,g as deriveSource,s as encodeQuery,M as filterContains,N as filterEquals,P as filterRange,S as itemRange,D as mergeSource,A as prefetchSource,j as prefetchSourceAndKeep,F as sortBy,I as sourceState};
2
+ //# sourceMappingURL=sourcerer.js.map