@vielzeug/sourcerer 1.0.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +19 -74
  2. package/dist/_dev.cjs +2 -0
  3. package/dist/_dev.cjs.map +1 -0
  4. package/dist/_dev.d.ts +2 -0
  5. package/dist/_dev.d.ts.map +1 -0
  6. package/dist/_dev.js +6 -0
  7. package/dist/_dev.js.map +1 -0
  8. package/dist/asyncSource.cjs +1 -1
  9. package/dist/asyncSource.cjs.map +1 -1
  10. package/dist/asyncSource.d.ts +14 -58
  11. package/dist/asyncSource.d.ts.map +1 -1
  12. package/dist/asyncSource.js +28 -27
  13. package/dist/asyncSource.js.map +1 -1
  14. package/dist/cursorSource.cjs +1 -1
  15. package/dist/cursorSource.cjs.map +1 -1
  16. package/dist/cursorSource.d.ts +3 -3
  17. package/dist/cursorSource.d.ts.map +1 -1
  18. package/dist/cursorSource.js +77 -110
  19. package/dist/cursorSource.js.map +1 -1
  20. package/dist/devtools.cjs +2 -0
  21. package/dist/devtools.cjs.map +1 -0
  22. package/dist/devtools.d.ts +6 -0
  23. package/dist/devtools.d.ts.map +1 -0
  24. package/dist/devtools.js +8 -0
  25. package/dist/devtools.js.map +1 -0
  26. package/dist/index.cjs +1 -1
  27. package/dist/index.d.ts +2 -12
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +5 -14
  30. package/dist/infiniteSource.cjs +1 -1
  31. package/dist/infiniteSource.cjs.map +1 -1
  32. package/dist/infiniteSource.d.ts +3 -3
  33. package/dist/infiniteSource.d.ts.map +1 -1
  34. package/dist/infiniteSource.js +72 -107
  35. package/dist/infiniteSource.js.map +1 -1
  36. package/dist/localSource.cjs +1 -1
  37. package/dist/localSource.cjs.map +1 -1
  38. package/dist/localSource.d.ts +2 -22
  39. package/dist/localSource.d.ts.map +1 -1
  40. package/dist/localSource.js +58 -114
  41. package/dist/localSource.js.map +1 -1
  42. package/dist/pageSource.cjs +2 -0
  43. package/dist/pageSource.cjs.map +1 -0
  44. package/dist/pageSource.d.ts +4 -0
  45. package/dist/pageSource.d.ts.map +1 -0
  46. package/dist/pageSource.js +96 -0
  47. package/dist/pageSource.js.map +1 -0
  48. package/dist/pagination.cjs +1 -1
  49. package/dist/pagination.cjs.map +1 -1
  50. package/dist/pagination.d.ts +5 -30
  51. package/dist/pagination.d.ts.map +1 -1
  52. package/dist/pagination.js +21 -27
  53. package/dist/pagination.js.map +1 -1
  54. package/dist/requestController.cjs +2 -0
  55. package/dist/requestController.cjs.map +1 -0
  56. package/dist/requestController.d.ts +13 -0
  57. package/dist/requestController.d.ts.map +1 -0
  58. package/dist/requestController.js +29 -0
  59. package/dist/requestController.js.map +1 -0
  60. package/dist/sourceStore.cjs +2 -0
  61. package/dist/sourceStore.cjs.map +1 -0
  62. package/dist/sourceStore.d.ts +10 -0
  63. package/dist/sourceStore.d.ts.map +1 -0
  64. package/dist/sourceStore.js +38 -0
  65. package/dist/sourceStore.js.map +1 -0
  66. package/dist/sourcerer.cjs +1 -1
  67. package/dist/sourcerer.cjs.map +1 -1
  68. package/dist/sourcerer.iife.js +1 -1
  69. package/dist/sourcerer.iife.js.map +1 -1
  70. package/dist/sourcerer.js +1 -1
  71. package/dist/sourcerer.js.map +1 -1
  72. package/dist/types.d.ts +128 -345
  73. package/dist/types.d.ts.map +1 -1
  74. package/package.json +10 -7
  75. package/dist/_utils.cjs +0 -2
  76. package/dist/_utils.cjs.map +0 -1
  77. package/dist/_utils.d.ts +0 -8
  78. package/dist/_utils.d.ts.map +0 -1
  79. package/dist/_utils.js +0 -7
  80. package/dist/_utils.js.map +0 -1
  81. package/dist/applyQuery.cjs +0 -2
  82. package/dist/applyQuery.cjs.map +0 -1
  83. package/dist/applyQuery.d.ts +0 -20
  84. package/dist/applyQuery.d.ts.map +0 -1
  85. package/dist/applyQuery.js +0 -8
  86. package/dist/applyQuery.js.map +0 -1
  87. package/dist/asyncSearch.cjs +0 -2
  88. package/dist/asyncSearch.cjs.map +0 -1
  89. package/dist/asyncSearch.d.ts +0 -13
  90. package/dist/asyncSearch.d.ts.map +0 -1
  91. package/dist/asyncSearch.js +0 -28
  92. package/dist/asyncSearch.js.map +0 -1
  93. package/dist/codecs.cjs +0 -2
  94. package/dist/codecs.cjs.map +0 -1
  95. package/dist/codecs.d.ts +0 -30
  96. package/dist/codecs.d.ts.map +0 -1
  97. package/dist/codecs.js +0 -36
  98. package/dist/codecs.js.map +0 -1
  99. package/dist/core.cjs +0 -2
  100. package/dist/core.cjs.map +0 -1
  101. package/dist/core.d.ts +0 -47
  102. package/dist/core.d.ts.map +0 -1
  103. package/dist/core.js +0 -59
  104. package/dist/core.js.map +0 -1
  105. package/dist/derive.cjs +0 -2
  106. package/dist/derive.cjs.map +0 -1
  107. package/dist/derive.d.ts +0 -28
  108. package/dist/derive.d.ts.map +0 -1
  109. package/dist/derive.js +0 -40
  110. package/dist/derive.js.map +0 -1
  111. package/dist/errors.cjs +0 -2
  112. package/dist/errors.cjs.map +0 -1
  113. package/dist/errors.d.ts +0 -62
  114. package/dist/errors.d.ts.map +0 -1
  115. package/dist/errors.js +0 -29
  116. package/dist/errors.js.map +0 -1
  117. package/dist/fetchManager.cjs +0 -2
  118. package/dist/fetchManager.cjs.map +0 -1
  119. package/dist/fetchManager.d.ts +0 -27
  120. package/dist/fetchManager.d.ts.map +0 -1
  121. package/dist/fetchManager.js +0 -41
  122. package/dist/fetchManager.js.map +0 -1
  123. package/dist/merge.cjs +0 -2
  124. package/dist/merge.cjs.map +0 -1
  125. package/dist/merge.d.ts +0 -30
  126. package/dist/merge.d.ts.map +0 -1
  127. package/dist/merge.js +0 -34
  128. package/dist/merge.js.map +0 -1
  129. package/dist/prefetch.cjs +0 -2
  130. package/dist/prefetch.cjs.map +0 -1
  131. package/dist/prefetch.d.ts +0 -32
  132. package/dist/prefetch.d.ts.map +0 -1
  133. package/dist/prefetch.js +0 -38
  134. package/dist/prefetch.js.map +0 -1
  135. package/dist/presets.cjs +0 -2
  136. package/dist/presets.cjs.map +0 -1
  137. package/dist/presets.d.ts +0 -17
  138. package/dist/presets.d.ts.map +0 -1
  139. package/dist/presets.js +0 -43
  140. package/dist/presets.js.map +0 -1
  141. package/dist/remoteSource.cjs +0 -2
  142. package/dist/remoteSource.cjs.map +0 -1
  143. package/dist/remoteSource.d.ts +0 -4
  144. package/dist/remoteSource.d.ts.map +0 -1
  145. package/dist/remoteSource.js +0 -165
  146. package/dist/remoteSource.js.map +0 -1
  147. package/dist/state.cjs +0 -2
  148. package/dist/state.cjs.map +0 -1
  149. package/dist/state.d.ts +0 -23
  150. package/dist/state.d.ts.map +0 -1
  151. package/dist/state.js +0 -14
  152. package/dist/state.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"infiniteSource.js","names":[],"sources":["../src/infiniteSource.ts"],"sourcesContent":["import type { InfiniteConfig, InfiniteMeta, InfiniteSource, InfiniteSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSearchCoordinator } from './asyncSearch';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\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\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 const searchCoordinator = createAsyncSearchCoordinator(base.core, notifyListeners);\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 searchCoordinator.settleIfIdle(base.pendingCount());\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 searchCoordinator.cancel();\n searchCoordinator.dispose();\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 searchCoordinator.cancel();\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 searchCoordinator.cancel();\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 searchCoordinator.cancel();\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 search = q;\n items = [];\n total = 0;\n loadedPages = 0;\n currentPage = 1;\n\n return searchCoordinator.schedule(() => void doFetch(), debounceMs);\n },\n\n subscribe: (listener) => base.core.subscribe(listener),\n\n [Symbol.dispose]: () => {\n searchCoordinator.cancel();\n searchCoordinator.dispose();\n base.dispose();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"infiniteSource.js","names":[],"sources":["../src/infiniteSource.ts"],"sourcesContent":["import type {\n InfinitePagination,\n InfiniteQuery,\n InfiniteQueryPatch,\n InfiniteSource,\n InfiniteSourceConfig,\n} from './types';\n\nimport { warn } from './_dev';\nimport { createAsyncSource } from './asyncSource';\nimport { positiveInteger, sameQuery, totalItems } from './pagination';\n\nconst createPagination = (loaded: number, total: number, isLoadingMore: boolean): InfinitePagination => ({\n hasMore: loaded < total,\n isLoadingMore,\n kind: 'infinite',\n loaded,\n total,\n});\n\nconst normalizeQuery = (current: InfiniteQuery, patch: InfiniteQueryPatch = {}): InfiniteQuery => ({\n pageSize: positiveInteger(patch.pageSize ?? current.pageSize, 'pageSize'),\n search: patch.search ?? current.search,\n});\n\n/** Infinite sources preserve loaded collection state while a pending query replaces it. */\nexport function createInfiniteSource<T>(config: InfiniteSourceConfig<T>): InfiniteSource<T> {\n const initialQuery: InfiniteQuery = { pageSize: 20, search: '' };\n let requestedQuery = normalizeQuery(initialQuery, config.initialQuery);\n let nextPage = 1;\n const asyncSource = createAsyncSource<T, InfiniteQuery, InfinitePagination>({\n data: [],\n error: null,\n isFetching: false,\n pagination: createPagination(0, 0, false),\n query: requestedQuery,\n });\n\n const fetch = (query: InfiniteQuery, page: number, append: boolean): Promise<void> => {\n const loadedData = asyncSource.snapshot.data;\n\n return asyncSource.fetch({\n failure: (previous, error) => ({ ...previous, error, isFetching: false, pendingQuery: undefined }),\n load: (signal) => config.load({ query: { ...query, page, pageSize: query.pageSize }, signal }),\n pending: (previous) => ({\n ...previous,\n error: null,\n isFetching: true,\n pagination: createPagination(previous.data.length, previous.pagination.total, append),\n pendingQuery: query,\n }),\n success: (result) => {\n const total = totalItems(result.total);\n const data = append ? [...loadedData, ...result.data] : result.data;\n\n nextPage = page + 1;\n\n return {\n data,\n error: null,\n isFetching: false,\n pagination: createPagination(data.length, total, false),\n query,\n };\n },\n });\n };\n\n const reload = (): Promise<void> => {\n nextPage = 1;\n\n return fetch(requestedQuery, 1, false);\n };\n\n const setQuery = async (patch: InfiniteQueryPatch): Promise<void> => {\n const current = asyncSource.snapshot.pendingQuery ?? asyncSource.snapshot.query;\n const next = normalizeQuery(current, patch);\n\n if (sameQuery(next, requestedQuery)) return;\n\n requestedQuery = next;\n nextPage = 1;\n await fetch(next, 1, false);\n };\n\n const source: InfiniteSource<T> = {\n get disposalSignal() {\n return asyncSource.disposalSignal;\n },\n\n dispose: asyncSource.dispose,\n\n get disposed() {\n return asyncSource.disposed;\n },\n\n loadMore() {\n const { isFetching, pagination } = asyncSource.snapshot;\n\n return isFetching || (asyncSource.snapshot.data.length > 0 && !pagination.hasMore)\n ? Promise.resolve()\n : fetch(requestedQuery, nextPage, true);\n },\n\n reload,\n setQuery,\n\n get snapshot() {\n return asyncSource.snapshot;\n },\n\n subscribe: asyncSource.subscribe,\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n if (config.autoStart !== false)\n void reload().catch(() => warn('Initial load failed. Inspect source.snapshot.error.'));\n\n return source;\n}\n"],"mappings":";;;;AAYA,IAAM,KAAoB,GAAgB,GAAe,OAAgD;CACvG,SAAS,IAAS;CAClB;CACA,MAAM;CACN;CACA;AACF,IAEM,KAAkB,GAAwB,IAA4B,CAAC,OAAsB;CACjG,UAAU,EAAgB,EAAM,YAAY,EAAQ,UAAU,UAAU;CACxE,QAAQ,EAAM,UAAU,EAAQ;AAClC;AAGA,SAAgB,EAAwB,GAAoD;CAE1F,IAAI,IAAiB,EAAe;EADE,UAAU;EAAI,QAAQ;CACxB,GAAc,EAAO,YAAY,GACjE,IAAW,GACT,IAAc,EAAwD;EAC1E,MAAM,CAAC;EACP,OAAO;EACP,YAAY;EACZ,YAAY,EAAiB,GAAG,GAAG,EAAK;EACxC,OAAO;CACT,CAAC,GAEK,KAAS,GAAsB,GAAc,MAAmC;EACpF,IAAM,IAAa,EAAY,SAAS;EAExC,OAAO,EAAY,MAAM;GACvB,UAAU,GAAU,OAAW;IAAE,GAAG;IAAU;IAAO,YAAY;IAAO,cAAc,KAAA;GAAU;GAChG,OAAO,MAAW,EAAO,KAAK;IAAE,OAAO;KAAE,GAAG;KAAO;KAAM,UAAU,EAAM;IAAS;IAAG;GAAO,CAAC;GAC7F,UAAU,OAAc;IACtB,GAAG;IACH,OAAO;IACP,YAAY;IACZ,YAAY,EAAiB,EAAS,KAAK,QAAQ,EAAS,WAAW,OAAO,CAAM;IACpF,cAAc;GAChB;GACA,UAAU,MAAW;IACnB,IAAM,IAAQ,EAAW,EAAO,KAAK,GAC/B,IAAO,IAAS,CAAC,GAAG,GAAY,GAAG,EAAO,IAAI,IAAI,EAAO;IAI/D,OAFA,IAAW,IAAO,GAEX;KACL;KACA,OAAO;KACP,YAAY;KACZ,YAAY,EAAiB,EAAK,QAAQ,GAAO,EAAK;KACtD;IACF;GACF;EACF,CAAC;CACH,GAEM,WACJ,IAAW,GAEJ,EAAM,GAAgB,GAAG,EAAK,IAcjC,IAA4B;EAChC,IAAI,iBAAiB;GACnB,OAAO,EAAY;EACrB;EAEA,SAAS,EAAY;EAErB,IAAI,WAAW;GACb,OAAO,EAAY;EACrB;EAEA,WAAW;GACT,IAAM,EAAE,eAAY,kBAAe,EAAY;GAE/C,OAAO,KAAe,EAAY,SAAS,KAAK,SAAS,KAAK,CAAC,EAAW,UACtE,QAAQ,QAAQ,IAChB,EAAM,GAAgB,GAAU,EAAI;EAC1C;EAEA;EACA,iBA/BsB,MAA6C;GAEnE,IAAM,IAAO,EADG,EAAY,SAAS,gBAAgB,EAAY,SAAS,OACrC,CAAK;GAEtC,EAAU,GAAM,CAAc,MAElC,IAAiB,GACjB,IAAW,GACX,MAAM,EAAM,GAAM,GAAG,EAAK;EAC5B;EAwBE,IAAI,WAAW;GACb,OAAO,EAAY;EACrB;EAEA,WAAW,EAAY;EAEvB,CAAC,OAAO,WAAW;GACjB,EAAO,QAAQ;EACjB;CACF;CAKA,OAHI,EAAO,cAAc,MACvB,EAAY,CAAC,CAAC,YAAY,kBAAK,qDAAqD,CAAC,GAEhF;AACT"}
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_utils.cjs"),n=require("./core.cjs"),r=require("./pagination.cjs");var i=20,a=300;function o(e,t){let n=t.toLowerCase();return e.filter(e=>JSON.stringify(e).toLowerCase().includes(n))}function s(s,c={}){let l=n.createSourceCore(),u=c.filterAsync!==void 0||c.sortAsync!==void 0,d=c.debounceMs??a,f=c.initialData??s,p=``,m=c.filter,h=c.sort,g=c.limit??i,_=1,v=[],y=!1,b=!1,x=null,S=null,C=null,w=()=>l.notify(),T=()=>{C&&=(C.resolve(),null)},E=()=>{let e=f;return p&&(e=(c.searchFn??o)(e,p)),m&&(e=e.filter(m)),h&&(e=[...e].sort(h)),e},D=()=>{v=E();let e=r.pageCount(v.length,g);_=r.clampPage(_,e)},O=async()=>{S?.abort();let n=new AbortController;S=n,y=!0,x=null,w();try{let e=f;if(p&&(e=(c.searchFn??o)(e,p)),m&&(e=e.filter(m)),c.filterAsync&&!n.signal.aborted&&(e=await c.filterAsync(e,n.signal)),h&&(e=[...e].sort(h)),c.sortAsync&&!n.signal.aborted&&(e=await c.sortAsync(e,n.signal)),n.signal.aborted)return;v=e,y=!1,b=!1;let t=r.pageCount(v.length,g);_=r.clampPage(_,t)}catch(r){n.signal.aborted||(y=!1,b=!1,x=new e.SourcererError(t.extractError(r),{cause:r}))}finally{S===n&&(S=null)}w(),T()},k=()=>u?O():(b=!1,D(),w(),T(),Promise.resolve());u?(v=E(),O()):D();let A={get current(){let e=(_-1)*g;return v.slice(e,e+g)},get disposalSignal(){return l.disposalSignal},dispose(){l.isDisposed||(l.cancelTimer(),S?.abort(),S=null,T(),l.dispose())},get disposed(){return l.isDisposed},goTo(e){let t=r.clampPage(e,r.pageCount(v.length,g));return t===_?Promise.resolve():(_=t,w(),Promise.resolve())},goToLast(){return A.goTo(r.pageCount(v.length,g))},get meta(){return r.createMeta({error:x,isLoading:y,isSearchPending:b,pageNumber:_,pageSize:g,totalItems:v.length})},next(){return A.goTo(_+1)},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==g&&(g=n,t=!0)}if(`filter`in e&&(m=e.filter,t=!0),`sort`in e&&(h=e.sort,t=!0),`search`in e&&(e.search!==p||l.isScheduled)&&(p=e.search??``,t=!0),e.page!==void 0){let n=r.clampPage(e.page,r.pageCount(v.length,g));n!==_&&(_=n,t=!0)}return t?(l.cancelTimer(),T(),(e.limit!==void 0||`filter`in e||`sort`in e||`search`in e)&&e.page===void 0&&(_=1),k()):Promise.resolve()},prev(){return A.goTo(_-1)},get query(){return{limit:g,page:_,...p&&{search:p}}},ready(e){return l.ready(()=>!y&&!l.isScheduled,e)},reset(){return l.cancelTimer(),b=!1,p=``,m=c.filter,h=c.sort,_=1,T(),k()},search(e,t){if(t?.immediate)return e===p&&!l.isScheduled?Promise.resolve():(l.cancelTimer(),b=!1,p=e,_=1,T(),k());if(e===p)return Promise.resolve();T(),p=e,_=1,b=!0,w();let n,r=new Promise(e=>{n=e});return C={promise:r,resolve:n},l.schedule(()=>{b=!1,k()},d),r},setData(e){return f=e,_=1,k()},subscribe(e){return l.subscribe(e)},[Symbol.dispose](){A.dispose()}};return A}exports.createLocalSource=s;
1
+ const e=require("./sourceStore.cjs"),t=require("./pagination.cjs");var n=(e,n={},r)=>{let i=n.page??e.page,a=n.pageSize??e.pageSize,o=n.search??e.search,s=n.pageSize!==void 0||n.search!==void 0,c=n.page??(s?1:i);return{page:r===void 0?t.positiveInteger(c,`page`):Math.min(t.positiveInteger(c,`page`),r),pageSize:t.positiveInteger(a,`pageSize`),search:o}};function r(r,i={}){let a=r,o=n({page:1,pageSize:20,search:``},i.initialQuery),s=()=>{let e=o.search.length>0&&i.match?a.filter(e=>i.match(e,o.search)):a,n=t.totalItems(e.length),r=t.createPagePagination(o.page,o.pageSize,n),s=(r.index-1)*r.size;return o={...o,page:r.index},{data:e.slice(s,s+r.size),error:null,isFetching:!1,pagination:r,query:o}},c=e.createSourceStore(s()),l=()=>{let e=s();return t.sameQuery(e.query,c.value.query)&&e.data===c.value.data&&e.pagination===c.value.pagination?!1:(c.set(e),!0)},u=e=>{let r=n(o,e,c.value.pagination.count);return!t.sameQuery(r,o)&&(o=r,l())},d={get disposalSignal(){return c.disposalSignal},dispose:c.dispose,get disposed(){return c.disposed},page:{go(e){return u({page:e})},last(){return u({page:c.value.pagination.count})},next(){return c.value.pagination.hasNext&&u({page:c.value.pagination.index+1})},previous(){return c.value.pagination.hasPrevious&&u({page:c.value.pagination.index-1})}},setData(e){return e!==a&&(a=e,l())},setQuery:u,get snapshot(){return c.value},subscribe:c.subscribe,[Symbol.dispose](){d.dispose()}};return d}exports.createLocalSource=r;
2
2
  //# sourceMappingURL=localSource.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"localSource.cjs","names":[],"sources":["../src/localSource.ts"],"sourcesContent":["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"],"mappings":"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"}
1
+ {"version":3,"file":"localSource.cjs","names":[],"sources":["../src/localSource.ts"],"sourcesContent":["import type {\n LocalQuery,\n LocalQueryPatch,\n LocalSource,\n LocalSourceConfig,\n PagePagination,\n SourceSnapshot,\n} from './types';\n\nimport { createPagePagination, positiveInteger, sameQuery, totalItems } from './pagination';\nimport { createSourceStore } from './sourceStore';\n\nconst normalizeQuery = (current: LocalQuery, patch: LocalQueryPatch = {}, maxPage?: number): LocalQuery => {\n const page = patch.page ?? current.page;\n const pageSize = patch.pageSize ?? current.pageSize;\n const search = patch.search ?? current.search;\n const resetsPage = patch.pageSize !== undefined || patch.search !== undefined;\n\n const requestedPage = patch.page ?? (resetsPage ? 1 : page);\n\n return {\n page:\n maxPage === undefined\n ? positiveInteger(requestedPage, 'page')\n : Math.min(positiveInteger(requestedPage, 'page'), maxPage),\n pageSize: positiveInteger(pageSize, 'pageSize'),\n search,\n };\n};\n\n/** Local sources own only search and pagination; callers prepare filtering and ranking. */\nexport function createLocalSource<T>(data: readonly T[], config: LocalSourceConfig<T> = {}): LocalSource<T> {\n let allData = data;\n let query = normalizeQuery({ page: 1, pageSize: 20, search: '' }, config.initialQuery);\n\n const buildSnapshot = (): SourceSnapshot<T, LocalQuery, PagePagination> => {\n const matched =\n query.search.length > 0 && config.match ? allData.filter((item) => config.match!(item, query.search)) : allData;\n const total = totalItems(matched.length);\n const pagination = createPagePagination(query.page, query.pageSize, total);\n const start = (pagination.index - 1) * pagination.size;\n\n query = { ...query, page: pagination.index };\n\n return {\n data: matched.slice(start, start + pagination.size),\n error: null,\n isFetching: false,\n pagination,\n query,\n };\n };\n\n const store = createSourceStore(buildSnapshot());\n\n const commit = (): boolean => {\n const next = buildSnapshot();\n\n if (\n sameQuery(next.query, store.value.query) &&\n next.data === store.value.data &&\n next.pagination === store.value.pagination\n ) {\n return false;\n }\n\n store.set(next);\n\n return true;\n };\n\n const setQuery = (patch: LocalQueryPatch): boolean => {\n const next = normalizeQuery(query, patch, store.value.pagination.count);\n\n if (sameQuery(next, query)) return false;\n\n query = next;\n\n return commit();\n };\n\n const source: LocalSource<T> = {\n get disposalSignal() {\n return store.disposalSignal;\n },\n\n dispose: store.dispose,\n\n get disposed() {\n return store.disposed;\n },\n\n page: {\n go(index) {\n return setQuery({ page: index });\n },\n\n last() {\n return setQuery({ page: store.value.pagination.count });\n },\n\n next() {\n return store.value.pagination.hasNext && setQuery({ page: store.value.pagination.index + 1 });\n },\n\n previous() {\n return store.value.pagination.hasPrevious && setQuery({ page: store.value.pagination.index - 1 });\n },\n },\n\n setData(nextData) {\n if (nextData === allData) return false;\n\n allData = nextData;\n\n return commit();\n },\n\n setQuery,\n\n get snapshot() {\n return store.value;\n },\n\n subscribe: store.subscribe,\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n"],"mappings":"mEAYA,IAAM,GAAkB,EAAqB,EAAyB,CAAC,EAAG,IAAiC,CACzG,IAAM,EAAO,EAAM,MAAQ,EAAQ,KAC7B,EAAW,EAAM,UAAY,EAAQ,SACrC,EAAS,EAAM,QAAU,EAAQ,OACjC,EAAa,EAAM,WAAa,IAAA,IAAa,EAAM,SAAW,IAAA,GAE9D,EAAgB,EAAM,OAAS,EAAa,EAAI,GAEtD,MAAO,CACL,KACE,IAAY,IAAA,GACR,EAAA,gBAAgB,EAAe,MAAM,EACrC,KAAK,IAAI,EAAA,gBAAgB,EAAe,MAAM,EAAG,CAAO,EAC9D,SAAU,EAAA,gBAAgB,EAAU,UAAU,EAC9C,QACF,CACF,EAGA,SAAgB,EAAqB,EAAoB,EAA+B,CAAC,EAAmB,CAC1G,IAAI,EAAU,EACV,EAAQ,EAAe,CAAE,KAAM,EAAG,SAAU,GAAI,OAAQ,EAAG,EAAG,EAAO,YAAY,EAE/E,MAAqE,CACzE,IAAM,EACJ,EAAM,OAAO,OAAS,GAAK,EAAO,MAAQ,EAAQ,OAAQ,GAAS,EAAO,MAAO,EAAM,EAAM,MAAM,CAAC,EAAI,EACpG,EAAQ,EAAA,WAAW,EAAQ,MAAM,EACjC,EAAa,EAAA,qBAAqB,EAAM,KAAM,EAAM,SAAU,CAAK,EACnE,GAAS,EAAW,MAAQ,GAAK,EAAW,KAIlD,MAFA,GAAQ,CAAE,GAAG,EAAO,KAAM,EAAW,KAAM,EAEpC,CACL,KAAM,EAAQ,MAAM,EAAO,EAAQ,EAAW,IAAI,EAClD,MAAO,KACP,WAAY,GACZ,aACA,OACF,CACF,EAEM,EAAQ,EAAA,kBAAkB,EAAc,CAAC,EAEzC,MAAwB,CAC5B,IAAM,EAAO,EAAc,EAY3B,OATE,EAAA,UAAU,EAAK,MAAO,EAAM,MAAM,KAAK,GACvC,EAAK,OAAS,EAAM,MAAM,MAC1B,EAAK,aAAe,EAAM,MAAM,WAEzB,IAGT,EAAM,IAAI,CAAI,EAEP,GACT,EAEM,EAAY,GAAoC,CACpD,IAAM,EAAO,EAAe,EAAO,EAAO,EAAM,MAAM,WAAW,KAAK,EAMtE,MAJA,CAAI,EAAA,UAAU,EAAM,CAAK,IAEzB,EAAQ,EAED,EAAO,EAChB,EAEM,EAAyB,CAC7B,IAAI,gBAAiB,CACnB,OAAO,EAAM,cACf,EAEA,QAAS,EAAM,QAEf,IAAI,UAAW,CACb,OAAO,EAAM,QACf,EAEA,KAAM,CACJ,GAAG,EAAO,CACR,OAAO,EAAS,CAAE,KAAM,CAAM,CAAC,CACjC,EAEA,MAAO,CACL,OAAO,EAAS,CAAE,KAAM,EAAM,MAAM,WAAW,KAAM,CAAC,CACxD,EAEA,MAAO,CACL,OAAO,EAAM,MAAM,WAAW,SAAW,EAAS,CAAE,KAAM,EAAM,MAAM,WAAW,MAAQ,CAAE,CAAC,CAC9F,EAEA,UAAW,CACT,OAAO,EAAM,MAAM,WAAW,aAAe,EAAS,CAAE,KAAM,EAAM,MAAM,WAAW,MAAQ,CAAE,CAAC,CAClG,CACF,EAEA,QAAQ,EAAU,CAKhB,OAJI,IAAa,IAEjB,EAAU,EAEH,EAAO,EAChB,EAEA,WAEA,IAAI,UAAW,CACb,OAAO,EAAM,KACf,EAEA,UAAW,EAAM,UAEjB,CAAC,OAAO,UAAW,CACjB,EAAO,QAAQ,CACjB,CACF,EAEA,OAAO,CACT"}
@@ -1,24 +1,4 @@
1
1
  import type { LocalSource, LocalSourceConfig } from './types';
2
- /**
3
- * Creates an in-memory paginated source with optional async filter/sort pipelines.
4
- *
5
- * When neither `filterAsync` nor `sortAsync` are configured, all operations
6
- * complete synchronously — there is no loading state and `ready()` resolves immediately.
7
- * Provide async ops to offload heavy computation (e.g. to a Web Worker).
8
- *
9
- * @example
10
- * ```ts
11
- * // Synchronous (common case)
12
- * const source = createLocalSource([1, 2, 3, 4, 5], { limit: 2 });
13
- * await source.goTo(2);
14
- * console.log(source.current); // [3, 4]
15
- *
16
- * // Async pipeline (Web Worker offloading)
17
- * const source = createLocalSource(users, {
18
- * filterAsync: (items, signal) => myWorker.filter(items, signal),
19
- * sortAsync: (items, signal) => myWorker.sort(items, signal),
20
- * });
21
- * ```
22
- */
23
- export declare function createLocalSource<T>(data: readonly T[], cfg?: LocalSourceConfig<T>): LocalSource<T>;
2
+ /** Local sources own only search and pagination; callers prepare filtering and ranking. */
3
+ export declare function createLocalSource<T>(data: readonly T[], config?: LocalSourceConfig<T>): LocalSource<T>;
24
4
  //# sourceMappingURL=localSource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"localSource.d.ts","sourceRoot":"","sources":["../src/localSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAgD,MAAM,SAAS,CAAC;AAkB5G;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,GAAE,iBAAiB,CAAC,CAAC,CAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CA6TvG"}
1
+ {"version":3,"file":"localSource.d.ts","sourceRoot":"","sources":["../src/localSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,WAAW,EACX,iBAAiB,EAGlB,MAAM,SAAS,CAAC;AAuBjB,2FAA2F;AAC3F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,GAAE,iBAAiB,CAAC,CAAC,CAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAqG1G"}
@@ -1,130 +1,74 @@
1
- import { SourcererError as e } from "./errors.js";
2
- import { extractError as t } from "./_utils.js";
3
- import { createSourceCore as n } from "./core.js";
4
- import { clampPage as r, createMeta as i, pageCount as a } from "./pagination.js";
1
+ import { createSourceStore as e } from "./sourceStore.js";
2
+ import { createPagePagination as t, positiveInteger as n, sameQuery as r, totalItems as i } from "./pagination.js";
5
3
  //#region src/localSource.ts
6
- var o = 20, s = 300;
7
- function c(e, t) {
8
- let n = t.toLowerCase();
9
- return e.filter((e) => JSON.stringify(e).toLowerCase().includes(n));
10
- }
11
- function l(l, u = {}) {
12
- let d = n(), f = u.filterAsync !== void 0 || u.sortAsync !== void 0, p = u.debounceMs ?? s, m = u.initialData ?? l, h = "", g = u.filter, _ = u.sort, v = u.limit ?? o, y = 1, b = [], x = !1, S = !1, C = null, w = null, T = null, E = () => d.notify(), D = () => {
13
- T &&= (T.resolve(), null);
14
- }, O = () => {
15
- let e = m;
16
- return h && (e = (u.searchFn ?? c)(e, h)), g && (e = e.filter(g)), _ && (e = [...e].sort(_)), e;
17
- }, k = () => {
18
- b = O();
19
- let e = a(b.length, v);
20
- y = r(y, e);
21
- }, A = async () => {
22
- w?.abort();
23
- let n = new AbortController();
24
- w = n, x = !0, C = null, E();
25
- try {
26
- let e = m;
27
- if (h && (e = (u.searchFn ?? c)(e, h)), g && (e = e.filter(g)), u.filterAsync && !n.signal.aborted && (e = await u.filterAsync(e, n.signal)), _ && (e = [...e].sort(_)), u.sortAsync && !n.signal.aborted && (e = await u.sortAsync(e, n.signal)), n.signal.aborted) return;
28
- b = e, x = !1, S = !1;
29
- let t = a(b.length, v);
30
- y = r(y, t);
31
- } catch (r) {
32
- n.signal.aborted || (x = !1, S = !1, C = new e(t(r), { cause: r }));
33
- } finally {
34
- w === n && (w = null);
35
- }
36
- E(), D();
37
- }, j = () => f ? A() : (S = !1, k(), E(), D(), Promise.resolve());
38
- f ? (b = O(), A()) : k();
39
- let M = {
40
- get current() {
41
- let e = (y - 1) * v;
42
- return b.slice(e, e + v);
43
- },
4
+ var a = (e, t = {}, r) => {
5
+ let i = t.page ?? e.page, a = t.pageSize ?? e.pageSize, o = t.search ?? e.search, s = t.pageSize !== void 0 || t.search !== void 0, c = t.page ?? (s ? 1 : i);
6
+ return {
7
+ page: r === void 0 ? n(c, "page") : Math.min(n(c, "page"), r),
8
+ pageSize: n(a, "pageSize"),
9
+ search: o
10
+ };
11
+ };
12
+ function o(n, o = {}) {
13
+ let s = n, c = a({
14
+ page: 1,
15
+ pageSize: 20,
16
+ search: ""
17
+ }, o.initialQuery), l = () => {
18
+ let e = c.search.length > 0 && o.match ? s.filter((e) => o.match(e, c.search)) : s, n = i(e.length), r = t(c.page, c.pageSize, n), a = (r.index - 1) * r.size;
19
+ return c = {
20
+ ...c,
21
+ page: r.index
22
+ }, {
23
+ data: e.slice(a, a + r.size),
24
+ error: null,
25
+ isFetching: !1,
26
+ pagination: r,
27
+ query: c
28
+ };
29
+ }, u = e(l()), d = () => {
30
+ let e = l();
31
+ return r(e.query, u.value.query) && e.data === u.value.data && e.pagination === u.value.pagination ? !1 : (u.set(e), !0);
32
+ }, f = (e) => {
33
+ let t = a(c, e, u.value.pagination.count);
34
+ return !r(t, c) && (c = t, d());
35
+ }, p = {
44
36
  get disposalSignal() {
45
- return d.disposalSignal;
46
- },
47
- dispose() {
48
- d.isDisposed || (d.cancelTimer(), w?.abort(), w = null, D(), d.dispose());
37
+ return u.disposalSignal;
49
38
  },
39
+ dispose: u.dispose,
50
40
  get disposed() {
51
- return d.isDisposed;
52
- },
53
- goTo(e) {
54
- let t = r(e, a(b.length, v));
55
- return t === y ? Promise.resolve() : (y = t, E(), Promise.resolve());
56
- },
57
- goToLast() {
58
- return M.goTo(a(b.length, v));
59
- },
60
- get meta() {
61
- return i({
62
- error: C,
63
- isLoading: x,
64
- isSearchPending: S,
65
- pageNumber: y,
66
- pageSize: v,
67
- totalItems: b.length
68
- });
69
- },
70
- next() {
71
- return M.goTo(y + 1);
72
- },
73
- patch(e) {
74
- let t = !1;
75
- if (e.limit !== void 0) {
76
- let n = Math.max(1, Math.trunc(e.limit));
77
- n !== v && (v = n, t = !0);
41
+ return u.disposed;
42
+ },
43
+ page: {
44
+ go(e) {
45
+ return f({ page: e });
46
+ },
47
+ last() {
48
+ return f({ page: u.value.pagination.count });
49
+ },
50
+ next() {
51
+ return u.value.pagination.hasNext && f({ page: u.value.pagination.index + 1 });
52
+ },
53
+ previous() {
54
+ return u.value.pagination.hasPrevious && f({ page: u.value.pagination.index - 1 });
78
55
  }
79
- if ("filter" in e && (g = e.filter, t = !0), "sort" in e && (_ = e.sort, t = !0), "search" in e && (e.search !== h || d.isScheduled) && (h = e.search ?? "", t = !0), e.page !== void 0) {
80
- let n = r(e.page, a(b.length, v));
81
- n !== y && (y = n, t = !0);
82
- }
83
- return t ? (d.cancelTimer(), D(), (e.limit !== void 0 || "filter" in e || "sort" in e || "search" in e) && e.page === void 0 && (y = 1), j()) : Promise.resolve();
84
- },
85
- prev() {
86
- return M.goTo(y - 1);
87
- },
88
- get query() {
89
- return {
90
- limit: v,
91
- page: y,
92
- ...h && { search: h }
93
- };
94
- },
95
- ready(e) {
96
- return d.ready(() => !x && !d.isScheduled, e);
97
- },
98
- reset() {
99
- return d.cancelTimer(), S = !1, h = "", g = u.filter, _ = u.sort, y = 1, D(), j();
100
- },
101
- search(e, t) {
102
- if (t?.immediate) return e === h && !d.isScheduled ? Promise.resolve() : (d.cancelTimer(), S = !1, h = e, y = 1, D(), j());
103
- if (e === h) return Promise.resolve();
104
- D(), h = e, y = 1, S = !0, E();
105
- let n, r = new Promise((e) => {
106
- n = e;
107
- });
108
- return T = {
109
- promise: r,
110
- resolve: n
111
- }, d.schedule(() => {
112
- S = !1, j();
113
- }, p), r;
114
56
  },
115
57
  setData(e) {
116
- return m = e, y = 1, j();
58
+ return e !== s && (s = e, d());
117
59
  },
118
- subscribe(e) {
119
- return d.subscribe(e);
60
+ setQuery: f,
61
+ get snapshot() {
62
+ return u.value;
120
63
  },
64
+ subscribe: u.subscribe,
121
65
  [Symbol.dispose]() {
122
- M.dispose();
66
+ p.dispose();
123
67
  }
124
68
  };
125
- return M;
69
+ return p;
126
70
  }
127
71
  //#endregion
128
- export { l as createLocalSource };
72
+ export { o as createLocalSource };
129
73
 
130
74
  //# sourceMappingURL=localSource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localSource.js","names":[],"sources":["../src/localSource.ts"],"sourcesContent":["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"],"mappings":"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"}
1
+ {"version":3,"file":"localSource.js","names":[],"sources":["../src/localSource.ts"],"sourcesContent":["import type {\n LocalQuery,\n LocalQueryPatch,\n LocalSource,\n LocalSourceConfig,\n PagePagination,\n SourceSnapshot,\n} from './types';\n\nimport { createPagePagination, positiveInteger, sameQuery, totalItems } from './pagination';\nimport { createSourceStore } from './sourceStore';\n\nconst normalizeQuery = (current: LocalQuery, patch: LocalQueryPatch = {}, maxPage?: number): LocalQuery => {\n const page = patch.page ?? current.page;\n const pageSize = patch.pageSize ?? current.pageSize;\n const search = patch.search ?? current.search;\n const resetsPage = patch.pageSize !== undefined || patch.search !== undefined;\n\n const requestedPage = patch.page ?? (resetsPage ? 1 : page);\n\n return {\n page:\n maxPage === undefined\n ? positiveInteger(requestedPage, 'page')\n : Math.min(positiveInteger(requestedPage, 'page'), maxPage),\n pageSize: positiveInteger(pageSize, 'pageSize'),\n search,\n };\n};\n\n/** Local sources own only search and pagination; callers prepare filtering and ranking. */\nexport function createLocalSource<T>(data: readonly T[], config: LocalSourceConfig<T> = {}): LocalSource<T> {\n let allData = data;\n let query = normalizeQuery({ page: 1, pageSize: 20, search: '' }, config.initialQuery);\n\n const buildSnapshot = (): SourceSnapshot<T, LocalQuery, PagePagination> => {\n const matched =\n query.search.length > 0 && config.match ? allData.filter((item) => config.match!(item, query.search)) : allData;\n const total = totalItems(matched.length);\n const pagination = createPagePagination(query.page, query.pageSize, total);\n const start = (pagination.index - 1) * pagination.size;\n\n query = { ...query, page: pagination.index };\n\n return {\n data: matched.slice(start, start + pagination.size),\n error: null,\n isFetching: false,\n pagination,\n query,\n };\n };\n\n const store = createSourceStore(buildSnapshot());\n\n const commit = (): boolean => {\n const next = buildSnapshot();\n\n if (\n sameQuery(next.query, store.value.query) &&\n next.data === store.value.data &&\n next.pagination === store.value.pagination\n ) {\n return false;\n }\n\n store.set(next);\n\n return true;\n };\n\n const setQuery = (patch: LocalQueryPatch): boolean => {\n const next = normalizeQuery(query, patch, store.value.pagination.count);\n\n if (sameQuery(next, query)) return false;\n\n query = next;\n\n return commit();\n };\n\n const source: LocalSource<T> = {\n get disposalSignal() {\n return store.disposalSignal;\n },\n\n dispose: store.dispose,\n\n get disposed() {\n return store.disposed;\n },\n\n page: {\n go(index) {\n return setQuery({ page: index });\n },\n\n last() {\n return setQuery({ page: store.value.pagination.count });\n },\n\n next() {\n return store.value.pagination.hasNext && setQuery({ page: store.value.pagination.index + 1 });\n },\n\n previous() {\n return store.value.pagination.hasPrevious && setQuery({ page: store.value.pagination.index - 1 });\n },\n },\n\n setData(nextData) {\n if (nextData === allData) return false;\n\n allData = nextData;\n\n return commit();\n },\n\n setQuery,\n\n get snapshot() {\n return store.value;\n },\n\n subscribe: store.subscribe,\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n"],"mappings":";;;AAYA,IAAM,KAAkB,GAAqB,IAAyB,CAAC,GAAG,MAAiC;CACzG,IAAM,IAAO,EAAM,QAAQ,EAAQ,MAC7B,IAAW,EAAM,YAAY,EAAQ,UACrC,IAAS,EAAM,UAAU,EAAQ,QACjC,IAAa,EAAM,aAAa,KAAA,KAAa,EAAM,WAAW,KAAA,GAE9D,IAAgB,EAAM,SAAS,IAAa,IAAI;CAEtD,OAAO;EACL,MACE,MAAY,KAAA,IACR,EAAgB,GAAe,MAAM,IACrC,KAAK,IAAI,EAAgB,GAAe,MAAM,GAAG,CAAO;EAC9D,UAAU,EAAgB,GAAU,UAAU;EAC9C;CACF;AACF;AAGA,SAAgB,EAAqB,GAAoB,IAA+B,CAAC,GAAmB;CAC1G,IAAI,IAAU,GACV,IAAQ,EAAe;EAAE,MAAM;EAAG,UAAU;EAAI,QAAQ;CAAG,GAAG,EAAO,YAAY,GAE/E,UAAqE;EACzE,IAAM,IACJ,EAAM,OAAO,SAAS,KAAK,EAAO,QAAQ,EAAQ,QAAQ,MAAS,EAAO,MAAO,GAAM,EAAM,MAAM,CAAC,IAAI,GACpG,IAAQ,EAAW,EAAQ,MAAM,GACjC,IAAa,EAAqB,EAAM,MAAM,EAAM,UAAU,CAAK,GACnE,KAAS,EAAW,QAAQ,KAAK,EAAW;EAIlD,OAFA,IAAQ;GAAE,GAAG;GAAO,MAAM,EAAW;EAAM,GAEpC;GACL,MAAM,EAAQ,MAAM,GAAO,IAAQ,EAAW,IAAI;GAClD,OAAO;GACP,YAAY;GACZ;GACA;EACF;CACF,GAEM,IAAQ,EAAkB,EAAc,CAAC,GAEzC,UAAwB;EAC5B,IAAM,IAAO,EAAc;EAY3B,OATE,EAAU,EAAK,OAAO,EAAM,MAAM,KAAK,KACvC,EAAK,SAAS,EAAM,MAAM,QAC1B,EAAK,eAAe,EAAM,MAAM,aAEzB,MAGT,EAAM,IAAI,CAAI,GAEP;CACT,GAEM,KAAY,MAAoC;EACpD,IAAM,IAAO,EAAe,GAAO,GAAO,EAAM,MAAM,WAAW,KAAK;EAMtE,OAJA,CAAI,EAAU,GAAM,CAAK,MAEzB,IAAQ,GAED,EAAO;CAChB,GAEM,IAAyB;EAC7B,IAAI,iBAAiB;GACnB,OAAO,EAAM;EACf;EAEA,SAAS,EAAM;EAEf,IAAI,WAAW;GACb,OAAO,EAAM;EACf;EAEA,MAAM;GACJ,GAAG,GAAO;IACR,OAAO,EAAS,EAAE,MAAM,EAAM,CAAC;GACjC;GAEA,OAAO;IACL,OAAO,EAAS,EAAE,MAAM,EAAM,MAAM,WAAW,MAAM,CAAC;GACxD;GAEA,OAAO;IACL,OAAO,EAAM,MAAM,WAAW,WAAW,EAAS,EAAE,MAAM,EAAM,MAAM,WAAW,QAAQ,EAAE,CAAC;GAC9F;GAEA,WAAW;IACT,OAAO,EAAM,MAAM,WAAW,eAAe,EAAS,EAAE,MAAM,EAAM,MAAM,WAAW,QAAQ,EAAE,CAAC;GAClG;EACF;EAEA,QAAQ,GAAU;GAKhB,OAJI,MAAa,MAEjB,IAAU,GAEH,EAAO;EAChB;EAEA;EAEA,IAAI,WAAW;GACb,OAAO,EAAM;EACf;EAEA,WAAW,EAAM;EAEjB,CAAC,OAAO,WAAW;GACjB,EAAO,QAAQ;EACjB;CACF;CAEA,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ const e=require("./_dev.cjs"),t=require("./asyncSource.cjs"),n=require("./pagination.cjs");var r=(e,t={},r)=>{let i=t.page??e.page,a=t.pageSize??e.pageSize,o=t.search??e.search,s=`filter`in t||t.pageSize!==void 0||t.search!==void 0||`sort`in t,c=t.page??(s?1:i);return{...`filter`in t?t.filter!==void 0&&{filter:t.filter}:e.filter!==void 0&&{filter:e.filter},...`sort`in t?t.sort!==void 0&&{sort:t.sort}:e.sort!==void 0&&{sort:e.sort},page:r===void 0?n.positiveInteger(c,`page`):Math.min(n.positiveInteger(c,`page`),r),pageSize:n.positiveInteger(a,`pageSize`),search:o}};function i(i){let a=r({page:1,pageSize:20,search:``},i.initialQuery),o=t.createAsyncSource({data:[],error:null,isFetching:!1,pagination:n.createPagePagination(a.page,a.pageSize,0),query:a}),s=e=>o.fetch({failure:(e,t)=>({...e,error:t,isFetching:!1,pendingQuery:void 0}),load:t=>i.load({query:e,signal:t}),pending:t=>({...t,error:null,isFetching:!0,pendingQuery:e}),success:t=>{let r=n.totalItems(t.total),i=n.createPagePagination(e.page,e.pageSize,r),o={...e,page:i.index};return a=o,{data:t.data,error:null,isFetching:!1,pagination:i,query:o}}}),c=()=>s(a),l=async e=>{let t=r(o.snapshot.pendingQuery??o.snapshot.query,e,o.snapshot.pagination.total>0?o.snapshot.pagination.count:void 0);n.sameQuery(t,a)||(a=t,await s(t))},u={get disposalSignal(){return o.disposalSignal},dispose:o.dispose,get disposed(){return o.disposed},page:{go(e){return o.snapshot.isFetching?Promise.resolve():l({page:e})},last(){return o.snapshot.isFetching?Promise.resolve():l({page:o.snapshot.pagination.count})},next(){return o.snapshot.isFetching||!o.snapshot.pagination.hasNext?Promise.resolve():l({page:o.snapshot.pagination.index+1})},previous(){return o.snapshot.isFetching||!o.snapshot.pagination.hasPrevious?Promise.resolve():l({page:o.snapshot.pagination.index-1})}},reload:c,setQuery:l,get snapshot(){return o.snapshot},subscribe:o.subscribe,[Symbol.dispose](){u.dispose()}};return i.autoStart!==!1&&c().catch(()=>e.warn(`Initial load failed. Inspect source.snapshot.error.`)),u}exports.createPageSource=i;
2
+ //# sourceMappingURL=pageSource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageSource.cjs","names":[],"sources":["../src/pageSource.ts"],"sourcesContent":["import type { PagePagination, PageQuery, PageQueryPatch, PageSource, PageSourceConfig } from './types';\n\nimport { warn } from './_dev';\nimport { createAsyncSource } from './asyncSource';\nimport { createPagePagination, positiveInteger, sameQuery, totalItems } from './pagination';\n\nconst normalizeQuery = <TFilter, TSort>(\n current: PageQuery<TFilter, TSort>,\n patch: PageQueryPatch<TFilter, TSort> = {},\n maxPage?: number,\n): PageQuery<TFilter, TSort> => {\n const page = patch.page ?? current.page;\n const pageSize = patch.pageSize ?? current.pageSize;\n const search = patch.search ?? current.search;\n const resetsPage = 'filter' in patch || patch.pageSize !== undefined || patch.search !== undefined || 'sort' in patch;\n\n const requestedPage = patch.page ?? (resetsPage ? 1 : page);\n\n return {\n ...('filter' in patch\n ? patch.filter !== undefined && { filter: patch.filter }\n : current.filter !== undefined && { filter: current.filter }),\n ...('sort' in patch\n ? patch.sort !== undefined && { sort: patch.sort }\n : current.sort !== undefined && { sort: current.sort }),\n page:\n maxPage === undefined\n ? positiveInteger(requestedPage, 'page')\n : Math.min(positiveInteger(requestedPage, 'page'), maxPage),\n pageSize: positiveInteger(pageSize, 'pageSize'),\n search,\n };\n};\n\n/** Page sources retain loaded state while pendingQuery records newer work. */\nexport function createPageSource<T, TFilter = unknown, TSort = unknown>(\n config: PageSourceConfig<T, TFilter, TSort>,\n): PageSource<T, TFilter, TSort> {\n const initialQuery: PageQuery<TFilter, TSort> = {\n page: 1,\n pageSize: 20,\n search: '',\n };\n let requestedQuery = normalizeQuery(initialQuery, config.initialQuery);\n const asyncSource = createAsyncSource<T, PageQuery<TFilter, TSort>, PagePagination>({\n data: [],\n error: null,\n isFetching: false,\n pagination: createPagePagination(requestedQuery.page, requestedQuery.pageSize, 0),\n query: requestedQuery,\n });\n\n const fetch = (query: PageQuery<TFilter, TSort>): Promise<void> =>\n asyncSource.fetch({\n failure: (previous, error) => ({ ...previous, error, isFetching: false, pendingQuery: undefined }),\n load: (signal) => config.load({ query, signal }),\n pending: (previous) => ({ ...previous, error: null, isFetching: true, pendingQuery: query }),\n success: (result) => {\n const total = totalItems(result.total);\n const pagination = createPagePagination(query.page, query.pageSize, total);\n const loadedQuery = { ...query, page: pagination.index };\n\n requestedQuery = loadedQuery;\n\n return {\n data: result.data,\n error: null,\n isFetching: false,\n pagination,\n query: loadedQuery,\n };\n },\n });\n\n const reload = (): Promise<void> => fetch(requestedQuery);\n\n const setQuery = async (patch: PageQueryPatch<TFilter, TSort>): Promise<void> => {\n const current = asyncSource.snapshot.pendingQuery ?? asyncSource.snapshot.query;\n const next = normalizeQuery(\n current,\n patch,\n asyncSource.snapshot.pagination.total > 0 ? asyncSource.snapshot.pagination.count : undefined,\n );\n\n if (sameQuery(next, requestedQuery)) return;\n\n requestedQuery = next;\n await fetch(next);\n };\n\n const source: PageSource<T, TFilter, TSort> = {\n get disposalSignal() {\n return asyncSource.disposalSignal;\n },\n\n dispose: asyncSource.dispose,\n\n get disposed() {\n return asyncSource.disposed;\n },\n\n page: {\n go(index) {\n return asyncSource.snapshot.isFetching ? Promise.resolve() : setQuery({ page: index });\n },\n\n last() {\n return asyncSource.snapshot.isFetching\n ? Promise.resolve()\n : setQuery({ page: asyncSource.snapshot.pagination.count });\n },\n\n next() {\n return asyncSource.snapshot.isFetching || !asyncSource.snapshot.pagination.hasNext\n ? Promise.resolve()\n : setQuery({ page: asyncSource.snapshot.pagination.index + 1 });\n },\n\n previous() {\n return asyncSource.snapshot.isFetching || !asyncSource.snapshot.pagination.hasPrevious\n ? Promise.resolve()\n : setQuery({ page: asyncSource.snapshot.pagination.index - 1 });\n },\n },\n\n reload,\n setQuery,\n\n get snapshot() {\n return asyncSource.snapshot;\n },\n\n subscribe: asyncSource.subscribe,\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n if (config.autoStart !== false)\n void reload().catch(() => warn('Initial load failed. Inspect source.snapshot.error.'));\n\n return source;\n}\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PageSource, PageSourceConfig } from './types';
2
+ /** Page sources retain loaded state while pendingQuery records newer work. */
3
+ export declare function createPageSource<T, TFilter = unknown, TSort = unknown>(config: PageSourceConfig<T, TFilter, TSort>): PageSource<T, TFilter, TSort>;
4
+ //# sourceMappingURL=pageSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageSource.d.ts","sourceRoot":"","sources":["../src/pageSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6C,UAAU,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAkCvG,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACpE,MAAM,EAAE,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,GAC1C,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CA0G/B"}
@@ -0,0 +1,96 @@
1
+ import { warn as e } from "./_dev.js";
2
+ import { createAsyncSource as t } from "./asyncSource.js";
3
+ import { createPagePagination as n, positiveInteger as r, sameQuery as i, totalItems as a } from "./pagination.js";
4
+ //#region src/pageSource.ts
5
+ var o = (e, t = {}, n) => {
6
+ let i = t.page ?? e.page, a = t.pageSize ?? e.pageSize, o = t.search ?? e.search, s = "filter" in t || t.pageSize !== void 0 || t.search !== void 0 || "sort" in t, c = t.page ?? (s ? 1 : i);
7
+ return {
8
+ ..."filter" in t ? t.filter !== void 0 && { filter: t.filter } : e.filter !== void 0 && { filter: e.filter },
9
+ ..."sort" in t ? t.sort !== void 0 && { sort: t.sort } : e.sort !== void 0 && { sort: e.sort },
10
+ page: n === void 0 ? r(c, "page") : Math.min(r(c, "page"), n),
11
+ pageSize: r(a, "pageSize"),
12
+ search: o
13
+ };
14
+ };
15
+ function s(r) {
16
+ let s = o({
17
+ page: 1,
18
+ pageSize: 20,
19
+ search: ""
20
+ }, r.initialQuery), c = t({
21
+ data: [],
22
+ error: null,
23
+ isFetching: !1,
24
+ pagination: n(s.page, s.pageSize, 0),
25
+ query: s
26
+ }), l = (e) => c.fetch({
27
+ failure: (e, t) => ({
28
+ ...e,
29
+ error: t,
30
+ isFetching: !1,
31
+ pendingQuery: void 0
32
+ }),
33
+ load: (t) => r.load({
34
+ query: e,
35
+ signal: t
36
+ }),
37
+ pending: (t) => ({
38
+ ...t,
39
+ error: null,
40
+ isFetching: !0,
41
+ pendingQuery: e
42
+ }),
43
+ success: (t) => {
44
+ let r = a(t.total), i = n(e.page, e.pageSize, r), o = {
45
+ ...e,
46
+ page: i.index
47
+ };
48
+ return s = o, {
49
+ data: t.data,
50
+ error: null,
51
+ isFetching: !1,
52
+ pagination: i,
53
+ query: o
54
+ };
55
+ }
56
+ }), u = () => l(s), d = async (e) => {
57
+ let t = o(c.snapshot.pendingQuery ?? c.snapshot.query, e, c.snapshot.pagination.total > 0 ? c.snapshot.pagination.count : void 0);
58
+ i(t, s) || (s = t, await l(t));
59
+ }, f = {
60
+ get disposalSignal() {
61
+ return c.disposalSignal;
62
+ },
63
+ dispose: c.dispose,
64
+ get disposed() {
65
+ return c.disposed;
66
+ },
67
+ page: {
68
+ go(e) {
69
+ return c.snapshot.isFetching ? Promise.resolve() : d({ page: e });
70
+ },
71
+ last() {
72
+ return c.snapshot.isFetching ? Promise.resolve() : d({ page: c.snapshot.pagination.count });
73
+ },
74
+ next() {
75
+ return c.snapshot.isFetching || !c.snapshot.pagination.hasNext ? Promise.resolve() : d({ page: c.snapshot.pagination.index + 1 });
76
+ },
77
+ previous() {
78
+ return c.snapshot.isFetching || !c.snapshot.pagination.hasPrevious ? Promise.resolve() : d({ page: c.snapshot.pagination.index - 1 });
79
+ }
80
+ },
81
+ reload: u,
82
+ setQuery: d,
83
+ get snapshot() {
84
+ return c.snapshot;
85
+ },
86
+ subscribe: c.subscribe,
87
+ [Symbol.dispose]() {
88
+ f.dispose();
89
+ }
90
+ };
91
+ return r.autoStart !== !1 && u().catch(() => /* @__PURE__ */ e("Initial load failed. Inspect source.snapshot.error.")), f;
92
+ }
93
+ //#endregion
94
+ export { s as createPageSource };
95
+
96
+ //# sourceMappingURL=pageSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageSource.js","names":[],"sources":["../src/pageSource.ts"],"sourcesContent":["import type { PagePagination, PageQuery, PageQueryPatch, PageSource, PageSourceConfig } from './types';\n\nimport { warn } from './_dev';\nimport { createAsyncSource } from './asyncSource';\nimport { createPagePagination, positiveInteger, sameQuery, totalItems } from './pagination';\n\nconst normalizeQuery = <TFilter, TSort>(\n current: PageQuery<TFilter, TSort>,\n patch: PageQueryPatch<TFilter, TSort> = {},\n maxPage?: number,\n): PageQuery<TFilter, TSort> => {\n const page = patch.page ?? current.page;\n const pageSize = patch.pageSize ?? current.pageSize;\n const search = patch.search ?? current.search;\n const resetsPage = 'filter' in patch || patch.pageSize !== undefined || patch.search !== undefined || 'sort' in patch;\n\n const requestedPage = patch.page ?? (resetsPage ? 1 : page);\n\n return {\n ...('filter' in patch\n ? patch.filter !== undefined && { filter: patch.filter }\n : current.filter !== undefined && { filter: current.filter }),\n ...('sort' in patch\n ? patch.sort !== undefined && { sort: patch.sort }\n : current.sort !== undefined && { sort: current.sort }),\n page:\n maxPage === undefined\n ? positiveInteger(requestedPage, 'page')\n : Math.min(positiveInteger(requestedPage, 'page'), maxPage),\n pageSize: positiveInteger(pageSize, 'pageSize'),\n search,\n };\n};\n\n/** Page sources retain loaded state while pendingQuery records newer work. */\nexport function createPageSource<T, TFilter = unknown, TSort = unknown>(\n config: PageSourceConfig<T, TFilter, TSort>,\n): PageSource<T, TFilter, TSort> {\n const initialQuery: PageQuery<TFilter, TSort> = {\n page: 1,\n pageSize: 20,\n search: '',\n };\n let requestedQuery = normalizeQuery(initialQuery, config.initialQuery);\n const asyncSource = createAsyncSource<T, PageQuery<TFilter, TSort>, PagePagination>({\n data: [],\n error: null,\n isFetching: false,\n pagination: createPagePagination(requestedQuery.page, requestedQuery.pageSize, 0),\n query: requestedQuery,\n });\n\n const fetch = (query: PageQuery<TFilter, TSort>): Promise<void> =>\n asyncSource.fetch({\n failure: (previous, error) => ({ ...previous, error, isFetching: false, pendingQuery: undefined }),\n load: (signal) => config.load({ query, signal }),\n pending: (previous) => ({ ...previous, error: null, isFetching: true, pendingQuery: query }),\n success: (result) => {\n const total = totalItems(result.total);\n const pagination = createPagePagination(query.page, query.pageSize, total);\n const loadedQuery = { ...query, page: pagination.index };\n\n requestedQuery = loadedQuery;\n\n return {\n data: result.data,\n error: null,\n isFetching: false,\n pagination,\n query: loadedQuery,\n };\n },\n });\n\n const reload = (): Promise<void> => fetch(requestedQuery);\n\n const setQuery = async (patch: PageQueryPatch<TFilter, TSort>): Promise<void> => {\n const current = asyncSource.snapshot.pendingQuery ?? asyncSource.snapshot.query;\n const next = normalizeQuery(\n current,\n patch,\n asyncSource.snapshot.pagination.total > 0 ? asyncSource.snapshot.pagination.count : undefined,\n );\n\n if (sameQuery(next, requestedQuery)) return;\n\n requestedQuery = next;\n await fetch(next);\n };\n\n const source: PageSource<T, TFilter, TSort> = {\n get disposalSignal() {\n return asyncSource.disposalSignal;\n },\n\n dispose: asyncSource.dispose,\n\n get disposed() {\n return asyncSource.disposed;\n },\n\n page: {\n go(index) {\n return asyncSource.snapshot.isFetching ? Promise.resolve() : setQuery({ page: index });\n },\n\n last() {\n return asyncSource.snapshot.isFetching\n ? Promise.resolve()\n : setQuery({ page: asyncSource.snapshot.pagination.count });\n },\n\n next() {\n return asyncSource.snapshot.isFetching || !asyncSource.snapshot.pagination.hasNext\n ? Promise.resolve()\n : setQuery({ page: asyncSource.snapshot.pagination.index + 1 });\n },\n\n previous() {\n return asyncSource.snapshot.isFetching || !asyncSource.snapshot.pagination.hasPrevious\n ? Promise.resolve()\n : setQuery({ page: asyncSource.snapshot.pagination.index - 1 });\n },\n },\n\n reload,\n setQuery,\n\n get snapshot() {\n return asyncSource.snapshot;\n },\n\n subscribe: asyncSource.subscribe,\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n if (config.autoStart !== false)\n void reload().catch(() => warn('Initial load failed. Inspect source.snapshot.error.'));\n\n return source;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var e=(e,t)=>{let n=Math.trunc(e);return Number.isFinite(n)?Math.max(t,n):t},t=(t,n)=>{let r=e(t,0),i=e(n,1);return Math.max(1,Math.ceil(r/i))},n=(t,n)=>Math.max(1,Math.min(e(t,1),Math.max(1,n))),r=r=>{let i=e(r.pageSize,1),a=e(r.totalItems,0),o=t(a,i),s=n(r.pageNumber,o);return{error:r.error,isLoading:r.isLoading,isSearchPending:r.isSearchPending,pageCount:o,pageNumber:s,pageSize:i,totalItems:a}};function i(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}}exports.clampPage=n,exports.createMeta=r,exports.itemRange=i,exports.pageCount=t;
1
+ function e(e,t){if(!Number.isInteger(e)||e<1)throw RangeError(`${t} must be a positive integer`);return e}function t(e){if(!Number.isInteger(e)||e<0)throw RangeError(`Source loader total must be a non-negative integer`);return e}function n(e,t,n){let r=Math.max(1,Math.ceil(n/t)),i=Math.min(e,r);return{count:r,hasNext:i<r,hasPrevious:i>1,index:i,kind:`page`,size:t,total:n}}function r(e,t){return[...new Set([...Object.keys(e),...Object.keys(t)])].every(n=>Object.is(e[n],t[n]))}exports.createPagePagination=n,exports.positiveInteger=e,exports.sameQuery=r,exports.totalItems=t;
2
2
  //# sourceMappingURL=pagination.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.cjs","names":[],"sources":["../src/pagination.ts"],"sourcesContent":["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"],"mappings":"AAEA,IAAM,GAAY,EAAe,IAAoB,CACnD,IAAM,EAAS,KAAK,MAAM,CAAK,EAM/B,OAJK,OAAO,SAAS,CAAM,EAIpB,KAAK,IAAI,EAAS,CAAM,EAHtB,CAIX,EAEa,GAAa,EAAe,IAAkB,CACzD,IAAM,EAAY,EAAS,EAAO,CAAC,EAC7B,EAAY,EAAS,EAAO,CAAC,EAEnC,OAAO,KAAK,IAAI,EAAG,KAAK,KAAK,EAAY,CAAS,CAAC,CACrD,EAEa,GAAa,EAAc,IAC/B,KAAK,IAAI,EAAG,KAAK,IAAI,EAAS,EAAM,CAAC,EAAG,KAAK,IAAI,EAAG,CAAK,CAAC,CAAC,EAGvD,EAAc,GAOT,CAChB,IAAM,EAAY,EAAS,EAAM,SAAU,CAAC,EACtC,EAAY,EAAS,EAAM,WAAY,CAAC,EACxC,EAAQ,EAAU,EAAW,CAAS,EACtC,EAAO,EAAU,EAAM,WAAY,CAAK,EAE9C,MAAO,CACL,MAAO,EAAM,MACb,UAAW,EAAM,UACjB,gBAAiB,EAAM,gBACvB,UAAW,EACX,WAAY,EACZ,SAAU,EACV,WAAY,CACd,CACF,EAaA,SAAgB,EAAU,EAGxB,CACA,GAAI,EAAK,aAAe,EAAG,MAAO,CAAE,IAAK,EAAG,MAAO,CAAE,EAErD,IAAM,GAAS,EAAK,WAAa,GAAK,EAAK,SAAW,EAEtD,MAAO,CACL,IAAK,KAAK,IAAI,EAAK,WAAa,EAAK,SAAU,EAAK,UAAU,EAC9D,OACF,CACF"}
1
+ {"version":3,"file":"pagination.cjs","names":[],"sources":["../src/pagination.ts"],"sourcesContent":["import type { PagePagination } from './types';\n\nexport function positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) throw new RangeError(`${name} must be a positive integer`);\n\n return value;\n}\n\nexport function totalItems(value: number): number {\n if (!Number.isInteger(value) || value < 0) throw new RangeError('Source loader total must be a non-negative integer');\n\n return value;\n}\n\nexport function createPagePagination(index: number, size: number, total: number): PagePagination {\n const count = Math.max(1, Math.ceil(total / size));\n const safeIndex = Math.min(index, count);\n\n return {\n count,\n hasNext: safeIndex < count,\n hasPrevious: safeIndex > 1,\n index: safeIndex,\n kind: 'page',\n size,\n total,\n };\n}\n\nexport function sameQuery<T extends Record<string, unknown>>(left: T, right: T): boolean {\n const keys = new Set([...Object.keys(left), ...Object.keys(right)]);\n\n return [...keys].every((key) => Object.is(left[key], right[key]));\n}\n"],"mappings":"AAEA,SAAgB,EAAgB,EAAe,EAAsB,CACnE,GAAI,CAAC,OAAO,UAAU,CAAK,GAAK,EAAQ,EAAG,MAAU,WAAW,GAAG,EAAK,4BAA4B,EAEpG,OAAO,CACT,CAEA,SAAgB,EAAW,EAAuB,CAChD,GAAI,CAAC,OAAO,UAAU,CAAK,GAAK,EAAQ,EAAG,MAAU,WAAW,oDAAoD,EAEpH,OAAO,CACT,CAEA,SAAgB,EAAqB,EAAe,EAAc,EAA+B,CAC/F,IAAM,EAAQ,KAAK,IAAI,EAAG,KAAK,KAAK,EAAQ,CAAI,CAAC,EAC3C,EAAY,KAAK,IAAI,EAAO,CAAK,EAEvC,MAAO,CACL,QACA,QAAS,EAAY,EACrB,YAAa,EAAY,EACzB,MAAO,EACP,KAAM,OACN,OACA,OACF,CACF,CAEA,SAAgB,EAA6C,EAAS,EAAmB,CAGvF,MAAO,CAAC,GAAG,IAFM,IAAI,CAAC,GAAG,OAAO,KAAK,CAAI,EAAG,GAAG,OAAO,KAAK,CAAK,CAAC,CAEtD,CAAI,CAAC,CAAC,MAAO,GAAQ,OAAO,GAAG,EAAK,GAAM,EAAM,EAAI,CAAC,CAClE"}
@@ -1,31 +1,6 @@
1
- import type { SourcererError, SourceMeta } from './types';
2
- export declare const pageCount: (total: number, limit: number) => number;
3
- export declare const clampPage: (page: number, pages: number) => number;
4
- export declare const createMeta: (state: {
5
- error: SourcererError | null;
6
- isLoading: boolean;
7
- isSearchPending: boolean;
8
- pageNumber: number;
9
- pageSize: number;
10
- totalItems: number;
11
- }) => SourceMeta;
12
- /**
13
- * Computes the 1-based display range of items on the current page.
14
- * Returns `{ start: 0, end: 0 }` when there are no items.
15
- *
16
- * @example
17
- * ```ts
18
- * const { start, end } = itemRange(source.meta);
19
- * // "Showing 11–20 of 50"
20
- * console.log(`Showing ${start}–${end} of ${source.meta.totalItems}`);
21
- * ```
22
- */
23
- export declare function itemRange(meta: Readonly<{
24
- pageNumber: number;
25
- pageSize: number;
26
- totalItems: number;
27
- }>): {
28
- end: number;
29
- start: number;
30
- };
1
+ import type { PagePagination } from './types';
2
+ export declare function positiveInteger(value: number, name: string): number;
3
+ export declare function totalItems(value: number): number;
4
+ export declare function createPagePagination(index: number, size: number, total: number): PagePagination;
5
+ export declare function sameQuery<T extends Record<string, unknown>>(left: T, right: T): boolean;
31
6
  //# sourceMappingURL=pagination.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAY1D,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,WAKrD,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,WAEpD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,OAAO;IAChC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,KAAG,UAeH,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IACvG,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CASA"}
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAInE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAa/F;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAIvF"}