@vielzeug/sourcerer 1.0.6 → 1.1.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 (89) hide show
  1. package/dist/_dev.cjs +2 -0
  2. package/dist/_dev.cjs.map +1 -0
  3. package/dist/_dev.d.ts +2 -0
  4. package/dist/_dev.d.ts.map +1 -0
  5. package/dist/_dev.js +12 -0
  6. package/dist/_dev.js.map +1 -0
  7. package/dist/_utils.cjs +1 -1
  8. package/dist/_utils.cjs.map +1 -1
  9. package/dist/_utils.d.ts +20 -0
  10. package/dist/_utils.d.ts.map +1 -1
  11. package/dist/_utils.js +16 -3
  12. package/dist/_utils.js.map +1 -1
  13. package/dist/codecs.cjs +1 -1
  14. package/dist/codecs.cjs.map +1 -1
  15. package/dist/codecs.d.ts +1 -1
  16. package/dist/codecs.d.ts.map +1 -1
  17. package/dist/codecs.js +21 -19
  18. package/dist/codecs.js.map +1 -1
  19. package/dist/core.cjs +1 -1
  20. package/dist/core.cjs.map +1 -1
  21. package/dist/core.d.ts +25 -1
  22. package/dist/core.d.ts.map +1 -1
  23. package/dist/core.js +1 -1
  24. package/dist/core.js.map +1 -1
  25. package/dist/cursorSource.cjs +1 -1
  26. package/dist/cursorSource.cjs.map +1 -1
  27. package/dist/cursorSource.js +69 -69
  28. package/dist/cursorSource.js.map +1 -1
  29. package/dist/derive.cjs +1 -1
  30. package/dist/derive.cjs.map +1 -1
  31. package/dist/derive.d.ts +1 -0
  32. package/dist/derive.d.ts.map +1 -1
  33. package/dist/derive.js +2 -4
  34. package/dist/derive.js.map +1 -1
  35. package/dist/devtools.cjs +2 -0
  36. package/dist/devtools.cjs.map +1 -0
  37. package/dist/devtools.d.ts +52 -0
  38. package/dist/devtools.d.ts.map +1 -0
  39. package/dist/devtools.js +20 -0
  40. package/dist/devtools.js.map +1 -0
  41. package/dist/errors.cjs +1 -1
  42. package/dist/errors.cjs.map +1 -1
  43. package/dist/errors.d.ts +2 -2
  44. package/dist/errors.d.ts.map +1 -1
  45. package/dist/errors.js +1 -1
  46. package/dist/errors.js.map +1 -1
  47. package/dist/fetchManager.cjs.map +1 -1
  48. package/dist/fetchManager.d.ts +13 -0
  49. package/dist/fetchManager.d.ts.map +1 -1
  50. package/dist/fetchManager.js.map +1 -1
  51. package/dist/index.cjs +1 -1
  52. package/dist/index.d.ts +1 -2
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +13 -14
  55. package/dist/infiniteSource.cjs +1 -1
  56. package/dist/infiniteSource.cjs.map +1 -1
  57. package/dist/infiniteSource.js +62 -62
  58. package/dist/infiniteSource.js.map +1 -1
  59. package/dist/localSource.cjs +1 -1
  60. package/dist/localSource.cjs.map +1 -1
  61. package/dist/localSource.d.ts.map +1 -1
  62. package/dist/localSource.js +69 -79
  63. package/dist/localSource.js.map +1 -1
  64. package/dist/merge.cjs +1 -1
  65. package/dist/merge.cjs.map +1 -1
  66. package/dist/merge.d.ts +6 -1
  67. package/dist/merge.d.ts.map +1 -1
  68. package/dist/merge.js +8 -1
  69. package/dist/merge.js.map +1 -1
  70. package/dist/remoteSource.cjs +1 -1
  71. package/dist/remoteSource.cjs.map +1 -1
  72. package/dist/remoteSource.d.ts.map +1 -1
  73. package/dist/remoteSource.js +94 -94
  74. package/dist/remoteSource.js.map +1 -1
  75. package/dist/sourcerer.cjs +1 -1
  76. package/dist/sourcerer.cjs.map +1 -1
  77. package/dist/sourcerer.iife.js +1 -1
  78. package/dist/sourcerer.iife.js.map +1 -1
  79. package/dist/sourcerer.js +1 -1
  80. package/dist/sourcerer.js.map +1 -1
  81. package/dist/types.d.ts +14 -4
  82. package/dist/types.d.ts.map +1 -1
  83. package/package.json +7 -1
  84. package/dist/applyQuery.cjs +0 -2
  85. package/dist/applyQuery.cjs.map +0 -1
  86. package/dist/applyQuery.d.ts +0 -20
  87. package/dist/applyQuery.d.ts.map +0 -1
  88. package/dist/applyQuery.js +0 -8
  89. package/dist/applyQuery.js.map +0 -1
@@ -1,130 +1,120 @@
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 { clampPositiveInt as e, extractError as t } from "./_utils.js";
2
+ import { SourcererError as n } from "./errors.js";
3
+ import { createAsyncSearchCoordinator as r } from "./asyncSearch.js";
4
+ import { createSourceCore as i } from "./core.js";
5
+ import { clampPage as a, createMeta as o, pageCount as s } from "./pagination.js";
5
6
  //#region src/localSource.ts
6
- var o = 20, s = 300;
7
- function c(e, t) {
7
+ var c = 20, l = 300;
8
+ function u(e, t) {
8
9
  let n = t.toLowerCase();
9
10
  return e.filter((e) => JSON.stringify(e).toLowerCase().includes(n));
10
11
  }
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();
12
+ function d(d, f = {}) {
13
+ let p = i(), m = f.filterAsync !== void 0 || f.sortAsync !== void 0, h = f.debounceMs ?? l, g = f.initialData ?? d, _ = "", v = f.filter, y = f.sort, b = e(f.limit ?? c, "createLocalSource", "limit"), x = 1, S = [], C = !1, w = !1, T = null, E = null, D = () => p.notify(), O = r(p, D), k = () => {
14
+ let e = g;
15
+ return _ && (e = (f.searchFn ?? u)(e, _)), v && (e = e.filter(v)), y && (e = [...e].sort(y)), e;
16
+ }, A = () => {
17
+ S = k();
18
+ let e = s(S.length, b);
19
+ x = a(x, e);
20
+ }, j = async () => {
21
+ E?.abort();
22
+ let e = new AbortController();
23
+ E = e, C = !0, T = null, D();
25
24
  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);
25
+ let t = g;
26
+ if (_ && (t = (f.searchFn ?? u)(t, _)), v && (t = t.filter(v)), f.filterAsync && !e.signal.aborted && (t = await f.filterAsync(t, e.signal)), y && (t = [...t].sort(y)), f.sortAsync && !e.signal.aborted && (t = await f.sortAsync(t, e.signal)), e.signal.aborted) return;
27
+ S = t, C = !1, w = !1;
28
+ let n = s(S.length, b);
29
+ x = a(x, n);
31
30
  } catch (r) {
32
- n.signal.aborted || (x = !1, S = !1, C = new e(t(r), { cause: r }));
31
+ e.signal.aborted || (C = !1, w = !1, T = new n(t(r), { cause: r }));
33
32
  } finally {
34
- w === n && (w = null);
33
+ E === e && (E = null);
35
34
  }
36
- E(), D();
37
- }, j = () => f ? A() : (S = !1, k(), E(), D(), Promise.resolve());
38
- f ? (b = O(), A()) : k();
39
- let M = {
35
+ D(), O.settleIfIdle(0);
36
+ }, M = () => m ? j() : (w = !1, A(), D(), O.settleIfIdle(0), Promise.resolve());
37
+ m ? (S = k(), j()) : A();
38
+ let N = {
40
39
  get current() {
41
- let e = (y - 1) * v;
42
- return b.slice(e, e + v);
40
+ let e = (x - 1) * b;
41
+ return S.slice(e, e + b);
43
42
  },
44
43
  get disposalSignal() {
45
- return d.disposalSignal;
44
+ return p.disposalSignal;
46
45
  },
47
46
  dispose() {
48
- d.isDisposed || (d.cancelTimer(), w?.abort(), w = null, D(), d.dispose());
47
+ p.isDisposed || (E?.abort(), E = null, O.cancel(), O.dispose(), p.dispose());
49
48
  },
50
49
  get disposed() {
51
- return d.isDisposed;
50
+ return p.isDisposed;
52
51
  },
53
52
  goTo(e) {
54
- let t = r(e, a(b.length, v));
55
- return t === y ? Promise.resolve() : (y = t, E(), Promise.resolve());
53
+ let t = a(e, s(S.length, b));
54
+ return t === x ? Promise.resolve() : (x = t, D(), Promise.resolve());
56
55
  },
57
56
  goToLast() {
58
- return M.goTo(a(b.length, v));
57
+ return N.goTo(s(S.length, b));
59
58
  },
60
59
  get meta() {
61
- return i({
62
- error: C,
63
- isLoading: x,
64
- isSearchPending: S,
65
- pageNumber: y,
66
- pageSize: v,
67
- totalItems: b.length
60
+ return o({
61
+ error: T,
62
+ isLoading: C,
63
+ isSearchPending: w,
64
+ pageNumber: x,
65
+ pageSize: b,
66
+ totalItems: S.length
68
67
  });
69
68
  },
70
69
  next() {
71
- return M.goTo(y + 1);
70
+ return N.goTo(x + 1);
72
71
  },
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);
72
+ patch(t) {
73
+ let n = !1;
74
+ if (t.limit !== void 0) {
75
+ let r = e(t.limit, "source.patch", "limit");
76
+ r !== b && (b = r, n = !0);
78
77
  }
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);
78
+ if ("filter" in t && (v = t.filter, n = !0), "sort" in t && (y = t.sort, n = !0), "search" in t && (t.search !== _ || p.isScheduled) && (_ = t.search ?? "", n = !0), t.page !== void 0) {
79
+ let e = a(t.page, s(S.length, b));
80
+ e !== x && (x = e, n = !0);
82
81
  }
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();
82
+ return n ? (O.cancel(), (t.limit !== void 0 || "filter" in t || "sort" in t || "search" in t) && t.page === void 0 && (x = 1), M()) : Promise.resolve();
84
83
  },
85
84
  prev() {
86
- return M.goTo(y - 1);
85
+ return N.goTo(x - 1);
87
86
  },
88
87
  get query() {
89
88
  return {
90
- limit: v,
91
- page: y,
92
- ...h && { search: h }
89
+ limit: b,
90
+ page: x,
91
+ ..._ && { search: _ }
93
92
  };
94
93
  },
95
94
  ready(e) {
96
- return d.ready(() => !x && !d.isScheduled, e);
95
+ return p.ready(() => !C && !p.isScheduled, e);
97
96
  },
98
97
  reset() {
99
- return d.cancelTimer(), S = !1, h = "", g = u.filter, _ = u.sort, y = 1, D(), j();
98
+ return O.cancel(), w = !1, _ = "", v = f.filter, y = f.sort, x = 1, M();
100
99
  },
101
100
  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;
101
+ return t?.immediate ? e === _ && !p.isScheduled ? Promise.resolve() : (O.cancel(), w = !1, _ = e, x = 1, M()) : e === _ ? Promise.resolve() : (_ = e, x = 1, w = !0, O.schedule(() => {
102
+ w = !1, M();
103
+ }, h));
114
104
  },
115
105
  setData(e) {
116
- return m = e, y = 1, j();
106
+ return g = e, x = 1, M();
117
107
  },
118
108
  subscribe(e) {
119
- return d.subscribe(e);
109
+ return p.subscribe(e);
120
110
  },
121
111
  [Symbol.dispose]() {
122
- M.dispose();
112
+ N.dispose();
123
113
  }
124
114
  };
125
- return M;
115
+ return N;
126
116
  }
127
117
  //#endregion
128
- export { l as createLocalSource };
118
+ export { d as createLocalSource };
129
119
 
130
120
  //# 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":";;;;;AASA,IAAM,IAAgB,IAChB,IAAsB;AAE5B,SAAS,EAAiB,GAAqB,GAA6B;CAC1E,IAAM,IAAI,EAAM,YAAY;CAE5B,OAAO,EAAM,QAAQ,MAAS,KAAK,UAAU,CAAI,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC;AAC9E;AAuBA,SAAgB,EAAqB,GAAoB,IAA4B,CAAC,GAAmB;CACvG,IAAM,IAAO,EAAiB,GACxB,IAAU,EAAI,gBAAgB,KAAA,KAAa,EAAI,cAAc,KAAA,GAC7D,IAAa,EAAI,cAAc,GAEjC,IAAwB,EAAI,eAAe,GAC3C,IAAS,IACT,IAAS,EAAI,QACb,IAAO,EAAI,MACX,IAAQ,EAAI,SAAS,GACrB,IAAO,GACP,IAA0B,CAAC,GAC3B,IAAY,IACZ,IAAkB,IAClB,IAA+B,MAC/B,IAA0C,MAC1C,IAAsC,MAEpC,UAAe,EAAK,OAAO,GAE3B,UAA6B;EACjC,AAEE,OADA,EAAc,QAAQ,GACN;CAEpB,GAGM,UAAsC;EAC1C,IAAI,IAAuB;EAQ3B,OANI,MAAQ,KAAU,EAAI,YAAY,EAAA,CAAe,GAAQ,CAAM,IAE/D,MAAQ,IAAS,EAAO,OAAO,CAAM,IAErC,MAAM,IAAS,CAAC,GAAG,CAAM,CAAC,CAAC,KAAK,CAAI,IAEjC;CACT,GAEM,UAAkB;EACtB,IAAY,EAAgB;EAE5B,IAAM,IAAQ,EAAU,EAAU,QAAQ,CAAK;EAE/C,IAAO,EAAU,GAAM,CAAK;CAC9B,GAGM,IAAmB,YAA2B;EAClD,GAAiB,MAAM;EAEvB,IAAM,IAAa,IAAI,gBAAgB;EAKvC,AAHA,IAAkB,GAClB,IAAY,IACZ,IAAQ,MACR,EAAO;EAEP,IAAI;GACF,IAAI,IAAuB;GAgB3B,IAdI,MAAQ,KAAU,EAAI,YAAY,EAAA,CAAe,GAAQ,CAAM,IAE/D,MAAQ,IAAS,EAAO,OAAO,CAAM,IAErC,EAAI,eAAe,CAAC,EAAW,OAAO,YACxC,IAAS,MAAM,EAAI,YAAY,GAAQ,EAAW,MAAM,IAGtD,MAAM,IAAS,CAAC,GAAG,CAAM,CAAC,CAAC,KAAK,CAAI,IAEpC,EAAI,aAAa,CAAC,EAAW,OAAO,YACtC,IAAS,MAAM,EAAI,UAAU,GAAQ,EAAW,MAAM,IAGpD,EAAW,OAAO,SAAS;GAI/B,AAFA,IAAY,GACZ,IAAY,IACZ,IAAkB;GAElB,IAAM,IAAQ,EAAU,EAAU,QAAQ,CAAK;GAE/C,IAAO,EAAU,GAAM,CAAK;EAC9B,SAAS,GAAiB;GACxB,AAAK,EAAW,OAAO,YACrB,IAAY,IACZ,IAAkB,IAClB,IAAQ,IAAI,EAAe,EAAa,CAAM,GAAG,EAAE,OAAO,EAAO,CAAC;EAEtE,UAAU;GACR,AAAI,MAAoB,MAAY,IAAkB;EACxD;EAGA,AADA,EAAO,GACP,EAAqB;CACvB,GAGM,UACA,IAAgB,EAAiB,KAErC,IAAkB,IAClB,EAAU,GACV,EAAO,GACP,EAAqB,GAEd,QAAQ,QAAQ;CAIzB,AAAI,KAEF,IAAY,EAAgB,GAC5B,EAAsB,KAEtB,EAAU;CAGZ,IAAM,IAAyB;EAC7B,IAAI,UAAU;GACZ,IAAM,KAAS,IAAO,KAAK;GAE3B,OAAO,EAAU,MAAM,GAAO,IAAQ,CAAK;EAC7C;EAEA,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,EAAK,eAET,EAAK,YAAY,GACjB,GAAiB,MAAM,GACvB,IAAkB,MAClB,EAAqB,GACrB,EAAK,QAAQ;EACf;EAEA,IAAI,WAAW;GACb,OAAO,EAAK;EACd;EAEA,KAAK,GAAG;GAEN,IAAM,IAAU,EAAU,GADZ,EAAU,EAAU,QAAQ,CACb,CAAK;GAOlC,OALI,MAAY,IAAa,QAAQ,QAAQ,KAE7C,IAAO,GACP,EAAO,GAEA,QAAQ,QAAQ;EACzB;EAEA,WAAW;GACT,OAAO,EAAO,KAAK,EAAU,EAAU,QAAQ,CAAK,CAAC;EACvD;EAEA,IAAI,OAAO;GACT,OAAO,EAAW;IAChB;IACA;IACA;IACA,YAAY;IACZ,UAAU;IACV,YAAY,EAAU;GACxB,CAAC;EACH;EAEA,OAAO;GACL,OAAO,EAAO,KAAK,IAAO,CAAC;EAC7B;EAEA,MAAM,GAA8B;GAClC,IAAI,IAAU;GAEd,IAAI,EAAQ,UAAU,KAAA,GAAW;IAC/B,IAAM,IAAO,KAAK,IAAI,GAAG,KAAK,MAAM,EAAQ,KAAK,CAAC;IAElD,AAAI,MAAS,MACX,IAAQ,GACR,IAAU;GAEd;GAmBA,IAjBI,YAAY,MACd,IAAS,EAAQ,QACjB,IAAU,KAGR,UAAU,MACZ,IAAO,EAAQ,MACf,IAAU,KAKR,YAAY,MAAY,EAAQ,WAAW,KAAU,EAAK,iBAC5D,IAAS,EAAQ,UAAU,IAC3B,IAAU,KAGR,EAAQ,SAAS,KAAA,GAAW;IAC9B,IAAM,IAAO,EAAU,EAAQ,MAAM,EAAU,EAAU,QAAQ,CAAK,CAAC;IAEvE,AAAI,MAAS,MACX,IAAO,GACP,IAAU;GAEd;GAiBA,OAfK,KAIL,EAAK,YAAY,GACjB,EAAqB,IAIlB,EAAQ,UAAU,KAAA,KAAa,YAAY,KAAW,UAAU,KAAW,YAAY,MACxF,EAAQ,SAAS,KAAA,MAEjB,IAAO,IAGF,EAAc,KAfA,QAAQ,QAAQ;EAgBvC;EAEA,OAAO;GACL,OAAO,EAAO,KAAK,IAAO,CAAC;EAC7B;EAEA,IAAI,QAAqB;GACvB,OAAO;IACL;IACA;IACA,GAAI,KAAU,EAAE,UAAO;GACzB;EACF;EAEA,MAAM,GAAkB;GACtB,OAAO,EAAK,YAAY,CAAC,KAAa,CAAC,EAAK,aAAa,CAAO;EAClE;EAEA,QAAQ;GASN,OARA,EAAK,YAAY,GACjB,IAAkB,IAClB,IAAS,IACT,IAAS,EAAI,QACb,IAAO,EAAI,MACX,IAAO,GACP,EAAqB,GAEd,EAAc;EACvB;EAEA,OAAO,GAAG,GAAqC;GAC7C,IAAI,GAAM,WAWR,OARI,MAAM,KAAU,CAAC,EAAK,cAAoB,QAAQ,QAAQ,KAE9D,EAAK,YAAY,GACjB,IAAkB,IAClB,IAAS,GACT,IAAO,GACP,EAAqB,GAEd,EAAc;GAGvB,IAAI,MAAM,GAAQ,OAAO,QAAQ,QAAQ;GAQzC,AALA,EAAqB,GAErB,IAAS,GACT,IAAO,GACP,IAAkB,IAClB,EAAO;GAEP,IAAI,GACE,IAAU,IAAI,SAAe,MAAQ;IACzC,IAAgB;GAClB,CAAC;GASD,OAPA,IAAgB;IAAE;IAAS,SAAS;GAAc,GAElD,EAAK,eAAe;IAElB,AADA,IAAkB,IAClB,EAAmB;GACrB,GAAG,CAAU,GAEN;EACT;EAEA,QAAQ,GAAG;GAIT,OAHA,IAAU,GACV,IAAO,GAEA,EAAc;EACvB;EAEA,UAAU,GAAU;GAClB,OAAO,EAAK,UAAU,CAAQ;EAChC;EAEA,CAAC,OAAO,WAAW;GACjB,EAAO,QAAQ;EACjB;CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"localSource.js","names":[],"sources":["../src/localSource.ts"],"sourcesContent":["import type { LocalSource, LocalSourceConfig, LocalSourceQuery, SearchOptions, SourceQuery } from './types';\n\nimport { clampPositiveInt, extractError } from './_utils';\nimport { createAsyncSearchCoordinator } from './asyncSearch';\nimport { createSourceCore } from './core';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\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 = clampPositiveInt(cfg.limit ?? DEFAULT_LIMIT, 'createLocalSource', '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\n const commit = () => core.notify();\n // Shared debounced-search promise coordinator — the same one remote/cursor/infinite use,\n // instead of a fourth hand-rolled copy of \"pendingSearch/resolvePendingSearch\" bookkeeping.\n const searchCoordinator = createAsyncSearchCoordinator(core, commit);\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 // Local source never has more than one pipeline run in flight — settling always means idle.\n searchCoordinator.settleIfIdle(0);\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 searchCoordinator.settleIfIdle(0);\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 asyncController?.abort();\n asyncController = null;\n searchCoordinator.cancel();\n searchCoordinator.dispose();\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 = clampPositiveInt(changes.limit, 'source.patch', '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 searchCoordinator.cancel();\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 searchCoordinator.cancel();\n isSearchPending = false;\n search = '';\n filter = cfg.filter;\n sort = cfg.sort;\n page = 1;\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 searchCoordinator.cancel();\n isSearchPending = false;\n search = q;\n page = 1;\n\n return flushInternal();\n }\n\n if (q === search) return Promise.resolve();\n\n search = q;\n page = 1;\n isSearchPending = true;\n\n return searchCoordinator.schedule(() => {\n isSearchPending = false;\n void flushInternal();\n }, debounceMs);\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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"}
package/dist/merge.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./core.cjs");function t(t,n){let r=e.createSourceCore(),i=n(t.map(e=>e.current)),a=t.map(e=>e.subscribe(()=>{i=n(t.map(e=>e.current)),r.notify()})),o={get current(){return i},get disposalSignal(){return r.disposalSignal},dispose(){if(!r.isDisposed){for(let e of a)e();r.dispose()}},get disposed(){return r.isDisposed},subscribe(e){return r.subscribe(e)},[Symbol.dispose](){o.dispose()}};return o}exports.mergeSource=t;
1
+ const e=require("./core.cjs");function t(t,n){let r=e.createSourceCore(),i=n(t.map(e=>e.current)),a=t.map(e=>e.subscribe(()=>{i=n(t.map(e=>e.current)),r.notify()})),o={get current(){return i},get disposalSignal(){return r.disposalSignal},dispose(){if(!r.isDisposed){for(let e of a)e();r.dispose()}},get disposed(){return r.isDisposed},subscribe(e){return r.subscribe(e)},[Symbol.dispose](){o.dispose()}},s=t.map(e=>e.disposalSignal).filter(e=>e instanceof AbortSignal);if(s.length===t.length&&s.length>0){let e=s.filter(e=>!e.aborted).length;if(e===0)o.dispose();else for(let t of s)t.aborted||t.addEventListener(`abort`,()=>{e--,e===0&&o.dispose()},{once:!0})}return o}exports.mergeSource=t;
2
2
  //# sourceMappingURL=merge.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"merge.cjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import type { MergedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Combines multiple reactive sources into a single read-only view by applying\n * a user-supplied `combine` function over all sources' `current` arrays.\n *\n * The merged source recalculates whenever any parent source changes. Useful for\n * offline/online hybrid UIs (local + remote merged), multi-dataset views, or\n * pagination across multiple backends.\n *\n * Call `dispose()` to unsubscribe from all parent sources.\n *\n * @example\n * ```ts\n * const local = createLocalSource(localCache, { limit: 50 });\n * const remote = createRemoteSource({ fetch: fetchUsers, limit: 50 });\n *\n * // Show local cache items first, then remote items (deduped by id).\n * const merged = mergeSource([local, remote], (all) => {\n * const seen = new Set<number>();\n * return all.flat().filter((u) => !seen.has(u.id) && seen.add(u.id));\n * });\n *\n * merged.subscribe(() => console.log(merged.current));\n * ```\n */\nexport function mergeSource<T>(\n sources: ReadonlyArray<{\n readonly current: readonly T[];\n subscribe(listener: () => void): () => void;\n }>,\n combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[],\n): MergedSource<T> {\n const core = createSourceCore();\n let cachedCurrent: readonly T[] = combine(sources.map((s) => s.current));\n\n const unsubscribeFns = sources.map((source) =>\n source.subscribe(() => {\n cachedCurrent = combine(sources.map((s) => s.current));\n core.notify();\n }),\n );\n\n const source: MergedSource<T> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n for (const unsub of unsubscribeFns) {\n unsub();\n }\n\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n"],"mappings":"8BA4BA,SAAgB,EACd,EAIA,EACiB,CACjB,IAAM,EAAO,EAAA,iBAAiB,EAC1B,EAA8B,EAAQ,EAAQ,IAAK,GAAM,EAAE,OAAO,CAAC,EAEjE,EAAiB,EAAQ,IAAK,GAClC,EAAO,cAAgB,CACrB,EAAgB,EAAQ,EAAQ,IAAK,GAAM,EAAE,OAAO,CAAC,EACrD,EAAK,OAAO,CACd,CAAC,CACH,EAEM,EAA0B,CAC9B,IAAI,SAAU,CACZ,OAAO,CACT,EAEA,IAAI,gBAAiB,CACnB,OAAO,EAAK,cACd,EAEA,SAAU,CACJ,MAAK,WAET,KAAK,IAAM,KAAS,EAClB,EAAM,EAGR,EAAK,QAAQ,CAHL,CAIV,EAEA,IAAI,UAAW,CACb,OAAO,EAAK,UACd,EAEA,UAAU,EAAU,CAClB,OAAO,EAAK,UAAU,CAAQ,CAChC,EAEA,CAAC,OAAO,UAAW,CACjB,EAAO,QAAQ,CACjB,CACF,EAEA,OAAO,CACT"}
1
+ {"version":3,"file":"merge.cjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import type { MergedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Combines multiple reactive sources into a single read-only view by applying\n * a user-supplied `combine` function over all sources' `current` arrays.\n *\n * The merged source recalculates whenever any parent source changes. Useful for\n * offline/online hybrid UIs (local + remote merged), multi-dataset views, or\n * pagination across multiple backends.\n *\n * Call `dispose()` to unsubscribe from all parent sources. If every parent exposes a\n * `disposalSignal` (the standard `ReactiveSource` shape does), the merge auto-disposes once\n * every parent has disposed — mirroring `deriveSource()`'s single-parent auto-dispose, scaled\n * to N parents. If any parent lacks `disposalSignal` (a minimal duck-typed source), auto-dispose\n * doesn't activate — call `dispose()` yourself.\n *\n * @example\n * ```ts\n * const local = createLocalSource(localCache, { limit: 50 });\n * const remote = createRemoteSource({ fetch: fetchUsers, limit: 50 });\n *\n * // Show local cache items first, then remote items (deduped by id).\n * const merged = mergeSource([local, remote], (all) => {\n * const seen = new Set<number>();\n * return all.flat().filter((u) => !seen.has(u.id) && seen.add(u.id));\n * });\n *\n * merged.subscribe(() => console.log(merged.current));\n * ```\n */\nexport function mergeSource<T>(\n sources: ReadonlyArray<{\n readonly current: readonly T[];\n readonly disposalSignal?: AbortSignal;\n subscribe(listener: () => void): () => void;\n }>,\n combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[],\n): MergedSource<T> {\n const core = createSourceCore();\n let cachedCurrent: readonly T[] = combine(sources.map((s) => s.current));\n\n const unsubscribeFns = sources.map((source) =>\n source.subscribe(() => {\n cachedCurrent = combine(sources.map((s) => s.current));\n core.notify();\n }),\n );\n\n const source: MergedSource<T> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n for (const unsub of unsubscribeFns) {\n unsub();\n }\n\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n // Mirrors deriveSource()'s parent-lifetime tracking, scaled to N parents: once every\n // parent has disposed there's nothing left this merge could ever reflect, so it disposes\n // itself too instead of staying alive holding now-permanently-stale combined state.\n // Only tracked when every source actually exposes a disposalSignal — mergeSource() also\n // accepts minimal duck-typed sources (per its parameter type) that may not have one.\n // Routed through `source.dispose()` above, not `core.dispose()` directly, so there's exactly\n // one cleanup path (including unsubscribing from parents) regardless of what triggered it.\n const disposalSignals = sources\n .map((s) => s.disposalSignal)\n .filter((s): s is AbortSignal => s instanceof AbortSignal);\n\n if (disposalSignals.length === sources.length && disposalSignals.length > 0) {\n let pendingCount = disposalSignals.filter((s) => !s.aborted).length;\n\n if (pendingCount === 0) {\n // Every parent was already disposed before mergeSource() was even called.\n source.dispose();\n } else {\n for (const signal of disposalSignals) {\n if (signal.aborted) continue;\n\n signal.addEventListener(\n 'abort',\n () => {\n pendingCount--;\n\n if (pendingCount === 0) source.dispose();\n },\n { once: true },\n );\n }\n }\n }\n\n return source;\n}\n"],"mappings":"8BAgCA,SAAgB,EACd,EAKA,EACiB,CACjB,IAAM,EAAO,EAAA,iBAAiB,EAC1B,EAA8B,EAAQ,EAAQ,IAAK,GAAM,EAAE,OAAO,CAAC,EAEjE,EAAiB,EAAQ,IAAK,GAClC,EAAO,cAAgB,CACrB,EAAgB,EAAQ,EAAQ,IAAK,GAAM,EAAE,OAAO,CAAC,EACrD,EAAK,OAAO,CACd,CAAC,CACH,EAEM,EAA0B,CAC9B,IAAI,SAAU,CACZ,OAAO,CACT,EAEA,IAAI,gBAAiB,CACnB,OAAO,EAAK,cACd,EAEA,SAAU,CACJ,MAAK,WAET,KAAK,IAAM,KAAS,EAClB,EAAM,EAGR,EAAK,QAAQ,CAHL,CAIV,EAEA,IAAI,UAAW,CACb,OAAO,EAAK,UACd,EAEA,UAAU,EAAU,CAClB,OAAO,EAAK,UAAU,CAAQ,CAChC,EAEA,CAAC,OAAO,UAAW,CACjB,EAAO,QAAQ,CACjB,CACF,EASM,EAAkB,EACrB,IAAK,GAAM,EAAE,cAAc,CAAC,CAC5B,OAAQ,GAAwB,aAAa,WAAW,EAE3D,GAAI,EAAgB,SAAW,EAAQ,QAAU,EAAgB,OAAS,EAAG,CAC3E,IAAI,EAAe,EAAgB,OAAQ,GAAM,CAAC,EAAE,OAAO,CAAC,CAAC,OAE7D,GAAI,IAAiB,EAEnB,EAAO,QAAQ,OAEf,IAAK,IAAM,KAAU,EACf,EAAO,SAEX,EAAO,iBACL,YACM,CACJ,IAEI,IAAiB,GAAG,EAAO,QAAQ,CACzC,EACA,CAAE,KAAM,EAAK,CACf,CAGN,CAEA,OAAO,CACT"}
package/dist/merge.d.ts CHANGED
@@ -7,7 +7,11 @@ import type { MergedSource } from './types';
7
7
  * offline/online hybrid UIs (local + remote merged), multi-dataset views, or
8
8
  * pagination across multiple backends.
9
9
  *
10
- * Call `dispose()` to unsubscribe from all parent sources.
10
+ * Call `dispose()` to unsubscribe from all parent sources. If every parent exposes a
11
+ * `disposalSignal` (the standard `ReactiveSource` shape does), the merge auto-disposes once
12
+ * every parent has disposed — mirroring `deriveSource()`'s single-parent auto-dispose, scaled
13
+ * to N parents. If any parent lacks `disposalSignal` (a minimal duck-typed source), auto-dispose
14
+ * doesn't activate — call `dispose()` yourself.
11
15
  *
12
16
  * @example
13
17
  * ```ts
@@ -25,6 +29,7 @@ import type { MergedSource } from './types';
25
29
  */
26
30
  export declare function mergeSource<T>(sources: ReadonlyArray<{
27
31
  readonly current: readonly T[];
32
+ readonly disposalSignal?: AbortSignal;
28
33
  subscribe(listener: () => void): () => void;
29
34
  }>, combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[]): MergedSource<T>;
30
35
  //# sourceMappingURL=merge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAC/B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C,CAAC,EACF,OAAO,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,EAAE,GAC/D,YAAY,CAAC,CAAC,CAAC,CA4CjB"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC;IACtC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C,CAAC,EACF,OAAO,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,EAAE,GAC/D,YAAY,CAAC,CAAC,CAAC,CA8EjB"}
package/dist/merge.js CHANGED
@@ -25,7 +25,14 @@ function t(t, n) {
25
25
  [Symbol.dispose]() {
26
26
  o.dispose();
27
27
  }
28
- };
28
+ }, s = t.map((e) => e.disposalSignal).filter((e) => e instanceof AbortSignal);
29
+ if (s.length === t.length && s.length > 0) {
30
+ let e = s.filter((e) => !e.aborted).length;
31
+ if (e === 0) o.dispose();
32
+ else for (let t of s) t.aborted || t.addEventListener("abort", () => {
33
+ e--, e === 0 && o.dispose();
34
+ }, { once: !0 });
35
+ }
29
36
  return o;
30
37
  }
31
38
  //#endregion
package/dist/merge.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import type { MergedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Combines multiple reactive sources into a single read-only view by applying\n * a user-supplied `combine` function over all sources' `current` arrays.\n *\n * The merged source recalculates whenever any parent source changes. Useful for\n * offline/online hybrid UIs (local + remote merged), multi-dataset views, or\n * pagination across multiple backends.\n *\n * Call `dispose()` to unsubscribe from all parent sources.\n *\n * @example\n * ```ts\n * const local = createLocalSource(localCache, { limit: 50 });\n * const remote = createRemoteSource({ fetch: fetchUsers, limit: 50 });\n *\n * // Show local cache items first, then remote items (deduped by id).\n * const merged = mergeSource([local, remote], (all) => {\n * const seen = new Set<number>();\n * return all.flat().filter((u) => !seen.has(u.id) && seen.add(u.id));\n * });\n *\n * merged.subscribe(() => console.log(merged.current));\n * ```\n */\nexport function mergeSource<T>(\n sources: ReadonlyArray<{\n readonly current: readonly T[];\n subscribe(listener: () => void): () => void;\n }>,\n combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[],\n): MergedSource<T> {\n const core = createSourceCore();\n let cachedCurrent: readonly T[] = combine(sources.map((s) => s.current));\n\n const unsubscribeFns = sources.map((source) =>\n source.subscribe(() => {\n cachedCurrent = combine(sources.map((s) => s.current));\n core.notify();\n }),\n );\n\n const source: MergedSource<T> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n for (const unsub of unsubscribeFns) {\n unsub();\n }\n\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n"],"mappings":";;AA4BA,SAAgB,EACd,GAIA,GACiB;CACjB,IAAM,IAAO,EAAiB,GAC1B,IAA8B,EAAQ,EAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,GAEjE,IAAiB,EAAQ,KAAK,MAClC,EAAO,gBAAgB;EAErB,AADA,IAAgB,EAAQ,EAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,GACrD,EAAK,OAAO;CACd,CAAC,CACH,GAEM,IAA0B;EAC9B,IAAI,UAAU;GACZ,OAAO;EACT;EAEA,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,OAAK,YAET;SAAK,IAAM,KAAS,GAClB,EAAM;IAGR,EAAK,QAAQ;GAHL;EAIV;EAEA,IAAI,WAAW;GACb,OAAO,EAAK;EACd;EAEA,UAAU,GAAU;GAClB,OAAO,EAAK,UAAU,CAAQ;EAChC;EAEA,CAAC,OAAO,WAAW;GACjB,EAAO,QAAQ;EACjB;CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"merge.js","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import type { MergedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Combines multiple reactive sources into a single read-only view by applying\n * a user-supplied `combine` function over all sources' `current` arrays.\n *\n * The merged source recalculates whenever any parent source changes. Useful for\n * offline/online hybrid UIs (local + remote merged), multi-dataset views, or\n * pagination across multiple backends.\n *\n * Call `dispose()` to unsubscribe from all parent sources. If every parent exposes a\n * `disposalSignal` (the standard `ReactiveSource` shape does), the merge auto-disposes once\n * every parent has disposed — mirroring `deriveSource()`'s single-parent auto-dispose, scaled\n * to N parents. If any parent lacks `disposalSignal` (a minimal duck-typed source), auto-dispose\n * doesn't activate — call `dispose()` yourself.\n *\n * @example\n * ```ts\n * const local = createLocalSource(localCache, { limit: 50 });\n * const remote = createRemoteSource({ fetch: fetchUsers, limit: 50 });\n *\n * // Show local cache items first, then remote items (deduped by id).\n * const merged = mergeSource([local, remote], (all) => {\n * const seen = new Set<number>();\n * return all.flat().filter((u) => !seen.has(u.id) && seen.add(u.id));\n * });\n *\n * merged.subscribe(() => console.log(merged.current));\n * ```\n */\nexport function mergeSource<T>(\n sources: ReadonlyArray<{\n readonly current: readonly T[];\n readonly disposalSignal?: AbortSignal;\n subscribe(listener: () => void): () => void;\n }>,\n combine: (allItems: ReadonlyArray<readonly T[]>) => readonly T[],\n): MergedSource<T> {\n const core = createSourceCore();\n let cachedCurrent: readonly T[] = combine(sources.map((s) => s.current));\n\n const unsubscribeFns = sources.map((source) =>\n source.subscribe(() => {\n cachedCurrent = combine(sources.map((s) => s.current));\n core.notify();\n }),\n );\n\n const source: MergedSource<T> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n for (const unsub of unsubscribeFns) {\n unsub();\n }\n\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n // Mirrors deriveSource()'s parent-lifetime tracking, scaled to N parents: once every\n // parent has disposed there's nothing left this merge could ever reflect, so it disposes\n // itself too instead of staying alive holding now-permanently-stale combined state.\n // Only tracked when every source actually exposes a disposalSignal — mergeSource() also\n // accepts minimal duck-typed sources (per its parameter type) that may not have one.\n // Routed through `source.dispose()` above, not `core.dispose()` directly, so there's exactly\n // one cleanup path (including unsubscribing from parents) regardless of what triggered it.\n const disposalSignals = sources\n .map((s) => s.disposalSignal)\n .filter((s): s is AbortSignal => s instanceof AbortSignal);\n\n if (disposalSignals.length === sources.length && disposalSignals.length > 0) {\n let pendingCount = disposalSignals.filter((s) => !s.aborted).length;\n\n if (pendingCount === 0) {\n // Every parent was already disposed before mergeSource() was even called.\n source.dispose();\n } else {\n for (const signal of disposalSignals) {\n if (signal.aborted) continue;\n\n signal.addEventListener(\n 'abort',\n () => {\n pendingCount--;\n\n if (pendingCount === 0) source.dispose();\n },\n { once: true },\n );\n }\n }\n }\n\n return source;\n}\n"],"mappings":";;AAgCA,SAAgB,EACd,GAKA,GACiB;CACjB,IAAM,IAAO,EAAiB,GAC1B,IAA8B,EAAQ,EAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,GAEjE,IAAiB,EAAQ,KAAK,MAClC,EAAO,gBAAgB;EAErB,AADA,IAAgB,EAAQ,EAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,GACrD,EAAK,OAAO;CACd,CAAC,CACH,GAEM,IAA0B;EAC9B,IAAI,UAAU;GACZ,OAAO;EACT;EAEA,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,OAAK,YAET;SAAK,IAAM,KAAS,GAClB,EAAM;IAGR,EAAK,QAAQ;GAHL;EAIV;EAEA,IAAI,WAAW;GACb,OAAO,EAAK;EACd;EAEA,UAAU,GAAU;GAClB,OAAO,EAAK,UAAU,CAAQ;EAChC;EAEA,CAAC,OAAO,WAAW;GACjB,EAAO,QAAQ;EACjB;CACF,GASM,IAAkB,EACrB,KAAK,MAAM,EAAE,cAAc,CAAC,CAC5B,QAAQ,MAAwB,aAAa,WAAW;CAE3D,IAAI,EAAgB,WAAW,EAAQ,UAAU,EAAgB,SAAS,GAAG;EAC3E,IAAI,IAAe,EAAgB,QAAQ,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;EAE7D,IAAI,MAAiB,GAEnB,EAAO,QAAQ;OAEf,KAAK,IAAM,KAAU,GACf,EAAO,WAEX,EAAO,iBACL,eACM;GAGJ,AAFA,KAEI,MAAiB,KAAG,EAAO,QAAQ;EACzC,GACA,EAAE,MAAM,GAAK,CACf;CAGN;CAEA,OAAO;AACT"}
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_utils.cjs"),n=require("./asyncSearch.cjs"),r=require("./asyncSource.cjs"),i=require("./pagination.cjs");let a=require("@vielzeug/arsenal");function o(o){let s=o.queryKey??t.defaultKeyOf,c=o.staleTime??0,l=Math.max(1,Math.trunc(o.limit??20)),u=1,d=``,f=o.filter,p=o.sort,m=[],h=0,g=null,_=0,v=``,y=null;o.snapshot&&(m=o.snapshot.items,h=o.snapshot.total,o.snapshot.page!==void 0&&(u=o.snapshot.page),o.snapshot.search&&(d=o.snapshot.search),_=Date.now());let b=[],x=i.createMeta({error:null,isLoading:!1,isSearchPending:!1,pageNumber:1,pageSize:l,totalItems:0}),S=()=>{let e=i.pageCount(h,l),t=i.clampPage(u,e);b=m,x=i.createMeta({error:g,isLoading:C.pendingCount()>0,isSearchPending:C.core.isScheduled,pageNumber:t,pageSize:l,totalItems:h})},C=r.createAsyncSource(o,s,S),{autoFetch:w,debounceMs:T,retryAttempts:E,retryDelay:D}=C;S();let O=()=>{C.disposed||C.core.notify()},k=n.createAsyncSearchCoordinator(C.core,O),A=e=>{y?.active&&(y.active=!1,y=null),m=e.items,h=e.total,u=i.clampPage(u,i.pageCount(h,l))},j=()=>({...f!==void 0&&{filter:f},...p!==void 0&&{sort:p},...d&&{search:d},limit:l,page:u}),M=n=>{let r=s(n);return c>0&&r===v&&h>0&&Date.now()-_<c&&!y?.active?Promise.resolve():(g=null,C.fetch(n,async(n,i,s)=>{let c=Date.now();try{let e=await(0,a.retry)(e=>o.fetch(n,e),{delay:D,signal:i,times:E+1});s()&&(A(e),g=null,v=r,_=Date.now(),o.onFetch?.({durationMs:Date.now()-c,query:n,status:`success`}))}catch(r){if(i.aborted)return;s()&&(y?.active?(y.active=!1,m=y.prevItems,h=y.prevTotal,y=null):(m=[],h=0),g=new e.SourcererError(t.extractError(r),{cause:r,context:{kind:`remote`,limit:n.limit,page:n.page,...n.search&&{search:n.search}}}),o.onFetch?.({durationMs:Date.now()-c,error:g,query:n,status:`error`}))}},()=>{O(),k.settleIfIdle(C.pendingCount())}))},N=()=>M(j());return w&&N(),C.startRefreshInterval(()=>void N()),{get current(){return b},get disposalSignal(){return C.disposalSignal},dispose:()=>{k.cancel(),k.dispose(),C.dispose()},get disposed(){return C.disposed},goTo(e){let t=h>0?i.clampPage(e,i.pageCount(h,l)):Math.max(1,Math.trunc(e));return t===u?Promise.resolve():(u=t,N())},goToLast(){let e=i.pageCount(h,l);return u===e?Promise.resolve():(u=e,N())},get meta(){return x},next(){let e=i.pageCount(h,l);return u>=e?Promise.resolve():(u++,N())},optimisticUpdate(t,n){if(y?.active)throw new e.SourcererError(`An optimistic update is already active. Rollback the previous update before starting another.`);let r=m,i=h;y={active:!0,prevItems:r,prevTotal:i};try{m=t(m)}catch(e){throw y=null,e}return h=n?.total??h,O(),()=>{y?.active&&(y.active=!1,m=r,h=i,y=null,O())}},patch(e){let t=!1;if(e.limit!==void 0){let n=Math.max(1,Math.trunc(e.limit));n!==l&&(l=n,t=!0)}if(`filter`in e&&(f=e.filter,t=!0),`sort`in e&&(p=e.sort,t=!0),`search`in e&&(e.search!==d||C.core.isScheduled)&&(d=e.search??``,t=!0),e.page!==void 0){let n=h>0?i.clampPage(e.page,i.pageCount(h,l)):Math.max(1,Math.trunc(e.page));n!==u&&(u=n,t=!0)}return t?(k.cancel(),(e.limit!==void 0||`filter`in e||`sort`in e||`search`in e)&&e.page===void 0&&(u=1),N()):Promise.resolve()},prev(){return u<=1?Promise.resolve():(u--,N())},get query(){return{...f!==void 0&&{filter:f},...p!==void 0&&{sort:p},...d&&{search:d},limit:l,page:u}},ready(e){return C.ready(e)},refresh(){return N()},reset(){return k.cancel(),d=``,u=1,f=o.filter,p=o.sort,N()},search(e,t){return t?.immediate?e===d&&!C.core.isScheduled?Promise.resolve():(k.cancel(),d=e,u=1,N()):e===d?Promise.resolve():(d=e,u=1,k.schedule(()=>void N(),T))},subscribe:e=>C.core.subscribe(e),[Symbol.dispose]:()=>{k.cancel(),k.dispose(),C.dispose()}}}exports.createRemoteSource=o;
1
+ const e=require("./_utils.cjs"),t=require("./errors.cjs"),n=require("./asyncSearch.cjs"),r=require("./asyncSource.cjs"),i=require("./pagination.cjs");let a=require("@vielzeug/arsenal");function o(o){let s=o.queryKey??e.defaultKeyOf,c=o.staleTime??0,l=e.clampPositiveInt(o.limit??20,`createRemoteSource`,`limit`),u=1,d=``,f=o.filter,p=o.sort,m=[],h=0,g=null,_=0,v=``,y=null;o.snapshot&&(m=o.snapshot.items,h=o.snapshot.total,o.snapshot.page!==void 0&&(u=o.snapshot.page),o.snapshot.search&&(d=o.snapshot.search),_=Date.now());let b=[],x=i.createMeta({error:null,isLoading:!1,isSearchPending:!1,pageNumber:1,pageSize:l,totalItems:0}),S=()=>{let e=i.pageCount(h,l),t=i.clampPage(u,e);b=m,x=i.createMeta({error:g,isLoading:C.pendingCount()>0,isSearchPending:C.core.isScheduled,pageNumber:t,pageSize:l,totalItems:h})},C=r.createAsyncSource(o,s,S),{autoFetch:w,debounceMs:T,retryAttempts:E,retryDelay:D}=C;S();let O=()=>{C.disposed||C.core.notify()},k=n.createAsyncSearchCoordinator(C.core,O),A=e=>{y?.active&&(y.active=!1,y=null),m=e.items,h=e.total,u=i.clampPage(u,i.pageCount(h,l))},j=()=>({...f!==void 0&&{filter:f},...p!==void 0&&{sort:p},...d&&{search:d},limit:l,page:u}),M=n=>{let r=s(n);return c>0&&r===v&&h>0&&Date.now()-_<c&&!y?.active?Promise.resolve():(g=null,C.fetch(n,async(n,i,s)=>{let c=Date.now();try{let e=await(0,a.retry)(e=>o.fetch(n,e),{delay:D,signal:i,times:E+1});s()&&(A(e),g=null,v=r,_=Date.now(),o.onFetch?.({durationMs:Date.now()-c,query:n,status:`success`}))}catch(r){if(i.aborted)return;s()&&(y?.active?(y.active=!1,m=y.prevItems,h=y.prevTotal,y=null):(m=[],h=0),g=new t.SourcererError(e.extractError(r),{cause:r,context:{kind:`remote`,limit:n.limit,page:n.page,...n.search&&{search:n.search}}}),o.onFetch?.({durationMs:Date.now()-c,error:g,query:n,status:`error`}))}},()=>{O(),k.settleIfIdle(C.pendingCount())}))},N=()=>M(j());return w&&N(),C.startRefreshInterval(()=>void N()),{get current(){return b},get disposalSignal(){return C.disposalSignal},dispose:()=>{k.cancel(),k.dispose(),C.dispose()},get disposed(){return C.disposed},goTo(t){let n=h>0?i.clampPage(t,i.pageCount(h,l)):e.clampPositiveInt(t,`source.goTo`,`target`);return n===u?Promise.resolve():(u=n,N())},goToLast(){let e=i.pageCount(h,l);return u===e?Promise.resolve():(u=e,N())},get meta(){return x},next(){let e=i.pageCount(h,l);return u>=e?Promise.resolve():(u++,N())},optimisticUpdate(e,n){if(y?.active)throw new t.SourcererError(`An optimistic update is already active. Rollback the previous update before starting another.`);let r=m,i=h;y={active:!0,prevItems:r,prevTotal:i};try{m=e(m)}catch(e){throw y=null,e}return h=n?.total??h,O(),()=>{y?.active&&(y.active=!1,m=r,h=i,y=null,O())}},patch(t){let n=!1;if(t.limit!==void 0){let r=e.clampPositiveInt(t.limit,`source.patch`,`limit`);r!==l&&(l=r,n=!0)}if(`filter`in t&&(f=t.filter,n=!0),`sort`in t&&(p=t.sort,n=!0),`search`in t&&(t.search!==d||C.core.isScheduled)&&(d=t.search??``,n=!0),t.page!==void 0){let r=h>0?i.clampPage(t.page,i.pageCount(h,l)):e.clampPositiveInt(t.page,`source.patch`,`page`);r!==u&&(u=r,n=!0)}return n?(k.cancel(),(t.limit!==void 0||`filter`in t||`sort`in t||`search`in t)&&t.page===void 0&&(u=1),N()):Promise.resolve()},prev(){return u<=1?Promise.resolve():(u--,N())},get query(){return{...f!==void 0&&{filter:f},...p!==void 0&&{sort:p},...d&&{search:d},limit:l,page:u}},ready(e){return C.ready(e)},refresh(){return N()},reset(){return k.cancel(),d=``,u=1,f=o.filter,p=o.sort,N()},search(e,t){return t?.immediate?e===d&&!C.core.isScheduled?Promise.resolve():(k.cancel(),d=e,u=1,N()):e===d?Promise.resolve():(d=e,u=1,k.schedule(()=>void N(),T))},subscribe:e=>C.core.subscribe(e),[Symbol.dispose]:()=>{k.cancel(),k.dispose(),C.dispose()}}}exports.createRemoteSource=o;
2
2
  //# sourceMappingURL=remoteSource.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"remoteSource.cjs","names":[],"sources":["../src/remoteSource.ts"],"sourcesContent":["import type { RemoteConfig, RemoteSource, RemoteSourceQuery, SearchOptions } from './types';\n\nimport { defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSearchCoordinator } from './asyncSearch';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\n\n/** Creates a remote page-based source that fetches data from a network endpoint. */\nexport function createRemoteSource<T, TFilter = unknown, TSort = unknown>(\n cfg: RemoteConfig<T, TFilter, TSort>,\n): RemoteSource<T, TFilter, TSort> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Config defaults ─────────────────────────────────────────────────────────\n const staleTimeMs = cfg.staleTime ?? 0;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let page = 1;\n let search = '';\n let filter: TFilter | undefined = cfg.filter;\n let sort: TSort | undefined = cfg.sort;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let lastFetchTime = 0;\n let lastFetchKey = '';\n\n // Optimistic update state.\n let optimistic: { active: boolean; prevItems: readonly T[]; prevTotal: number } | null = null;\n\n // ── Snapshot pre-population ─────────────────────────────────────────────────\n if (cfg.snapshot) {\n items = cfg.snapshot.items;\n total = cfg.snapshot.total;\n\n if (cfg.snapshot.page !== undefined) page = cfg.snapshot.page;\n\n if (cfg.snapshot.search) search = cfg.snapshot.search;\n\n lastFetchTime = Date.now();\n }\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta = createMeta({\n error: null,\n isLoading: false,\n isSearchPending: false,\n pageNumber: 1,\n pageSize: limit,\n totalItems: 0,\n });\n\n const refreshMeta = () => {\n const pages = pageCount(total, limit);\n const safePage = clampPage(page, pages);\n\n // items is already the current page from the server — no local slicing needed.\n cachedCurrent = items;\n cachedMeta = createMeta({\n error,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageNumber: safePage,\n pageSize: limit,\n totalItems: total,\n });\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<RemoteSourceQuery<TFilter, TSort>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n // Initialise cache before first fetch.\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n const searchCoordinator = createAsyncSearchCoordinator(base.core, notifyListeners);\n\n // ── Assign result ───────────────────────────────────────────────────────────\n const assign = (result: Readonly<{ items: readonly T[]; total: number }>) => {\n if (optimistic?.active) {\n optimistic.active = false;\n optimistic = null;\n }\n\n items = result.items;\n total = result.total;\n page = clampPage(page, pageCount(total, limit));\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): RemoteSourceQuery<TFilter, TSort> => ({\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n });\n\n const fetchQuery = (q: RemoteSourceQuery<TFilter, TSort>): Promise<void> => {\n const key = keyOf(q);\n\n if (\n staleTimeMs > 0 &&\n key === lastFetchKey &&\n total > 0 &&\n Date.now() - lastFetchTime < staleTimeMs &&\n !optimistic?.active\n ) {\n return Promise.resolve();\n }\n\n error = null;\n\n return base.fetch(\n q,\n async (q: RemoteSourceQuery<TFilter, TSort>, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n assign(result);\n error = null;\n lastFetchKey = key;\n lastFetchTime = Date.now();\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n if (optimistic?.active) {\n optimistic.active = false;\n items = optimistic.prevItems;\n total = optimistic.prevTotal;\n optimistic = null;\n } else {\n items = [];\n total = 0;\n }\n\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'remote', limit: q.limit, page: q.page, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n searchCoordinator.settleIfIdle(base.pendingCount());\n },\n );\n };\n\n const doUpdate = () => fetchQuery(toRemoteQuery());\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doUpdate();\n\n base.startRefreshInterval(() => void doUpdate());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n searchCoordinator.cancel();\n searchCoordinator.dispose();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n goTo(target) {\n const clamped = total > 0 ? clampPage(target, pageCount(total, limit)) : Math.max(1, Math.trunc(target));\n\n if (clamped === page) return Promise.resolve();\n\n page = clamped;\n\n return doUpdate();\n },\n\n goToLast() {\n const last = pageCount(total, limit);\n\n if (page === last) return Promise.resolve();\n\n page = last;\n\n return doUpdate();\n },\n\n get meta() {\n return cachedMeta;\n },\n\n next() {\n const pages = pageCount(total, limit);\n\n if (page >= pages) return Promise.resolve();\n\n page++;\n\n return doUpdate();\n },\n\n optimisticUpdate(mutator, options) {\n if (optimistic?.active) {\n throw new SourcererError(\n 'An optimistic update is already active. Rollback the previous update before starting another.',\n );\n }\n\n const prevItems = items;\n const prevTotal = total;\n\n optimistic = { active: true, prevItems, prevTotal };\n\n try {\n items = mutator(items);\n } catch (e: unknown) {\n optimistic = null;\n throw e;\n }\n\n total = options?.total ?? total;\n notifyListeners();\n\n return () => {\n if (optimistic?.active) {\n optimistic.active = false;\n items = prevItems;\n total = prevTotal;\n optimistic = null;\n notifyListeners();\n }\n };\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n if ('filter' in changes) {\n filter = changes.filter;\n changed = true;\n }\n\n if ('sort' in changes) {\n sort = changes.sort;\n changed = true;\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (changes.page !== undefined) {\n const next =\n total > 0 ? clampPage(changes.page, pageCount(total, limit)) : Math.max(1, Math.trunc(changes.page));\n\n if (next !== page) {\n page = next;\n changed = true;\n }\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n searchCoordinator.cancel();\n\n // Reset to page 1 when non-page query fields changed without explicit page\n if (\n (changes.limit !== undefined || 'filter' in changes || 'sort' in changes || 'search' in changes) &&\n changes.page === undefined\n ) {\n page = 1;\n }\n\n return doUpdate();\n },\n\n prev() {\n if (page <= 1) return Promise.resolve();\n\n page--;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n searchCoordinator.cancel();\n search = '';\n page = 1;\n filter = cfg.filter;\n sort = cfg.sort;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n searchCoordinator.cancel();\n search = q;\n page = 1;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n search = q;\n page = 1;\n\n return searchCoordinator.schedule(() => void doUpdate(), 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":"remoteSource.cjs","names":[],"sources":["../src/remoteSource.ts"],"sourcesContent":["import type { RemoteConfig, RemoteSource, RemoteSourceQuery, SearchOptions } from './types';\n\nimport { clampPositiveInt, defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSearchCoordinator } from './asyncSearch';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\nimport { clampPage, createMeta, pageCount } from './pagination';\n\n/** Creates a remote page-based source that fetches data from a network endpoint. */\nexport function createRemoteSource<T, TFilter = unknown, TSort = unknown>(\n cfg: RemoteConfig<T, TFilter, TSort>,\n): RemoteSource<T, TFilter, TSort> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Config defaults ─────────────────────────────────────────────────────────\n const staleTimeMs = cfg.staleTime ?? 0;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = clampPositiveInt(cfg.limit ?? 20, 'createRemoteSource', 'limit');\n let page = 1;\n let search = '';\n let filter: TFilter | undefined = cfg.filter;\n let sort: TSort | undefined = cfg.sort;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n let lastFetchTime = 0;\n let lastFetchKey = '';\n\n // Optimistic update state.\n let optimistic: { active: boolean; prevItems: readonly T[]; prevTotal: number } | null = null;\n\n // ── Snapshot pre-population ─────────────────────────────────────────────────\n if (cfg.snapshot) {\n items = cfg.snapshot.items;\n total = cfg.snapshot.total;\n\n if (cfg.snapshot.page !== undefined) page = cfg.snapshot.page;\n\n if (cfg.snapshot.search) search = cfg.snapshot.search;\n\n lastFetchTime = Date.now();\n }\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta = createMeta({\n error: null,\n isLoading: false,\n isSearchPending: false,\n pageNumber: 1,\n pageSize: limit,\n totalItems: 0,\n });\n\n const refreshMeta = () => {\n const pages = pageCount(total, limit);\n const safePage = clampPage(page, pages);\n\n // items is already the current page from the server — no local slicing needed.\n cachedCurrent = items;\n cachedMeta = createMeta({\n error,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageNumber: safePage,\n pageSize: limit,\n totalItems: total,\n });\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<RemoteSourceQuery<TFilter, TSort>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n // Initialise cache before first fetch.\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n const searchCoordinator = createAsyncSearchCoordinator(base.core, notifyListeners);\n\n // ── Assign result ───────────────────────────────────────────────────────────\n const assign = (result: Readonly<{ items: readonly T[]; total: number }>) => {\n if (optimistic?.active) {\n optimistic.active = false;\n optimistic = null;\n }\n\n items = result.items;\n total = result.total;\n page = clampPage(page, pageCount(total, limit));\n };\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): RemoteSourceQuery<TFilter, TSort> => ({\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n });\n\n const fetchQuery = (q: RemoteSourceQuery<TFilter, TSort>): Promise<void> => {\n const key = keyOf(q);\n\n if (\n staleTimeMs > 0 &&\n key === lastFetchKey &&\n total > 0 &&\n Date.now() - lastFetchTime < staleTimeMs &&\n !optimistic?.active\n ) {\n return Promise.resolve();\n }\n\n error = null;\n\n return base.fetch(\n q,\n async (q: RemoteSourceQuery<TFilter, TSort>, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n assign(result);\n error = null;\n lastFetchKey = key;\n lastFetchTime = Date.now();\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n if (optimistic?.active) {\n optimistic.active = false;\n items = optimistic.prevItems;\n total = optimistic.prevTotal;\n optimistic = null;\n } else {\n items = [];\n total = 0;\n }\n\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'remote', limit: q.limit, page: q.page, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n searchCoordinator.settleIfIdle(base.pendingCount());\n },\n );\n };\n\n const doUpdate = () => fetchQuery(toRemoteQuery());\n\n // ── Auto-fetch + refresh interval ───────────────────────────────────────────\n if (autoFetch) void doUpdate();\n\n base.startRefreshInterval(() => void doUpdate());\n\n // ── Public API ──────────────────────────────────────────────────────────────\n return {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return base.disposalSignal;\n },\n\n dispose: () => {\n searchCoordinator.cancel();\n searchCoordinator.dispose();\n base.dispose();\n },\n\n get disposed() {\n return base.disposed;\n },\n\n goTo(target) {\n const clamped =\n total > 0 ? clampPage(target, pageCount(total, limit)) : clampPositiveInt(target, 'source.goTo', 'target');\n\n if (clamped === page) return Promise.resolve();\n\n page = clamped;\n\n return doUpdate();\n },\n\n goToLast() {\n const last = pageCount(total, limit);\n\n if (page === last) return Promise.resolve();\n\n page = last;\n\n return doUpdate();\n },\n\n get meta() {\n return cachedMeta;\n },\n\n next() {\n const pages = pageCount(total, limit);\n\n if (page >= pages) return Promise.resolve();\n\n page++;\n\n return doUpdate();\n },\n\n optimisticUpdate(mutator, options) {\n if (optimistic?.active) {\n throw new SourcererError(\n 'An optimistic update is already active. Rollback the previous update before starting another.',\n );\n }\n\n const prevItems = items;\n const prevTotal = total;\n\n optimistic = { active: true, prevItems, prevTotal };\n\n try {\n items = mutator(items);\n } catch (e: unknown) {\n optimistic = null;\n throw e;\n }\n\n total = options?.total ?? total;\n notifyListeners();\n\n return () => {\n if (optimistic?.active) {\n optimistic.active = false;\n items = prevItems;\n total = prevTotal;\n optimistic = null;\n notifyListeners();\n }\n };\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = clampPositiveInt(changes.limit, 'source.patch', 'limit');\n\n if (next !== limit) {\n limit = next;\n changed = true;\n }\n }\n\n if ('filter' in changes) {\n filter = changes.filter;\n changed = true;\n }\n\n if ('sort' in changes) {\n sort = changes.sort;\n changed = true;\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n changed = true;\n }\n\n if (changes.page !== undefined) {\n const next =\n total > 0\n ? clampPage(changes.page, pageCount(total, limit))\n : clampPositiveInt(changes.page, 'source.patch', 'page');\n\n if (next !== page) {\n page = next;\n changed = true;\n }\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n searchCoordinator.cancel();\n\n // Reset to page 1 when non-page query fields changed without explicit page\n if (\n (changes.limit !== undefined || 'filter' in changes || 'sort' in changes || 'search' in changes) &&\n changes.page === undefined\n ) {\n page = 1;\n }\n\n return doUpdate();\n },\n\n prev() {\n if (page <= 1) return Promise.resolve();\n\n page--;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(filter !== undefined && { filter }),\n ...(sort !== undefined && { sort }),\n ...(search && { search }),\n limit,\n page,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n searchCoordinator.cancel();\n search = '';\n page = 1;\n filter = cfg.filter;\n sort = cfg.sort;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n searchCoordinator.cancel();\n search = q;\n page = 1;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n search = q;\n page = 1;\n\n return searchCoordinator.schedule(() => void doUpdate(), 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 +1 @@
1
- {"version":3,"file":"remoteSource.d.ts","sourceRoot":"","sources":["../src/remoteSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAoC,MAAM,SAAS,CAAC;AAQ5F,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtE,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,GACnC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAuXjC"}
1
+ {"version":3,"file":"remoteSource.d.ts","sourceRoot":"","sources":["../src/remoteSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAoC,MAAM,SAAS,CAAC;AAQ5F,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,EACtE,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,GACnC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CA0XjC"}