@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.
- package/README.md +19 -74
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +6 -0
- package/dist/_dev.js.map +1 -0
- package/dist/asyncSource.cjs +1 -1
- package/dist/asyncSource.cjs.map +1 -1
- package/dist/asyncSource.d.ts +14 -58
- package/dist/asyncSource.d.ts.map +1 -1
- package/dist/asyncSource.js +28 -27
- package/dist/asyncSource.js.map +1 -1
- package/dist/cursorSource.cjs +1 -1
- package/dist/cursorSource.cjs.map +1 -1
- package/dist/cursorSource.d.ts +3 -3
- package/dist/cursorSource.d.ts.map +1 -1
- package/dist/cursorSource.js +77 -110
- package/dist/cursorSource.js.map +1 -1
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.ts +6 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +8 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -14
- package/dist/infiniteSource.cjs +1 -1
- package/dist/infiniteSource.cjs.map +1 -1
- package/dist/infiniteSource.d.ts +3 -3
- package/dist/infiniteSource.d.ts.map +1 -1
- package/dist/infiniteSource.js +72 -107
- package/dist/infiniteSource.js.map +1 -1
- package/dist/localSource.cjs +1 -1
- package/dist/localSource.cjs.map +1 -1
- package/dist/localSource.d.ts +2 -22
- package/dist/localSource.d.ts.map +1 -1
- package/dist/localSource.js +58 -114
- package/dist/localSource.js.map +1 -1
- package/dist/pageSource.cjs +2 -0
- package/dist/pageSource.cjs.map +1 -0
- package/dist/pageSource.d.ts +4 -0
- package/dist/pageSource.d.ts.map +1 -0
- package/dist/pageSource.js +96 -0
- package/dist/pageSource.js.map +1 -0
- package/dist/pagination.cjs +1 -1
- package/dist/pagination.cjs.map +1 -1
- package/dist/pagination.d.ts +5 -30
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +21 -27
- package/dist/pagination.js.map +1 -1
- package/dist/requestController.cjs +2 -0
- package/dist/requestController.cjs.map +1 -0
- package/dist/requestController.d.ts +13 -0
- package/dist/requestController.d.ts.map +1 -0
- package/dist/requestController.js +29 -0
- package/dist/requestController.js.map +1 -0
- package/dist/sourceStore.cjs +2 -0
- package/dist/sourceStore.cjs.map +1 -0
- package/dist/sourceStore.d.ts +10 -0
- package/dist/sourceStore.d.ts.map +1 -0
- package/dist/sourceStore.js +38 -0
- package/dist/sourceStore.js.map +1 -0
- package/dist/sourcerer.cjs +1 -1
- package/dist/sourcerer.cjs.map +1 -1
- package/dist/sourcerer.iife.js +1 -1
- package/dist/sourcerer.iife.js.map +1 -1
- package/dist/sourcerer.js +1 -1
- package/dist/sourcerer.js.map +1 -1
- package/dist/types.d.ts +128 -345
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -7
- package/dist/_utils.cjs +0 -2
- package/dist/_utils.cjs.map +0 -1
- package/dist/_utils.d.ts +0 -8
- package/dist/_utils.d.ts.map +0 -1
- package/dist/_utils.js +0 -7
- package/dist/_utils.js.map +0 -1
- package/dist/applyQuery.cjs +0 -2
- package/dist/applyQuery.cjs.map +0 -1
- package/dist/applyQuery.d.ts +0 -20
- package/dist/applyQuery.d.ts.map +0 -1
- package/dist/applyQuery.js +0 -8
- package/dist/applyQuery.js.map +0 -1
- package/dist/asyncSearch.cjs +0 -2
- package/dist/asyncSearch.cjs.map +0 -1
- package/dist/asyncSearch.d.ts +0 -13
- package/dist/asyncSearch.d.ts.map +0 -1
- package/dist/asyncSearch.js +0 -28
- package/dist/asyncSearch.js.map +0 -1
- package/dist/codecs.cjs +0 -2
- package/dist/codecs.cjs.map +0 -1
- package/dist/codecs.d.ts +0 -30
- package/dist/codecs.d.ts.map +0 -1
- package/dist/codecs.js +0 -36
- package/dist/codecs.js.map +0 -1
- package/dist/core.cjs +0 -2
- package/dist/core.cjs.map +0 -1
- package/dist/core.d.ts +0 -47
- package/dist/core.d.ts.map +0 -1
- package/dist/core.js +0 -59
- package/dist/core.js.map +0 -1
- package/dist/derive.cjs +0 -2
- package/dist/derive.cjs.map +0 -1
- package/dist/derive.d.ts +0 -28
- package/dist/derive.d.ts.map +0 -1
- package/dist/derive.js +0 -40
- package/dist/derive.js.map +0 -1
- package/dist/errors.cjs +0 -2
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.d.ts +0 -62
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -29
- package/dist/errors.js.map +0 -1
- package/dist/fetchManager.cjs +0 -2
- package/dist/fetchManager.cjs.map +0 -1
- package/dist/fetchManager.d.ts +0 -27
- package/dist/fetchManager.d.ts.map +0 -1
- package/dist/fetchManager.js +0 -41
- package/dist/fetchManager.js.map +0 -1
- package/dist/merge.cjs +0 -2
- package/dist/merge.cjs.map +0 -1
- package/dist/merge.d.ts +0 -30
- package/dist/merge.d.ts.map +0 -1
- package/dist/merge.js +0 -34
- package/dist/merge.js.map +0 -1
- package/dist/prefetch.cjs +0 -2
- package/dist/prefetch.cjs.map +0 -1
- package/dist/prefetch.d.ts +0 -32
- package/dist/prefetch.d.ts.map +0 -1
- package/dist/prefetch.js +0 -38
- package/dist/prefetch.js.map +0 -1
- package/dist/presets.cjs +0 -2
- package/dist/presets.cjs.map +0 -1
- package/dist/presets.d.ts +0 -17
- package/dist/presets.d.ts.map +0 -1
- package/dist/presets.js +0 -43
- package/dist/presets.js.map +0 -1
- package/dist/remoteSource.cjs +0 -2
- package/dist/remoteSource.cjs.map +0 -1
- package/dist/remoteSource.d.ts +0 -4
- package/dist/remoteSource.d.ts.map +0 -1
- package/dist/remoteSource.js +0 -165
- package/dist/remoteSource.js.map +0 -1
- package/dist/state.cjs +0 -2
- package/dist/state.cjs.map +0 -1
- package/dist/state.d.ts +0 -23
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -14
- 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"}
|
package/dist/localSource.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
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
|
package/dist/localSource.cjs.map
CHANGED
|
@@ -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"}
|
package/dist/localSource.d.ts
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
import type { LocalSource, LocalSourceConfig } from './types';
|
|
2
|
-
/**
|
|
3
|
-
|
|
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,
|
|
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"}
|
package/dist/localSource.js
CHANGED
|
@@ -1,130 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
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
|
|
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
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
58
|
+
return e !== s && (s = e, d());
|
|
117
59
|
},
|
|
118
|
-
|
|
119
|
-
|
|
60
|
+
setQuery: f,
|
|
61
|
+
get snapshot() {
|
|
62
|
+
return u.value;
|
|
120
63
|
},
|
|
64
|
+
subscribe: u.subscribe,
|
|
121
65
|
[Symbol.dispose]() {
|
|
122
|
-
|
|
66
|
+
p.dispose();
|
|
123
67
|
}
|
|
124
68
|
};
|
|
125
|
-
return
|
|
69
|
+
return p;
|
|
126
70
|
}
|
|
127
71
|
//#endregion
|
|
128
|
-
export {
|
|
72
|
+
export { o as createLocalSource };
|
|
129
73
|
|
|
130
74
|
//# sourceMappingURL=localSource.js.map
|
package/dist/localSource.js.map
CHANGED
|
@@ -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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"}
|
package/dist/pagination.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
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
|
package/dist/pagination.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.cjs","names":[],"sources":["../src/pagination.ts"],"sourcesContent":["import type {
|
|
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"}
|
package/dist/pagination.d.ts
CHANGED
|
@@ -1,31 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare
|
|
3
|
-
export declare
|
|
4
|
-
export declare
|
|
5
|
-
|
|
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
|
package/dist/pagination.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,
|
|
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"}
|