@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,125 +1,125 @@
1
- import { SourcererError as e } from "./errors.js";
2
- import { defaultKeyOf as t, extractError as n, retry as r } from "./_utils.js";
3
- import { createAsyncSearchCoordinator as i } from "./asyncSearch.js";
4
- import { createAsyncSource as a } from "./asyncSource.js";
1
+ import { clampPositiveInt as e, defaultKeyOf as t, extractError as n, retry as r } from "./_utils.js";
2
+ import { SourcererError as i } from "./errors.js";
3
+ import { createAsyncSearchCoordinator as a } from "./asyncSearch.js";
4
+ import { createAsyncSource as o } from "./asyncSource.js";
5
5
  //#region src/cursorSource.ts
6
- function o(o) {
7
- let s = o.queryKey ?? t, c = Math.max(1, Math.trunc(o.limit ?? 20)), l = "", u, d, f, p, m = [], h = 0, g = null, _ = [], v = {
6
+ function s(s) {
7
+ let c = s.queryKey ?? t, l = e(s.limit ?? 20, "createCursorSource", "limit"), u = "", d, f, p, m, h = [], g = 0, _ = null, v = [], y = {
8
8
  error: null,
9
9
  hasNextPage: !1,
10
10
  hasPrevPage: !1,
11
11
  isLoading: !1,
12
12
  isSearchPending: !1,
13
- pageSize: c,
13
+ pageSize: l,
14
14
  totalItems: 0
15
- }, y = () => {
16
- _ = m, v = {
17
- error: g,
18
- hasNextPage: f !== void 0,
19
- hasPrevPage: p !== void 0,
20
- isLoading: b.pendingCount() > 0,
21
- isSearchPending: b.core.isScheduled,
22
- pageSize: c,
23
- totalItems: h
15
+ }, b = () => {
16
+ v = h, y = {
17
+ error: _,
18
+ hasNextPage: p !== void 0,
19
+ hasPrevPage: m !== void 0,
20
+ isLoading: x.pendingCount() > 0,
21
+ isSearchPending: x.core.isScheduled,
22
+ pageSize: l,
23
+ totalItems: g
24
24
  };
25
- }, b = a(o, s, y), { autoFetch: x, debounceMs: S, retryAttempts: C, retryDelay: w } = b;
26
- y();
27
- let T = () => {
28
- b.disposed || b.core.notify();
29
- }, E = i(b.core, T), D = () => ({
30
- ...u !== void 0 && { after: u },
31
- ...d !== void 0 && { before: d },
32
- ...l && { search: l },
33
- limit: c
34
- }), O = (t) => (g = null, b.fetch(t, async (t, i, a) => {
35
- let s = Date.now();
25
+ }, x = o(s, c, b), { autoFetch: S, debounceMs: C, retryAttempts: w, retryDelay: T } = x;
26
+ b();
27
+ let E = () => {
28
+ x.disposed || x.core.notify();
29
+ }, D = a(x.core, E), O = () => ({
30
+ ...d !== void 0 && { after: d },
31
+ ...f !== void 0 && { before: f },
32
+ ...u && { search: u },
33
+ limit: l
34
+ }), k = (e) => (_ = null, x.fetch(e, async (e, t, a) => {
35
+ let o = Date.now();
36
36
  try {
37
- let e = await r((e) => o.fetch(t, e), {
38
- delay: w,
39
- signal: i,
40
- times: C + 1
37
+ let n = await r((t) => s.fetch(e, t), {
38
+ delay: T,
39
+ signal: t,
40
+ times: w + 1
41
41
  });
42
- a() && (m = e.items, h = e.total ?? e.items.length, f = e.nextCursor, p = e.prevCursor, g = null, o.onFetch?.({
43
- durationMs: Date.now() - s,
44
- query: t,
42
+ a() && (h = n.items, g = n.total ?? n.items.length, p = n.nextCursor, m = n.prevCursor, _ = null, s.onFetch?.({
43
+ durationMs: Date.now() - o,
44
+ query: e,
45
45
  status: "success"
46
46
  }));
47
47
  } catch (r) {
48
- if (i.aborted) return;
49
- a() && (m = [], h = 0, f = void 0, p = void 0, g = new e(n(r), {
48
+ if (t.aborted) return;
49
+ a() && (h = [], g = 0, p = void 0, m = void 0, _ = new i(n(r), {
50
50
  cause: r,
51
51
  context: {
52
52
  kind: "cursor",
53
- limit: t.limit,
54
- ...t.search && { search: t.search }
53
+ limit: e.limit,
54
+ ...e.search && { search: e.search }
55
55
  }
56
- }), o.onFetch?.({
57
- durationMs: Date.now() - s,
58
- error: g,
59
- query: t,
56
+ }), s.onFetch?.({
57
+ durationMs: Date.now() - o,
58
+ error: _,
59
+ query: e,
60
60
  status: "error"
61
61
  }));
62
62
  }
63
63
  }, () => {
64
- T(), E.settleIfIdle(b.pendingCount());
65
- })), k = () => O(D());
66
- return x && k(), b.startRefreshInterval(() => void k()), {
64
+ E(), D.settleIfIdle(x.pendingCount());
65
+ })), A = () => k(O());
66
+ return S && A(), x.startRefreshInterval(() => void A()), {
67
67
  get current() {
68
- return _;
68
+ return v;
69
69
  },
70
70
  get disposalSignal() {
71
- return b.disposalSignal;
71
+ return x.disposalSignal;
72
72
  },
73
73
  dispose: () => {
74
- E.cancel(), E.dispose(), b.dispose();
74
+ D.cancel(), D.dispose(), x.dispose();
75
75
  },
76
76
  get disposed() {
77
- return b.disposed;
77
+ return x.disposed;
78
78
  },
79
79
  get meta() {
80
- return v;
80
+ return y;
81
81
  },
82
82
  next() {
83
- return f ? (u = f, d = void 0, k()) : Promise.resolve();
83
+ return p ? (d = p, f = void 0, A()) : Promise.resolve();
84
84
  },
85
- patch(e) {
86
- let t = !1;
87
- if (e.limit !== void 0) {
88
- let n = Math.max(1, Math.trunc(e.limit));
89
- n !== c && (c = n, u = void 0, d = void 0, t = !0);
85
+ patch(t) {
86
+ let n = !1;
87
+ if (t.limit !== void 0) {
88
+ let r = e(t.limit, "source.patch", "limit");
89
+ r !== l && (l = r, d = void 0, f = void 0, n = !0);
90
90
  }
91
- return "search" in e && (e.search !== l || b.core.isScheduled) && (l = e.search ?? "", u = void 0, d = void 0, t = !0), t ? (E.cancel(), k()) : Promise.resolve();
91
+ return "search" in t && (t.search !== u || x.core.isScheduled) && (u = t.search ?? "", d = void 0, f = void 0, n = !0), n ? (D.cancel(), A()) : Promise.resolve();
92
92
  },
93
93
  prev() {
94
- return p ? (d = p, u = void 0, k()) : Promise.resolve();
94
+ return m ? (f = m, d = void 0, A()) : Promise.resolve();
95
95
  },
96
96
  get query() {
97
97
  return {
98
- ...u !== void 0 && { after: u },
99
- ...d !== void 0 && { before: d },
100
- ...l && { search: l },
101
- limit: c
98
+ ...d !== void 0 && { after: d },
99
+ ...f !== void 0 && { before: f },
100
+ ...u && { search: u },
101
+ limit: l
102
102
  };
103
103
  },
104
104
  ready(e) {
105
- return b.ready(e);
105
+ return x.ready(e);
106
106
  },
107
107
  refresh() {
108
- return k();
108
+ return A();
109
109
  },
110
110
  reset() {
111
- return E.cancel(), l = "", u = void 0, d = void 0, k();
111
+ return D.cancel(), u = "", d = void 0, f = void 0, A();
112
112
  },
113
113
  search(e, t) {
114
- return t?.immediate ? e === l && !b.core.isScheduled ? Promise.resolve() : (E.cancel(), l = e, u = void 0, d = void 0, k()) : e === l ? Promise.resolve() : (l = e, u = void 0, d = void 0, E.schedule(() => void k(), S));
114
+ return t?.immediate ? e === u && !x.core.isScheduled ? Promise.resolve() : (D.cancel(), u = e, d = void 0, f = void 0, A()) : e === u ? Promise.resolve() : (u = e, d = void 0, f = void 0, D.schedule(() => void A(), C));
115
115
  },
116
- subscribe: (e) => b.core.subscribe(e),
116
+ subscribe: (e) => x.core.subscribe(e),
117
117
  [Symbol.dispose]: () => {
118
- E.cancel(), E.dispose(), b.dispose();
118
+ D.cancel(), D.dispose(), x.dispose();
119
119
  }
120
120
  };
121
121
  }
122
122
  //#endregion
123
- export { o as createCursorSource };
123
+ export { s as createCursorSource };
124
124
 
125
125
  //# sourceMappingURL=cursorSource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursorSource.js","names":[],"sources":["../src/cursorSource.ts"],"sourcesContent":["import type { CursorConfig, CursorMeta, CursorSource, CursorSourceQuery, 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 a cursor-based (keyset-pagination) source that fetches data from a network endpoint. */\nexport function createCursorSource<T, TCursor = string>(cfg: CursorConfig<T, TCursor>): CursorSource<T, TCursor> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = Math.max(1, Math.trunc(cfg.limit ?? 20));\n let search = '';\n let afterCursor: TCursor | undefined;\n let beforeCursor: TCursor | undefined;\n let nextCursor: TCursor | undefined;\n let prevCursor: TCursor | undefined;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta: CursorMeta = {\n error: null,\n hasNextPage: false,\n hasPrevPage: false,\n isLoading: false,\n isSearchPending: false,\n pageSize: limit,\n totalItems: 0,\n };\n\n const refreshMeta = () => {\n cachedCurrent = items;\n cachedMeta = {\n error,\n hasNextPage: nextCursor !== undefined,\n hasPrevPage: prevCursor !== undefined,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageSize: limit,\n totalItems: total,\n };\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<CursorSourceQuery<TCursor>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n const searchCoordinator = createAsyncSearchCoordinator(base.core, notifyListeners);\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): CursorSourceQuery<TCursor> => ({\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n });\n\n const fetchQuery = (q: CursorSourceQuery<TCursor>): Promise<void> => {\n error = null;\n\n return base.fetch(\n q,\n async (q, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n items = result.items;\n total = result.total ?? result.items.length;\n nextCursor = result.nextCursor;\n prevCursor = result.prevCursor;\n error = null;\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n items = [];\n total = 0;\n nextCursor = undefined;\n prevCursor = undefined;\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'cursor', limit: q.limit, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n 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 get meta() {\n return cachedMeta;\n },\n\n next() {\n if (!nextCursor) return Promise.resolve();\n\n afterCursor = nextCursor;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = Math.max(1, Math.trunc(changes.limit));\n\n if (next !== limit) {\n limit = next;\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n searchCoordinator.cancel();\n\n return doUpdate();\n },\n\n prev() {\n if (!prevCursor) return Promise.resolve();\n\n beforeCursor = prevCursor;\n afterCursor = undefined;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n searchCoordinator.cancel();\n search = '';\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n searchCoordinator.cancel();\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\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":"cursorSource.js","names":[],"sources":["../src/cursorSource.ts"],"sourcesContent":["import type { CursorConfig, CursorMeta, CursorSource, CursorSourceQuery, SearchOptions } from './types';\n\nimport { clampPositiveInt, defaultKeyOf, extractError, retry } from './_utils';\nimport { createAsyncSearchCoordinator } from './asyncSearch';\nimport { createAsyncSource } from './asyncSource';\nimport { SourcererError } from './errors';\n\n/** Creates a cursor-based (keyset-pagination) source that fetches data from a network endpoint. */\nexport function createCursorSource<T, TCursor = string>(cfg: CursorConfig<T, TCursor>): CursorSource<T, TCursor> {\n const keyOf = cfg.queryKey ?? defaultKeyOf;\n\n // ── Mutable state ───────────────────────────────────────────────────────────\n let limit = clampPositiveInt(cfg.limit ?? 20, 'createCursorSource', 'limit');\n let search = '';\n let afterCursor: TCursor | undefined;\n let beforeCursor: TCursor | undefined;\n let nextCursor: TCursor | undefined;\n let prevCursor: TCursor | undefined;\n let items: readonly T[] = [];\n let total = 0;\n let error: SourcererError | null = null;\n\n // ── Cached accessors ────────────────────────────────────────────────────────\n let cachedCurrent: readonly T[] = [];\n let cachedMeta: CursorMeta = {\n error: null,\n hasNextPage: false,\n hasPrevPage: false,\n isLoading: false,\n isSearchPending: false,\n pageSize: limit,\n totalItems: 0,\n };\n\n const refreshMeta = () => {\n cachedCurrent = items;\n cachedMeta = {\n error,\n hasNextPage: nextCursor !== undefined,\n hasPrevPage: prevCursor !== undefined,\n isLoading: base.pendingCount() > 0,\n isSearchPending: base.core.isScheduled,\n pageSize: limit,\n totalItems: total,\n };\n };\n\n // onBeforeNotify ensures refreshMeta() runs before any subscriber observes the new state,\n // eliminating the need for a parallel listeners Set.\n const base = createAsyncSource<CursorSourceQuery<TCursor>>(cfg, keyOf, refreshMeta);\n const { autoFetch, debounceMs, retryAttempts, retryDelay } = base;\n\n refreshMeta();\n\n const notifyListeners = () => {\n if (base.disposed) return;\n\n base.core.notify();\n };\n const searchCoordinator = createAsyncSearchCoordinator(base.core, notifyListeners);\n\n // ── Core fetch ──────────────────────────────────────────────────────────────\n const toRemoteQuery = (): CursorSourceQuery<TCursor> => ({\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n });\n\n const fetchQuery = (q: CursorSourceQuery<TCursor>): Promise<void> => {\n error = null;\n\n return base.fetch(\n q,\n async (q, signal, isLatest) => {\n const startTime = Date.now();\n\n try {\n const result = await retry((sig) => cfg.fetch(q, sig!), {\n delay: retryDelay,\n signal,\n times: retryAttempts + 1,\n });\n\n if (isLatest()) {\n items = result.items;\n total = result.total ?? result.items.length;\n nextCursor = result.nextCursor;\n prevCursor = result.prevCursor;\n error = null;\n cfg.onFetch?.({ durationMs: Date.now() - startTime, query: q, status: 'success' });\n }\n } catch (reason: unknown) {\n if (signal.aborted) return;\n\n if (isLatest()) {\n items = [];\n total = 0;\n nextCursor = undefined;\n prevCursor = undefined;\n error = new SourcererError(extractError(reason), {\n cause: reason,\n context: { kind: 'cursor', limit: q.limit, ...(q.search && { search: q.search }) },\n });\n cfg.onFetch?.({ durationMs: Date.now() - startTime, error, query: q, status: 'error' });\n }\n }\n },\n () => {\n notifyListeners();\n\n 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 get meta() {\n return cachedMeta;\n },\n\n next() {\n if (!nextCursor) return Promise.resolve();\n\n afterCursor = nextCursor;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n patch(changes) {\n let changed = false;\n\n if (changes.limit !== undefined) {\n const next = clampPositiveInt(changes.limit, 'source.patch', 'limit');\n\n if (next !== limit) {\n limit = next;\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n }\n\n // `|| base.core.isScheduled` also catches patching in the same search text as an already-pending\n // debounced search() call — that still needs flushing, not silently dropping as a no-op.\n if ('search' in changes && (changes.search !== search || base.core.isScheduled)) {\n search = changes.search ?? '';\n afterCursor = undefined;\n beforeCursor = undefined;\n changed = true;\n }\n\n if (!changed) return Promise.resolve();\n\n // patch() is documented as a single atomic fetch — cancel any debounced search()\n // still pending so it doesn't fire a second, redundant fetch afterwards.\n searchCoordinator.cancel();\n\n return doUpdate();\n },\n\n prev() {\n if (!prevCursor) return Promise.resolve();\n\n beforeCursor = prevCursor;\n afterCursor = undefined;\n\n return doUpdate();\n },\n\n get query() {\n return {\n ...(afterCursor !== undefined && { after: afterCursor }),\n ...(beforeCursor !== undefined && { before: beforeCursor }),\n ...(search && { search }),\n limit,\n };\n },\n\n ready(timeout) {\n return base.ready(timeout);\n },\n\n refresh() {\n return doUpdate();\n },\n\n reset() {\n searchCoordinator.cancel();\n search = '';\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n },\n\n search(q, opts?: SearchOptions): Promise<void> {\n if (opts?.immediate) {\n // A pending debounced search for this same text still needs flushing —\n // `q === search` alone doesn't mean the fetch already happened.\n if (q === search && !base.core.isScheduled) return Promise.resolve();\n\n searchCoordinator.cancel();\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\n\n return doUpdate();\n }\n\n if (q === search) return Promise.resolve();\n\n search = q;\n afterCursor = undefined;\n beforeCursor = undefined;\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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"}
package/dist/derive.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./core.cjs");function t(t,n){let r=e.createSourceCore(),i=[],a=()=>{i=n(t.current)};a();let o=t.subscribe(()=>{a(),r.notify()});`disposalSignal`in t&&t.disposalSignal instanceof AbortSignal&&t.disposalSignal.addEventListener(`abort`,()=>r.dispose(),{once:!0});let s={get current(){return i},get disposalSignal(){return r.disposalSignal},dispose(){r.isDisposed||(o(),r.dispose())},get disposed(){return r.isDisposed},get meta(){return t.meta},subscribe(e){return r.subscribe(e)},[Symbol.dispose](){s.dispose()}};return s}exports.deriveSource=t;
1
+ const e=require("./core.cjs");function t(t,n){let r=e.createSourceCore(),i=[],a=()=>{i=n(t.current)};a();let o=t.subscribe(()=>{a(),r.notify()}),s={get current(){return i},get disposalSignal(){return r.disposalSignal},dispose(){r.isDisposed||(o(),r.dispose())},get disposed(){return r.isDisposed},get meta(){return t.meta},subscribe(e){return r.subscribe(e)},[Symbol.dispose](){s.dispose()}};return t.disposalSignal instanceof AbortSignal&&t.disposalSignal.addEventListener(`abort`,()=>s.dispose(),{once:!0}),s}exports.deriveSource=t;
2
2
  //# sourceMappingURL=derive.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"derive.cjs","names":[],"sources":["../src/derive.ts"],"sourcesContent":["import type { DerivedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Creates a reactive read-only projection derived from a parent source.\n *\n * The derived source mirrors the parent's `meta` and applies `transform` to `current`\n * whenever the parent changes. This is useful for client-side projections (e.g. additional\n * filtering, mapping, or sorting) over a remote source without re-fetching.\n *\n * Call `dispose()` to stop listening to the parent.\n *\n * @example\n * ```ts\n * const source = createRemoteSource<User>({ fetch: fetchUsers, limit: 20 });\n *\n * // Show only active users without re-fetching.\n * const activeUsers = deriveSource(source, (items) => items.filter((u) => u.active));\n *\n * activeUsers.subscribe(() => {\n * console.log(activeUsers.current, activeUsers.meta.totalItems);\n * });\n * ```\n */\nexport function deriveSource<T, U, TMeta>(\n parent: {\n readonly current: readonly T[];\n readonly meta: TMeta;\n subscribe(listener: () => void): () => void;\n },\n transform: (items: readonly T[]) => readonly U[],\n): DerivedSource<U, TMeta> {\n const core = createSourceCore();\n let cachedCurrent: readonly U[] = [];\n\n const applyTransform = () => {\n cachedCurrent = transform(parent.current);\n };\n\n applyTransform();\n\n const unsubscribeParent = parent.subscribe(() => {\n applyTransform();\n core.notify();\n });\n\n if ('disposalSignal' in parent && parent.disposalSignal instanceof AbortSignal) {\n parent.disposalSignal.addEventListener('abort', () => core.dispose(), { once: true });\n }\n\n const source: DerivedSource<U, TMeta> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n unsubscribeParent();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n get meta() {\n return parent.meta;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n"],"mappings":"8BAyBA,SAAgB,EACd,EAKA,EACyB,CACzB,IAAM,EAAO,EAAA,iBAAiB,EAC1B,EAA8B,CAAC,EAE7B,MAAuB,CAC3B,EAAgB,EAAU,EAAO,OAAO,CAC1C,EAEA,EAAe,EAEf,IAAM,EAAoB,EAAO,cAAgB,CAC/C,EAAe,EACf,EAAK,OAAO,CACd,CAAC,EAEG,mBAAoB,GAAU,EAAO,0BAA0B,aACjE,EAAO,eAAe,iBAAiB,YAAe,EAAK,QAAQ,EAAG,CAAE,KAAM,EAAK,CAAC,EAGtF,IAAM,EAAkC,CACtC,IAAI,SAAU,CACZ,OAAO,CACT,EAEA,IAAI,gBAAiB,CACnB,OAAO,EAAK,cACd,EAEA,SAAU,CACJ,EAAK,aAET,EAAkB,EAClB,EAAK,QAAQ,EACf,EAEA,IAAI,UAAW,CACb,OAAO,EAAK,UACd,EAEA,IAAI,MAAO,CACT,OAAO,EAAO,IAChB,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":"derive.cjs","names":[],"sources":["../src/derive.ts"],"sourcesContent":["import type { DerivedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Creates a reactive read-only projection derived from a parent source.\n *\n * The derived source mirrors the parent's `meta` and applies `transform` to `current`\n * whenever the parent changes. This is useful for client-side projections (e.g. additional\n * filtering, mapping, or sorting) over a remote source without re-fetching.\n *\n * Call `dispose()` to stop listening to the parent.\n *\n * @example\n * ```ts\n * const source = createRemoteSource<User>({ fetch: fetchUsers, limit: 20 });\n *\n * // Show only active users without re-fetching.\n * const activeUsers = deriveSource(source, (items) => items.filter((u) => u.active));\n *\n * activeUsers.subscribe(() => {\n * console.log(activeUsers.current, activeUsers.meta.totalItems);\n * });\n * ```\n */\nexport function deriveSource<T, U, TMeta>(\n parent: {\n readonly current: readonly T[];\n readonly disposalSignal?: AbortSignal;\n readonly meta: TMeta;\n subscribe(listener: () => void): () => void;\n },\n transform: (items: readonly T[]) => readonly U[],\n): DerivedSource<U, TMeta> {\n const core = createSourceCore();\n let cachedCurrent: readonly U[] = [];\n\n const applyTransform = () => {\n cachedCurrent = transform(parent.current);\n };\n\n applyTransform();\n\n const unsubscribeParent = parent.subscribe(() => {\n applyTransform();\n core.notify();\n });\n\n const source: DerivedSource<U, TMeta> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n unsubscribeParent();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n get meta() {\n return parent.meta;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n // Only tracked when the parent actually exposes a disposalSignal — deriveSource() also\n // accepts minimal duck-typed parents (per its parameter type) that may not have one.\n // Routed through the same `dispose()` method above, not `core.dispose()` directly, so there's\n // exactly one cleanup path regardless of what triggered it.\n if (parent.disposalSignal instanceof AbortSignal) {\n parent.disposalSignal.addEventListener('abort', () => source.dispose(), { once: true });\n }\n\n return source;\n}\n"],"mappings":"8BAyBA,SAAgB,EACd,EAMA,EACyB,CACzB,IAAM,EAAO,EAAA,iBAAiB,EAC1B,EAA8B,CAAC,EAE7B,MAAuB,CAC3B,EAAgB,EAAU,EAAO,OAAO,CAC1C,EAEA,EAAe,EAEf,IAAM,EAAoB,EAAO,cAAgB,CAC/C,EAAe,EACf,EAAK,OAAO,CACd,CAAC,EAEK,EAAkC,CACtC,IAAI,SAAU,CACZ,OAAO,CACT,EAEA,IAAI,gBAAiB,CACnB,OAAO,EAAK,cACd,EAEA,SAAU,CACJ,EAAK,aAET,EAAkB,EAClB,EAAK,QAAQ,EACf,EAEA,IAAI,UAAW,CACb,OAAO,EAAK,UACd,EAEA,IAAI,MAAO,CACT,OAAO,EAAO,IAChB,EAEA,UAAU,EAAU,CAClB,OAAO,EAAK,UAAU,CAAQ,CAChC,EAEA,CAAC,OAAO,UAAW,CACjB,EAAO,QAAQ,CACjB,CACF,EAUA,OAJI,EAAO,0BAA0B,aACnC,EAAO,eAAe,iBAAiB,YAAe,EAAO,QAAQ,EAAG,CAAE,KAAM,EAAK,CAAC,EAGjF,CACT"}
package/dist/derive.d.ts CHANGED
@@ -22,6 +22,7 @@ import type { DerivedSource } from './types';
22
22
  */
23
23
  export declare function deriveSource<T, U, TMeta>(parent: {
24
24
  readonly current: readonly T[];
25
+ readonly disposalSignal?: AbortSignal;
25
26
  readonly meta: TMeta;
26
27
  subscribe(listener: () => void): () => void;
27
28
  }, transform: (items: readonly T[]) => readonly U[]): DerivedSource<U, TMeta>;
@@ -1 +1 @@
1
- {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EACtC,MAAM,EAAE;IACN,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C,EACD,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,GAC/C,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC,CAqDzB"}
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EACtC,MAAM,EAAE;IACN,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C,EACD,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,GAC/C,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC,CAyDzB"}
package/dist/derive.js CHANGED
@@ -7,9 +7,7 @@ function t(t, n) {
7
7
  a();
8
8
  let o = t.subscribe(() => {
9
9
  a(), r.notify();
10
- });
11
- "disposalSignal" in t && t.disposalSignal instanceof AbortSignal && t.disposalSignal.addEventListener("abort", () => r.dispose(), { once: !0 });
12
- let s = {
10
+ }), s = {
13
11
  get current() {
14
12
  return i;
15
13
  },
@@ -32,7 +30,7 @@ function t(t, n) {
32
30
  s.dispose();
33
31
  }
34
32
  };
35
- return s;
33
+ return t.disposalSignal instanceof AbortSignal && t.disposalSignal.addEventListener("abort", () => s.dispose(), { once: !0 }), s;
36
34
  }
37
35
  //#endregion
38
36
  export { t as deriveSource };
@@ -1 +1 @@
1
- {"version":3,"file":"derive.js","names":[],"sources":["../src/derive.ts"],"sourcesContent":["import type { DerivedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Creates a reactive read-only projection derived from a parent source.\n *\n * The derived source mirrors the parent's `meta` and applies `transform` to `current`\n * whenever the parent changes. This is useful for client-side projections (e.g. additional\n * filtering, mapping, or sorting) over a remote source without re-fetching.\n *\n * Call `dispose()` to stop listening to the parent.\n *\n * @example\n * ```ts\n * const source = createRemoteSource<User>({ fetch: fetchUsers, limit: 20 });\n *\n * // Show only active users without re-fetching.\n * const activeUsers = deriveSource(source, (items) => items.filter((u) => u.active));\n *\n * activeUsers.subscribe(() => {\n * console.log(activeUsers.current, activeUsers.meta.totalItems);\n * });\n * ```\n */\nexport function deriveSource<T, U, TMeta>(\n parent: {\n readonly current: readonly T[];\n readonly meta: TMeta;\n subscribe(listener: () => void): () => void;\n },\n transform: (items: readonly T[]) => readonly U[],\n): DerivedSource<U, TMeta> {\n const core = createSourceCore();\n let cachedCurrent: readonly U[] = [];\n\n const applyTransform = () => {\n cachedCurrent = transform(parent.current);\n };\n\n applyTransform();\n\n const unsubscribeParent = parent.subscribe(() => {\n applyTransform();\n core.notify();\n });\n\n if ('disposalSignal' in parent && parent.disposalSignal instanceof AbortSignal) {\n parent.disposalSignal.addEventListener('abort', () => core.dispose(), { once: true });\n }\n\n const source: DerivedSource<U, TMeta> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n unsubscribeParent();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n get meta() {\n return parent.meta;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n return source;\n}\n"],"mappings":";;AAyBA,SAAgB,EACd,GAKA,GACyB;CACzB,IAAM,IAAO,EAAiB,GAC1B,IAA8B,CAAC,GAE7B,UAAuB;EAC3B,IAAgB,EAAU,EAAO,OAAO;CAC1C;CAEA,EAAe;CAEf,IAAM,IAAoB,EAAO,gBAAgB;EAE/C,AADA,EAAe,GACf,EAAK,OAAO;CACd,CAAC;CAED,AAAI,oBAAoB,KAAU,EAAO,0BAA0B,eACjE,EAAO,eAAe,iBAAiB,eAAe,EAAK,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC;CAGtF,IAAM,IAAkC;EACtC,IAAI,UAAU;GACZ,OAAO;EACT;EAEA,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,EAAK,eAET,EAAkB,GAClB,EAAK,QAAQ;EACf;EAEA,IAAI,WAAW;GACb,OAAO,EAAK;EACd;EAEA,IAAI,OAAO;GACT,OAAO,EAAO;EAChB;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":"derive.js","names":[],"sources":["../src/derive.ts"],"sourcesContent":["import type { DerivedSource } from './types';\n\nimport { createSourceCore } from './core';\n\n/**\n * Creates a reactive read-only projection derived from a parent source.\n *\n * The derived source mirrors the parent's `meta` and applies `transform` to `current`\n * whenever the parent changes. This is useful for client-side projections (e.g. additional\n * filtering, mapping, or sorting) over a remote source without re-fetching.\n *\n * Call `dispose()` to stop listening to the parent.\n *\n * @example\n * ```ts\n * const source = createRemoteSource<User>({ fetch: fetchUsers, limit: 20 });\n *\n * // Show only active users without re-fetching.\n * const activeUsers = deriveSource(source, (items) => items.filter((u) => u.active));\n *\n * activeUsers.subscribe(() => {\n * console.log(activeUsers.current, activeUsers.meta.totalItems);\n * });\n * ```\n */\nexport function deriveSource<T, U, TMeta>(\n parent: {\n readonly current: readonly T[];\n readonly disposalSignal?: AbortSignal;\n readonly meta: TMeta;\n subscribe(listener: () => void): () => void;\n },\n transform: (items: readonly T[]) => readonly U[],\n): DerivedSource<U, TMeta> {\n const core = createSourceCore();\n let cachedCurrent: readonly U[] = [];\n\n const applyTransform = () => {\n cachedCurrent = transform(parent.current);\n };\n\n applyTransform();\n\n const unsubscribeParent = parent.subscribe(() => {\n applyTransform();\n core.notify();\n });\n\n const source: DerivedSource<U, TMeta> = {\n get current() {\n return cachedCurrent;\n },\n\n get disposalSignal() {\n return core.disposalSignal;\n },\n\n dispose() {\n if (core.isDisposed) return;\n\n unsubscribeParent();\n core.dispose();\n },\n\n get disposed() {\n return core.isDisposed;\n },\n\n get meta() {\n return parent.meta;\n },\n\n subscribe(listener) {\n return core.subscribe(listener);\n },\n\n [Symbol.dispose]() {\n source.dispose();\n },\n };\n\n // Only tracked when the parent actually exposes a disposalSignal — deriveSource() also\n // accepts minimal duck-typed parents (per its parameter type) that may not have one.\n // Routed through the same `dispose()` method above, not `core.dispose()` directly, so there's\n // exactly one cleanup path regardless of what triggered it.\n if (parent.disposalSignal instanceof AbortSignal) {\n parent.disposalSignal.addEventListener('abort', () => source.dispose(), { once: true });\n }\n\n return source;\n}\n"],"mappings":";;AAyBA,SAAgB,EACd,GAMA,GACyB;CACzB,IAAM,IAAO,EAAiB,GAC1B,IAA8B,CAAC,GAE7B,UAAuB;EAC3B,IAAgB,EAAU,EAAO,OAAO;CAC1C;CAEA,EAAe;CAEf,IAAM,IAAoB,EAAO,gBAAgB;EAE/C,AADA,EAAe,GACf,EAAK,OAAO;CACd,CAAC,GAEK,IAAkC;EACtC,IAAI,UAAU;GACZ,OAAO;EACT;EAEA,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,EAAK,eAET,EAAkB,GAClB,EAAK,QAAQ;EACf;EAEA,IAAI,WAAW;GACb,OAAO,EAAK;EACd;EAEA,IAAI,OAAO;GACT,OAAO,EAAO;EAChB;EAEA,UAAU,GAAU;GAClB,OAAO,EAAK,UAAU,CAAQ;EAChC;EAEA,CAAC,OAAO,WAAW;GACjB,EAAO,QAAQ;EACjB;CACF;CAUA,OAJI,EAAO,0BAA0B,eACnC,EAAO,eAAe,iBAAiB,eAAe,EAAO,QAAQ,GAAG,EAAE,MAAM,GAAK,CAAC,GAGjF;AACT"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=!globalThis.__SOURCERER_PROD__;function t(t,n={}){if(!e)return()=>{};let r=`[sourcerer:devtools:${n.label??`source`}]`,i=t.meta,a=t.current.length;return t.subscribe(()=>{let e=t.meta;if(e)for(let t of Object.keys(e)){let n=i?.[t],a=e[t];Object.is(n,a)||console.debug(`${r} meta.${t}:`,n,`→`,a)}i=e;let n=t.current.length;n!==a&&(console.debug(`${r} current:`,a,`→`,n,`items`),a=n)})}exports.debugSource=t;
2
+ //# sourceMappingURL=devtools.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/sourcerer — debug utilities for reactive source state visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugSource } from '@vielzeug/sourcerer/devtools';\n * ```\n */\n\nconst isDev = !(globalThis as { __SOURCERER_PROD__?: boolean }).__SOURCERER_PROD__;\n\n/** Options for {@link debugSource}. */\nexport type SourcererDevtoolsOptions = {\n /** Label included in every log line, useful when debugging multiple sources at once. Default: `\"source\"`. */\n label?: string;\n};\n\n/**\n * Minimal shape `debugSource()` needs. `meta` is optional because `mergeSource()`'s\n * `MergedSource<T>` deliberately has none (parents may have incompatible meta shapes) — every\n * other source/combinator (`createLocalSource`/`createRemoteSource`/`createCursorSource`/\n * `createInfiniteSource`/`deriveSource`) does have one, and gets the full meta-diff logging;\n * `mergeSource()` results still get `current`'s item-count logging, just no `meta.*` lines.\n */\ntype DebuggableSource<TMeta extends Record<string, unknown>> = {\n readonly current: readonly unknown[];\n readonly meta?: TMeta;\n subscribe(listener: () => void): () => void;\n};\n\n/**\n * Attaches a `console.debug`-based observer to `source` that logs a distinct line per\n * observable state transition: any changed `meta` field (works generically across\n * `SourceMeta`/`CursorMeta`/`InfiniteMeta` — whatever shape this particular source has, when it\n * has one at all — see `DebuggableSource`'s doc comment for the `mergeSource()` exception) and\n * `current`'s item count.\n *\n * **Development only** by default — logging is a no-op when `__SOURCERER_PROD__` is set (the\n * same convention `_dev.ts` uses internally). Import from the dedicated `/devtools` sub-path so\n * the logging code is tree-shaken from production bundles entirely.\n *\n * @example\n * ```ts\n * import { createRemoteSource } from '@vielzeug/sourcerer';\n * import { debugSource } from '@vielzeug/sourcerer/devtools';\n *\n * const source = createRemoteSource({ fetch: fetchUsers, limit: 20 });\n * const detach = debugSource(source, { label: 'users' });\n * // [sourcerer:devtools:users] meta.isLoading: false → true\n * // [sourcerer:devtools:users] current: 0 → 20 items\n *\n * detach(); // stop logging\n * ```\n */\nexport function debugSource<TMeta extends Record<string, unknown> = Record<string, unknown>>(\n source: DebuggableSource<TMeta>,\n options: SourcererDevtoolsOptions = {},\n): () => void {\n if (!isDev) return () => {};\n\n const prefix = `[sourcerer:devtools:${options.label ?? 'source'}]`;\n let prevMeta = source.meta;\n let prevLength = source.current.length;\n\n return source.subscribe(() => {\n const nextMeta = source.meta;\n\n if (nextMeta) {\n for (const key of Object.keys(nextMeta)) {\n const prevValue = prevMeta?.[key as keyof TMeta];\n const nextValue = nextMeta[key as keyof TMeta];\n\n if (!Object.is(prevValue, nextValue)) {\n console.debug(`${prefix} meta.${key}:`, prevValue, '→', nextValue);\n }\n }\n }\n\n prevMeta = nextMeta;\n\n const nextLength = source.current.length;\n\n if (nextLength !== prevLength) {\n console.debug(`${prefix} current:`, prevLength, '→', nextLength, 'items');\n prevLength = nextLength;\n }\n });\n}\n"],"mappings":"mEASA,IAAM,EAAQ,CAAE,WAAgD,mBA6ChE,SAAgB,EACd,EACA,EAAoC,CAAC,EACzB,CACZ,GAAI,CAAC,EAAO,UAAa,CAAC,EAE1B,IAAM,EAAS,uBAAuB,EAAQ,OAAS,SAAS,GAC5D,EAAW,EAAO,KAClB,EAAa,EAAO,QAAQ,OAEhC,OAAO,EAAO,cAAgB,CAC5B,IAAM,EAAW,EAAO,KAExB,GAAI,EACF,IAAK,IAAM,KAAO,OAAO,KAAK,CAAQ,EAAG,CACvC,IAAM,EAAY,IAAW,GACvB,EAAY,EAAS,GAEtB,OAAO,GAAG,EAAW,CAAS,GACjC,QAAQ,MAAM,GAAG,EAAO,QAAQ,EAAI,GAAI,EAAW,IAAK,CAAS,CAErE,CAGF,EAAW,EAEX,IAAM,EAAa,EAAO,QAAQ,OAE9B,IAAe,IACjB,QAAQ,MAAM,GAAG,EAAO,WAAY,EAAY,IAAK,EAAY,OAAO,EACxE,EAAa,EAEjB,CAAC,CACH"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @vielzeug/sourcerer — debug utilities for reactive source state visualisation.
3
+ *
4
+ * Import from the dedicated sub-path so it is tree-shaken from production bundles:
5
+ * ```ts
6
+ * import { debugSource } from '@vielzeug/sourcerer/devtools';
7
+ * ```
8
+ */
9
+ /** Options for {@link debugSource}. */
10
+ export type SourcererDevtoolsOptions = {
11
+ /** Label included in every log line, useful when debugging multiple sources at once. Default: `"source"`. */
12
+ label?: string;
13
+ };
14
+ /**
15
+ * Minimal shape `debugSource()` needs. `meta` is optional because `mergeSource()`'s
16
+ * `MergedSource<T>` deliberately has none (parents may have incompatible meta shapes) — every
17
+ * other source/combinator (`createLocalSource`/`createRemoteSource`/`createCursorSource`/
18
+ * `createInfiniteSource`/`deriveSource`) does have one, and gets the full meta-diff logging;
19
+ * `mergeSource()` results still get `current`'s item-count logging, just no `meta.*` lines.
20
+ */
21
+ type DebuggableSource<TMeta extends Record<string, unknown>> = {
22
+ readonly current: readonly unknown[];
23
+ readonly meta?: TMeta;
24
+ subscribe(listener: () => void): () => void;
25
+ };
26
+ /**
27
+ * Attaches a `console.debug`-based observer to `source` that logs a distinct line per
28
+ * observable state transition: any changed `meta` field (works generically across
29
+ * `SourceMeta`/`CursorMeta`/`InfiniteMeta` — whatever shape this particular source has, when it
30
+ * has one at all — see `DebuggableSource`'s doc comment for the `mergeSource()` exception) and
31
+ * `current`'s item count.
32
+ *
33
+ * **Development only** by default — logging is a no-op when `__SOURCERER_PROD__` is set (the
34
+ * same convention `_dev.ts` uses internally). Import from the dedicated `/devtools` sub-path so
35
+ * the logging code is tree-shaken from production bundles entirely.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { createRemoteSource } from '@vielzeug/sourcerer';
40
+ * import { debugSource } from '@vielzeug/sourcerer/devtools';
41
+ *
42
+ * const source = createRemoteSource({ fetch: fetchUsers, limit: 20 });
43
+ * const detach = debugSource(source, { label: 'users' });
44
+ * // [sourcerer:devtools:users] meta.isLoading: false → true
45
+ * // [sourcerer:devtools:users] current: 0 → 20 items
46
+ *
47
+ * detach(); // stop logging
48
+ * ```
49
+ */
50
+ export declare function debugSource<TMeta extends Record<string, unknown> = Record<string, unknown>>(source: DebuggableSource<TMeta>, options?: SourcererDevtoolsOptions): () => void;
51
+ export {};
52
+ //# sourceMappingURL=devtools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,uCAAuC;AACvC,MAAM,MAAM,wBAAwB,GAAG;IACrC,6GAA6G;IAC7G,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,KAAK,gBAAgB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAC7D,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IACtB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzF,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC/B,OAAO,GAAE,wBAA6B,GACrC,MAAM,IAAI,CA8BZ"}
@@ -0,0 +1,20 @@
1
+ //#region src/devtools.ts
2
+ var e = !globalThis.__SOURCERER_PROD__;
3
+ function t(t, n = {}) {
4
+ if (!e) return () => {};
5
+ let r = `[sourcerer:devtools:${n.label ?? "source"}]`, i = t.meta, a = t.current.length;
6
+ return t.subscribe(() => {
7
+ let e = t.meta;
8
+ if (e) for (let t of Object.keys(e)) {
9
+ let n = i?.[t], a = e[t];
10
+ Object.is(n, a) || console.debug(`${r} meta.${t}:`, n, "→", a);
11
+ }
12
+ i = e;
13
+ let n = t.current.length;
14
+ n !== a && (console.debug(`${r} current:`, a, "→", n, "items"), a = n);
15
+ });
16
+ }
17
+ //#endregion
18
+ export { t as debugSource };
19
+
20
+ //# sourceMappingURL=devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/sourcerer — debug utilities for reactive source state visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugSource } from '@vielzeug/sourcerer/devtools';\n * ```\n */\n\nconst isDev = !(globalThis as { __SOURCERER_PROD__?: boolean }).__SOURCERER_PROD__;\n\n/** Options for {@link debugSource}. */\nexport type SourcererDevtoolsOptions = {\n /** Label included in every log line, useful when debugging multiple sources at once. Default: `\"source\"`. */\n label?: string;\n};\n\n/**\n * Minimal shape `debugSource()` needs. `meta` is optional because `mergeSource()`'s\n * `MergedSource<T>` deliberately has none (parents may have incompatible meta shapes) — every\n * other source/combinator (`createLocalSource`/`createRemoteSource`/`createCursorSource`/\n * `createInfiniteSource`/`deriveSource`) does have one, and gets the full meta-diff logging;\n * `mergeSource()` results still get `current`'s item-count logging, just no `meta.*` lines.\n */\ntype DebuggableSource<TMeta extends Record<string, unknown>> = {\n readonly current: readonly unknown[];\n readonly meta?: TMeta;\n subscribe(listener: () => void): () => void;\n};\n\n/**\n * Attaches a `console.debug`-based observer to `source` that logs a distinct line per\n * observable state transition: any changed `meta` field (works generically across\n * `SourceMeta`/`CursorMeta`/`InfiniteMeta` — whatever shape this particular source has, when it\n * has one at all — see `DebuggableSource`'s doc comment for the `mergeSource()` exception) and\n * `current`'s item count.\n *\n * **Development only** by default — logging is a no-op when `__SOURCERER_PROD__` is set (the\n * same convention `_dev.ts` uses internally). Import from the dedicated `/devtools` sub-path so\n * the logging code is tree-shaken from production bundles entirely.\n *\n * @example\n * ```ts\n * import { createRemoteSource } from '@vielzeug/sourcerer';\n * import { debugSource } from '@vielzeug/sourcerer/devtools';\n *\n * const source = createRemoteSource({ fetch: fetchUsers, limit: 20 });\n * const detach = debugSource(source, { label: 'users' });\n * // [sourcerer:devtools:users] meta.isLoading: false → true\n * // [sourcerer:devtools:users] current: 0 → 20 items\n *\n * detach(); // stop logging\n * ```\n */\nexport function debugSource<TMeta extends Record<string, unknown> = Record<string, unknown>>(\n source: DebuggableSource<TMeta>,\n options: SourcererDevtoolsOptions = {},\n): () => void {\n if (!isDev) return () => {};\n\n const prefix = `[sourcerer:devtools:${options.label ?? 'source'}]`;\n let prevMeta = source.meta;\n let prevLength = source.current.length;\n\n return source.subscribe(() => {\n const nextMeta = source.meta;\n\n if (nextMeta) {\n for (const key of Object.keys(nextMeta)) {\n const prevValue = prevMeta?.[key as keyof TMeta];\n const nextValue = nextMeta[key as keyof TMeta];\n\n if (!Object.is(prevValue, nextValue)) {\n console.debug(`${prefix} meta.${key}:`, prevValue, '→', nextValue);\n }\n }\n }\n\n prevMeta = nextMeta;\n\n const nextLength = source.current.length;\n\n if (nextLength !== prevLength) {\n console.debug(`${prefix} current:`, prevLength, '→', nextLength, 'items');\n prevLength = nextLength;\n }\n });\n}\n"],"mappings":";AASA,IAAM,IAAQ,CAAE,WAAgD;AA6ChE,SAAgB,EACd,GACA,IAAoC,CAAC,GACzB;CACZ,IAAI,CAAC,GAAO,aAAa,CAAC;CAE1B,IAAM,IAAS,uBAAuB,EAAQ,SAAS,SAAS,IAC5D,IAAW,EAAO,MAClB,IAAa,EAAO,QAAQ;CAEhC,OAAO,EAAO,gBAAgB;EAC5B,IAAM,IAAW,EAAO;EAExB,IAAI,GACF,KAAK,IAAM,KAAO,OAAO,KAAK,CAAQ,GAAG;GACvC,IAAM,IAAY,IAAW,IACvB,IAAY,EAAS;GAE3B,AAAK,OAAO,GAAG,GAAW,CAAS,KACjC,QAAQ,MAAM,GAAG,EAAO,QAAQ,EAAI,IAAI,GAAW,KAAK,CAAS;EAErE;EAGF,IAAW;EAEX,IAAM,IAAa,EAAO,QAAQ;EAElC,AAAI,MAAe,MACjB,QAAQ,MAAM,GAAG,EAAO,YAAY,GAAY,KAAK,GAAY,OAAO,GACxE,IAAa;CAEjB,CAAC;AACH"}
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=class e extends Error{#e;constructor(e,t={}){super(e,t.cause===void 0?void 0:{cause:t.cause}),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype),this.#e=t}get attempt(){return this.#e.attempt??0}get context(){return this.#e.context}static is(t){return t instanceof e}},t=class extends e{timeoutMs;constructor(e){super(`Source.ready() timed out after ${e}ms`),this.timeoutMs=e}},n=class extends e{constructor(){super(`Source disposed while waiting for ready()`)}};exports.SourceDisposedError=n,exports.SourceTimeoutError=t,exports.SourcererError=e;
1
+ var e=class e extends Error{#e;constructor(e,t={}){super(e,t.cause===void 0?void 0:{cause:t.cause}),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype),this.#e=t}get attempt(){return this.#e.attempt??0}get context(){return this.#e.context}static is(t){return t instanceof e}},t=class extends e{timeoutMs;constructor(e){super(`Source.ready() timed out after ${e}ms`),this.timeoutMs=e}},n=class extends e{constructor(){super(`Source disposed while waiting for ready()`)}};exports.SourcererDisposedError=n,exports.SourcererError=e,exports.SourcererTimeoutError=t;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":["#opts"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured context carried by `SourcererError`.\n * Discriminated on `kind` — narrow with `error.context?.kind` to access typed fields.\n *\n * @example\n * ```ts\n * if (source.meta.error?.context?.kind === 'remote') {\n * console.log('Failed page', source.meta.error.context.page);\n * }\n * ```\n */\nexport type SourcererErrorContext =\n | Readonly<{ kind: 'cursor'; limit: number; search?: string }>\n | Readonly<{ kind: 'infinite'; limit: number; page: number; search?: string }>\n | Readonly<{ kind: 'remote'; limit: number; page: number; search?: string }>;\n\n/**\n * Base class for all sourcerer errors. Catch with `instanceof SourcererError` to handle any\n * sourcerer-originated error regardless of specific subtype.\n *\n * Carries the original cause, structured context, and the attempt number.\n *\n * @example\n * ```ts\n * if (source.meta.error) {\n * console.error(source.meta.error.message, source.meta.error.context);\n * }\n * ```\n */\nexport class SourcererError extends Error {\n readonly #opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n };\n\n constructor(\n message: string,\n opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n } = {},\n ) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n this.#opts = opts;\n }\n\n get attempt(): number {\n return this.#opts.attempt ?? 0;\n }\n\n /** Context bag for the error — safe to log. */\n get context(): SourcererErrorContext | undefined {\n return this.#opts.context;\n }\n\n static is(err: unknown): err is SourcererError {\n return err instanceof SourcererError;\n }\n}\n\n/** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */\nexport class SourceTimeoutError extends SourcererError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Source.ready() timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown by `ready()` when the source is disposed while waiting. */\nexport class SourceDisposedError extends SourcererError {\n constructor() {\n super('Source disposed while waiting for ready()');\n }\n}\n"],"mappings":"AA6BA,IAAa,EAAb,MAAa,UAAuB,KAAM,CACxC,GAMA,YACE,EACA,EAII,CAAC,EACL,CACA,MAAM,EAAS,EAAK,QAAU,IAAA,GAAoC,IAAA,GAAxB,CAAE,MAAO,EAAK,KAAM,CAAa,EAC3E,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,EAChD,KAAKA,GAAQ,CACf,CAEA,IAAI,SAAkB,CACpB,OAAO,KAAKA,GAAM,SAAW,CAC/B,CAGA,IAAI,SAA6C,CAC/C,OAAO,KAAKA,GAAM,OACpB,CAEA,OAAO,GAAG,EAAqC,CAC7C,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAwC,CAAe,CAErD,UAEA,YAAY,EAAmB,CAC7B,MAAM,kCAAkC,EAAU,GAAG,EACrD,KAAK,UAAY,CACnB,CACF,EAGa,EAAb,cAAyC,CAAe,CACtD,aAAc,CACZ,MAAM,2CAA2C,CACnD,CACF"}
1
+ {"version":3,"file":"errors.cjs","names":["#opts"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured context carried by `SourcererError`.\n * Discriminated on `kind` — narrow with `error.context?.kind` to access typed fields.\n *\n * @example\n * ```ts\n * if (source.meta.error?.context?.kind === 'remote') {\n * console.log('Failed page', source.meta.error.context.page);\n * }\n * ```\n */\nexport type SourcererErrorContext =\n | Readonly<{ kind: 'cursor'; limit: number; search?: string }>\n | Readonly<{ kind: 'infinite'; limit: number; page: number; search?: string }>\n | Readonly<{ kind: 'remote'; limit: number; page: number; search?: string }>;\n\n/**\n * Base class for all sourcerer errors. Catch with `instanceof SourcererError` to handle any\n * sourcerer-originated error regardless of specific subtype.\n *\n * Carries the original cause, structured context, and the attempt number.\n *\n * @example\n * ```ts\n * if (source.meta.error) {\n * console.error(source.meta.error.message, source.meta.error.context);\n * }\n * ```\n */\nexport class SourcererError extends Error {\n readonly #opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n };\n\n constructor(\n message: string,\n opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n } = {},\n ) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n this.#opts = opts;\n }\n\n get attempt(): number {\n return this.#opts.attempt ?? 0;\n }\n\n /** Context bag for the error — safe to log. */\n get context(): SourcererErrorContext | undefined {\n return this.#opts.context;\n }\n\n static is(err: unknown): err is SourcererError {\n return err instanceof SourcererError;\n }\n}\n\n/** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */\nexport class SourcererTimeoutError extends SourcererError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Source.ready() timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown by `ready()` when the source is disposed while waiting. */\nexport class SourcererDisposedError extends SourcererError {\n constructor() {\n super('Source disposed while waiting for ready()');\n }\n}\n"],"mappings":"AA6BA,IAAa,EAAb,MAAa,UAAuB,KAAM,CACxC,GAMA,YACE,EACA,EAII,CAAC,EACL,CACA,MAAM,EAAS,EAAK,QAAU,IAAA,GAAoC,IAAA,GAAxB,CAAE,MAAO,EAAK,KAAM,CAAa,EAC3E,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,EAChD,KAAKA,GAAQ,CACf,CAEA,IAAI,SAAkB,CACpB,OAAO,KAAKA,GAAM,SAAW,CAC/B,CAGA,IAAI,SAA6C,CAC/C,OAAO,KAAKA,GAAM,OACpB,CAEA,OAAO,GAAG,EAAqC,CAC7C,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAA2C,CAAe,CAExD,UAEA,YAAY,EAAmB,CAC7B,MAAM,kCAAkC,EAAU,GAAG,EACrD,KAAK,UAAY,CACnB,CACF,EAGa,EAAb,cAA4C,CAAe,CACzD,aAAc,CACZ,MAAM,2CAA2C,CACnD,CACF"}
package/dist/errors.d.ts CHANGED
@@ -50,13 +50,13 @@ export declare class SourcererError extends Error {
50
50
  static is(err: unknown): err is SourcererError;
51
51
  }
52
52
  /** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */
53
- export declare class SourceTimeoutError extends SourcererError {
53
+ export declare class SourcererTimeoutError extends SourcererError {
54
54
  /** The configured timeout in milliseconds. */
55
55
  readonly timeoutMs: number;
56
56
  constructor(timeoutMs: number);
57
57
  }
58
58
  /** Thrown by `ready()` when the source is disposed while waiting. */
59
- export declare class SourceDisposedError extends SourcererError {
59
+ export declare class SourcererDisposedError extends SourcererError {
60
60
  constructor();
61
61
  }
62
62
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5D,QAAQ,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5E,QAAQ,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,SAAQ,KAAK;;gBAQrC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;QACJ,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;KACrC;IAQR,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+CAA+C;IAC/C,IAAI,OAAO,IAAI,qBAAqB,GAAG,SAAS,CAE/C;IAED,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc;CAG/C;AAED,4FAA4F;AAC5F,qBAAa,kBAAmB,SAAQ,cAAc;IACpD,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM;CAI9B;AAED,qEAAqE;AACrE,qBAAa,mBAAoB,SAAQ,cAAc;;CAItD"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5D,QAAQ,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5E,QAAQ,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,SAAQ,KAAK;;gBAQrC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;QACJ,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;KACrC;IAQR,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+CAA+C;IAC/C,IAAI,OAAO,IAAI,qBAAqB,GAAG,SAAS,CAE/C;IAED,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc;CAG/C;AAED,4FAA4F;AAC5F,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM;CAI9B;AAED,qEAAqE;AACrE,qBAAa,sBAAuB,SAAQ,cAAc;;CAIzD"}
package/dist/errors.js CHANGED
@@ -24,6 +24,6 @@ var e = class e extends Error {
24
24
  }
25
25
  };
26
26
  //#endregion
27
- export { n as SourceDisposedError, t as SourceTimeoutError, e as SourcererError };
27
+ export { n as SourcererDisposedError, e as SourcererError, t as SourcererTimeoutError };
28
28
 
29
29
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":["#opts"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured context carried by `SourcererError`.\n * Discriminated on `kind` — narrow with `error.context?.kind` to access typed fields.\n *\n * @example\n * ```ts\n * if (source.meta.error?.context?.kind === 'remote') {\n * console.log('Failed page', source.meta.error.context.page);\n * }\n * ```\n */\nexport type SourcererErrorContext =\n | Readonly<{ kind: 'cursor'; limit: number; search?: string }>\n | Readonly<{ kind: 'infinite'; limit: number; page: number; search?: string }>\n | Readonly<{ kind: 'remote'; limit: number; page: number; search?: string }>;\n\n/**\n * Base class for all sourcerer errors. Catch with `instanceof SourcererError` to handle any\n * sourcerer-originated error regardless of specific subtype.\n *\n * Carries the original cause, structured context, and the attempt number.\n *\n * @example\n * ```ts\n * if (source.meta.error) {\n * console.error(source.meta.error.message, source.meta.error.context);\n * }\n * ```\n */\nexport class SourcererError extends Error {\n readonly #opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n };\n\n constructor(\n message: string,\n opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n } = {},\n ) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n this.#opts = opts;\n }\n\n get attempt(): number {\n return this.#opts.attempt ?? 0;\n }\n\n /** Context bag for the error — safe to log. */\n get context(): SourcererErrorContext | undefined {\n return this.#opts.context;\n }\n\n static is(err: unknown): err is SourcererError {\n return err instanceof SourcererError;\n }\n}\n\n/** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */\nexport class SourceTimeoutError extends SourcererError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Source.ready() timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown by `ready()` when the source is disposed while waiting. */\nexport class SourceDisposedError extends SourcererError {\n constructor() {\n super('Source disposed while waiting for ready()');\n }\n}\n"],"mappings":";AA6BA,IAAa,IAAb,MAAa,UAAuB,MAAM;CACxC;CAMA,YACE,GACA,IAII,CAAC,GACL;EAIA,AAHA,MAAM,GAAS,EAAK,UAAU,KAAA,IAAoC,KAAA,IAAxB,EAAE,OAAO,EAAK,MAAM,CAAa,GAC3E,KAAK,OAAO,IAAI,OAAO,MACvB,OAAO,eAAe,MAAM,IAAI,OAAO,SAAS,GAChD,KAAKA,KAAQ;CACf;CAEA,IAAI,UAAkB;EACpB,OAAO,KAAKA,GAAM,WAAW;CAC/B;CAGA,IAAI,UAA6C;EAC/C,OAAO,KAAKA,GAAM;CACpB;CAEA,OAAO,GAAG,GAAqC;EAC7C,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAwC,EAAe;CAErD;CAEA,YAAY,GAAmB;EAE7B,AADA,MAAM,kCAAkC,EAAU,GAAG,GACrD,KAAK,YAAY;CACnB;AACF,GAGa,IAAb,cAAyC,EAAe;CACtD,cAAc;EACZ,MAAM,2CAA2C;CACnD;AACF"}
1
+ {"version":3,"file":"errors.js","names":["#opts"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Structured context carried by `SourcererError`.\n * Discriminated on `kind` — narrow with `error.context?.kind` to access typed fields.\n *\n * @example\n * ```ts\n * if (source.meta.error?.context?.kind === 'remote') {\n * console.log('Failed page', source.meta.error.context.page);\n * }\n * ```\n */\nexport type SourcererErrorContext =\n | Readonly<{ kind: 'cursor'; limit: number; search?: string }>\n | Readonly<{ kind: 'infinite'; limit: number; page: number; search?: string }>\n | Readonly<{ kind: 'remote'; limit: number; page: number; search?: string }>;\n\n/**\n * Base class for all sourcerer errors. Catch with `instanceof SourcererError` to handle any\n * sourcerer-originated error regardless of specific subtype.\n *\n * Carries the original cause, structured context, and the attempt number.\n *\n * @example\n * ```ts\n * if (source.meta.error) {\n * console.error(source.meta.error.message, source.meta.error.context);\n * }\n * ```\n */\nexport class SourcererError extends Error {\n readonly #opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n };\n\n constructor(\n message: string,\n opts: {\n readonly attempt?: number;\n readonly cause?: unknown;\n readonly context?: SourcererErrorContext;\n } = {},\n ) {\n super(message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n this.#opts = opts;\n }\n\n get attempt(): number {\n return this.#opts.attempt ?? 0;\n }\n\n /** Context bag for the error — safe to log. */\n get context(): SourcererErrorContext | undefined {\n return this.#opts.context;\n }\n\n static is(err: unknown): err is SourcererError {\n return err instanceof SourcererError;\n }\n}\n\n/** Thrown by `ready()` when the source timed out waiting for the first successful fetch. */\nexport class SourcererTimeoutError extends SourcererError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Source.ready() timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown by `ready()` when the source is disposed while waiting. */\nexport class SourcererDisposedError extends SourcererError {\n constructor() {\n super('Source disposed while waiting for ready()');\n }\n}\n"],"mappings":";AA6BA,IAAa,IAAb,MAAa,UAAuB,MAAM;CACxC;CAMA,YACE,GACA,IAII,CAAC,GACL;EAIA,AAHA,MAAM,GAAS,EAAK,UAAU,KAAA,IAAoC,KAAA,IAAxB,EAAE,OAAO,EAAK,MAAM,CAAa,GAC3E,KAAK,OAAO,IAAI,OAAO,MACvB,OAAO,eAAe,MAAM,IAAI,OAAO,SAAS,GAChD,KAAKA,KAAQ;CACf;CAEA,IAAI,UAAkB;EACpB,OAAO,KAAKA,GAAM,WAAW;CAC/B;CAGA,IAAI,UAA6C;EAC/C,OAAO,KAAKA,GAAM;CACpB;CAEA,OAAO,GAAG,GAAqC;EAC7C,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAA2C,EAAe;CAExD;CAEA,YAAY,GAAmB;EAE7B,AADA,MAAM,kCAAkC,EAAU,GAAG,GACrD,KAAK,YAAY;CACnB;AACF,GAGa,IAAb,cAA4C,EAAe;CACzD,cAAc;EACZ,MAAM,2CAA2C;CACnD;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"fetchManager.cjs","names":[],"sources":["../src/fetchManager.ts"],"sourcesContent":["/**\n * Internal fetch deduplication and in-flight request manager.\n *\n * - Deduplicates identical concurrent queries: joiners await the existing in-flight promise.\n * - Aborts superseded in-flight requests when a new query key arrives.\n * - Tracks pending count so callers can reflect loading state.\n *\n * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,\n * cursorSource, and infiniteSource.\n */\n\ntype InFlightEntry = { controller: AbortController; promise: Promise<void> };\n\nexport type FetchManager<TQuery> = {\n /** Abort all in-flight requests and clear the registry. */\n dispose(): void;\n /** Number of currently in-flight requests (including joiners). */\n readonly pendingCount: number;\n /**\n * Run a fetch for query `q`.\n *\n * - If an identical key is already in-flight, joins as a waiter.\n * - If a different key is in-flight, aborts it and starts a new request.\n * - Calls `onPendingChange()` whenever `pendingCount` changes.\n * - Passes an `isLatest()` predicate to `execute` so it can skip stale responses.\n */\n run(\n q: TQuery,\n execute: (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>,\n onPendingChange: () => void,\n ): Promise<void>;\n};\n\nexport function createFetchManager<TQuery>(keyOf: (q: TQuery) => string): FetchManager<TQuery> {\n const inflight = new Map<string, InFlightEntry>();\n let latestKey = '';\n let _pendingCount = 0;\n\n return {\n dispose() {\n for (const entry of inflight.values()) {\n entry.controller.abort();\n }\n\n inflight.clear();\n _pendingCount = 0;\n },\n\n get pendingCount() {\n return _pendingCount;\n },\n\n async run(q, execute, onPendingChange) {\n const key = keyOf(q);\n\n latestKey = key;\n\n // Abort and remove all superseded in-flight requests.\n for (const [k, entry] of inflight) {\n if (k !== key) {\n entry.controller.abort();\n inflight.delete(k);\n }\n }\n\n // Join an identical in-flight request rather than issuing a duplicate.\n const existing = inflight.get(key);\n\n if (existing) {\n _pendingCount++;\n onPendingChange();\n\n try {\n await existing.promise;\n } finally {\n _pendingCount--;\n onPendingChange();\n }\n\n return;\n }\n\n // New request.\n const controller = new AbortController();\n\n _pendingCount++;\n onPendingChange();\n\n const promise = execute(q, controller.signal, () => key === latestKey).finally(() => {\n inflight.delete(key);\n _pendingCount--;\n onPendingChange();\n });\n\n inflight.set(key, { controller, promise });\n await promise;\n },\n };\n}\n"],"mappings":"AAiCA,SAAgB,EAA2B,EAAoD,CAC7F,IAAM,EAAW,IAAI,IACjB,EAAY,GACZ,EAAgB,EAEpB,MAAO,CACL,SAAU,CACR,IAAK,IAAM,KAAS,EAAS,OAAO,EAClC,EAAM,WAAW,MAAM,EAGzB,EAAS,MAAM,EACf,EAAgB,CAClB,EAEA,IAAI,cAAe,CACjB,OAAO,CACT,EAEA,MAAM,IAAI,EAAG,EAAS,EAAiB,CACrC,IAAM,EAAM,EAAM,CAAC,EAEnB,EAAY,EAGZ,IAAK,GAAM,CAAC,EAAG,KAAU,EACnB,IAAM,IACR,EAAM,WAAW,MAAM,EACvB,EAAS,OAAO,CAAC,GAKrB,IAAM,EAAW,EAAS,IAAI,CAAG,EAEjC,GAAI,EAAU,CACZ,IACA,EAAgB,EAEhB,GAAI,CACF,MAAM,EAAS,OACjB,QAAU,CACR,IACA,EAAgB,CAClB,CAEA,MACF,CAGA,IAAM,EAAa,IAAI,gBAEvB,IACA,EAAgB,EAEhB,IAAM,EAAU,EAAQ,EAAG,EAAW,WAAc,IAAQ,CAAS,CAAC,CAAC,YAAc,CACnF,EAAS,OAAO,CAAG,EACnB,IACA,EAAgB,CAClB,CAAC,EAED,EAAS,IAAI,EAAK,CAAE,aAAY,SAAQ,CAAC,EACzC,MAAM,CACR,CACF,CACF"}
1
+ {"version":3,"file":"fetchManager.cjs","names":[],"sources":["../src/fetchManager.ts"],"sourcesContent":["/**\n * Internal fetch deduplication and in-flight request manager.\n *\n * - Deduplicates identical concurrent queries: joiners await the existing in-flight promise.\n * - Aborts superseded in-flight requests when a new query key arrives.\n * - Tracks pending count so callers can reflect loading state.\n *\n * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,\n * cursorSource, and infiniteSource.\n *\n * Deliberately not `@vielzeug/courier`'s query cache: retry itself already comes from the\n * one shared implementation both packages use (`retry` from `@vielzeug/arsenal` — see\n * `_utils.ts`), so there's no retry logic duplicated here to remove. What's left — \"only one\n * query is ever active per source instance; a new query always supersedes and aborts the\n * previous one, regardless of key\" — is a narrower, different contract than courier's, which\n * is a shared cross-consumer cache keyed by query (many independent entries, each with its own\n * staleness window, meant to be read by multiple observers). Forcing this single-active-query\n * source to route through a per-key shared cache would need as much glue code to reconcile the\n * two models as this file already is, for no behavioral win. See `docs/sourcerer/examples/\n * sourcerer-with-courier.md` for the actually-intended integration point: pass a courier-backed\n * client call as the `fetch` callback into `createRemoteSource()` — composition at the\n * transport layer, not inside this dedup manager.\n */\n\ntype InFlightEntry = { controller: AbortController; promise: Promise<void> };\n\nexport type FetchManager<TQuery> = {\n /** Abort all in-flight requests and clear the registry. */\n dispose(): void;\n /** Number of currently in-flight requests (including joiners). */\n readonly pendingCount: number;\n /**\n * Run a fetch for query `q`.\n *\n * - If an identical key is already in-flight, joins as a waiter.\n * - If a different key is in-flight, aborts it and starts a new request.\n * - Calls `onPendingChange()` whenever `pendingCount` changes.\n * - Passes an `isLatest()` predicate to `execute` so it can skip stale responses.\n */\n run(\n q: TQuery,\n execute: (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>,\n onPendingChange: () => void,\n ): Promise<void>;\n};\n\nexport function createFetchManager<TQuery>(keyOf: (q: TQuery) => string): FetchManager<TQuery> {\n const inflight = new Map<string, InFlightEntry>();\n let latestKey = '';\n let _pendingCount = 0;\n\n return {\n dispose() {\n for (const entry of inflight.values()) {\n entry.controller.abort();\n }\n\n inflight.clear();\n _pendingCount = 0;\n },\n\n get pendingCount() {\n return _pendingCount;\n },\n\n async run(q, execute, onPendingChange) {\n const key = keyOf(q);\n\n latestKey = key;\n\n // Abort and remove all superseded in-flight requests.\n for (const [k, entry] of inflight) {\n if (k !== key) {\n entry.controller.abort();\n inflight.delete(k);\n }\n }\n\n // Join an identical in-flight request rather than issuing a duplicate.\n const existing = inflight.get(key);\n\n if (existing) {\n _pendingCount++;\n onPendingChange();\n\n try {\n await existing.promise;\n } finally {\n _pendingCount--;\n onPendingChange();\n }\n\n return;\n }\n\n // New request.\n const controller = new AbortController();\n\n _pendingCount++;\n onPendingChange();\n\n const promise = execute(q, controller.signal, () => key === latestKey).finally(() => {\n inflight.delete(key);\n _pendingCount--;\n onPendingChange();\n });\n\n inflight.set(key, { controller, promise });\n await promise;\n },\n };\n}\n"],"mappings":"AA8CA,SAAgB,EAA2B,EAAoD,CAC7F,IAAM,EAAW,IAAI,IACjB,EAAY,GACZ,EAAgB,EAEpB,MAAO,CACL,SAAU,CACR,IAAK,IAAM,KAAS,EAAS,OAAO,EAClC,EAAM,WAAW,MAAM,EAGzB,EAAS,MAAM,EACf,EAAgB,CAClB,EAEA,IAAI,cAAe,CACjB,OAAO,CACT,EAEA,MAAM,IAAI,EAAG,EAAS,EAAiB,CACrC,IAAM,EAAM,EAAM,CAAC,EAEnB,EAAY,EAGZ,IAAK,GAAM,CAAC,EAAG,KAAU,EACnB,IAAM,IACR,EAAM,WAAW,MAAM,EACvB,EAAS,OAAO,CAAC,GAKrB,IAAM,EAAW,EAAS,IAAI,CAAG,EAEjC,GAAI,EAAU,CACZ,IACA,EAAgB,EAEhB,GAAI,CACF,MAAM,EAAS,OACjB,QAAU,CACR,IACA,EAAgB,CAClB,CAEA,MACF,CAGA,IAAM,EAAa,IAAI,gBAEvB,IACA,EAAgB,EAEhB,IAAM,EAAU,EAAQ,EAAG,EAAW,WAAc,IAAQ,CAAS,CAAC,CAAC,YAAc,CACnF,EAAS,OAAO,CAAG,EACnB,IACA,EAAgB,CAClB,CAAC,EAED,EAAS,IAAI,EAAK,CAAE,aAAY,SAAQ,CAAC,EACzC,MAAM,CACR,CACF,CACF"}
@@ -7,6 +7,19 @@
7
7
  *
8
8
  * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,
9
9
  * cursorSource, and infiniteSource.
10
+ *
11
+ * Deliberately not `@vielzeug/courier`'s query cache: retry itself already comes from the
12
+ * one shared implementation both packages use (`retry` from `@vielzeug/arsenal` — see
13
+ * `_utils.ts`), so there's no retry logic duplicated here to remove. What's left — "only one
14
+ * query is ever active per source instance; a new query always supersedes and aborts the
15
+ * previous one, regardless of key" — is a narrower, different contract than courier's, which
16
+ * is a shared cross-consumer cache keyed by query (many independent entries, each with its own
17
+ * staleness window, meant to be read by multiple observers). Forcing this single-active-query
18
+ * source to route through a per-key shared cache would need as much glue code to reconcile the
19
+ * two models as this file already is, for no behavioral win. See `docs/sourcerer/examples/
20
+ * sourcerer-with-courier.md` for the actually-intended integration point: pass a courier-backed
21
+ * client call as the `fetch` callback into `createRemoteSource()` — composition at the
22
+ * transport layer, not inside this dedup manager.
10
23
  */
11
24
  export type FetchManager<TQuery> = {
12
25
  /** Abort all in-flight requests and clear the registry. */
@@ -1 +1 @@
1
- {"version":3,"file":"fetchManager.d.ts","sourceRoot":"","sources":["../src/fetchManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,MAAM,YAAY,CAAC,MAAM,IAAI;IACjC,2DAA2D;IAC3D,OAAO,IAAI,IAAI,CAAC;IAChB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,GAAG,CACD,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,EACnF,eAAe,EAAE,MAAM,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAiE7F"}
1
+ {"version":3,"file":"fetchManager.d.ts","sourceRoot":"","sources":["../src/fetchManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,MAAM,MAAM,YAAY,CAAC,MAAM,IAAI;IACjC,2DAA2D;IAC3D,OAAO,IAAI,IAAI,CAAC;IAChB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,GAAG,CACD,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,EACnF,eAAe,EAAE,MAAM,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAiE7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"fetchManager.js","names":[],"sources":["../src/fetchManager.ts"],"sourcesContent":["/**\n * Internal fetch deduplication and in-flight request manager.\n *\n * - Deduplicates identical concurrent queries: joiners await the existing in-flight promise.\n * - Aborts superseded in-flight requests when a new query key arrives.\n * - Tracks pending count so callers can reflect loading state.\n *\n * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,\n * cursorSource, and infiniteSource.\n */\n\ntype InFlightEntry = { controller: AbortController; promise: Promise<void> };\n\nexport type FetchManager<TQuery> = {\n /** Abort all in-flight requests and clear the registry. */\n dispose(): void;\n /** Number of currently in-flight requests (including joiners). */\n readonly pendingCount: number;\n /**\n * Run a fetch for query `q`.\n *\n * - If an identical key is already in-flight, joins as a waiter.\n * - If a different key is in-flight, aborts it and starts a new request.\n * - Calls `onPendingChange()` whenever `pendingCount` changes.\n * - Passes an `isLatest()` predicate to `execute` so it can skip stale responses.\n */\n run(\n q: TQuery,\n execute: (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>,\n onPendingChange: () => void,\n ): Promise<void>;\n};\n\nexport function createFetchManager<TQuery>(keyOf: (q: TQuery) => string): FetchManager<TQuery> {\n const inflight = new Map<string, InFlightEntry>();\n let latestKey = '';\n let _pendingCount = 0;\n\n return {\n dispose() {\n for (const entry of inflight.values()) {\n entry.controller.abort();\n }\n\n inflight.clear();\n _pendingCount = 0;\n },\n\n get pendingCount() {\n return _pendingCount;\n },\n\n async run(q, execute, onPendingChange) {\n const key = keyOf(q);\n\n latestKey = key;\n\n // Abort and remove all superseded in-flight requests.\n for (const [k, entry] of inflight) {\n if (k !== key) {\n entry.controller.abort();\n inflight.delete(k);\n }\n }\n\n // Join an identical in-flight request rather than issuing a duplicate.\n const existing = inflight.get(key);\n\n if (existing) {\n _pendingCount++;\n onPendingChange();\n\n try {\n await existing.promise;\n } finally {\n _pendingCount--;\n onPendingChange();\n }\n\n return;\n }\n\n // New request.\n const controller = new AbortController();\n\n _pendingCount++;\n onPendingChange();\n\n const promise = execute(q, controller.signal, () => key === latestKey).finally(() => {\n inflight.delete(key);\n _pendingCount--;\n onPendingChange();\n });\n\n inflight.set(key, { controller, promise });\n await promise;\n },\n };\n}\n"],"mappings":";AAiCA,SAAgB,EAA2B,GAAoD;CAC7F,IAAM,oBAAW,IAAI,IAA2B,GAC5C,IAAY,IACZ,IAAgB;CAEpB,OAAO;EACL,UAAU;GACR,KAAK,IAAM,KAAS,EAAS,OAAO,GAClC,EAAM,WAAW,MAAM;GAIzB,AADA,EAAS,MAAM,GACf,IAAgB;EAClB;EAEA,IAAI,eAAe;GACjB,OAAO;EACT;EAEA,MAAM,IAAI,GAAG,GAAS,GAAiB;GACrC,IAAM,IAAM,EAAM,CAAC;GAEnB,IAAY;GAGZ,KAAK,IAAM,CAAC,GAAG,MAAU,GACvB,AAAI,MAAM,MACR,EAAM,WAAW,MAAM,GACvB,EAAS,OAAO,CAAC;GAKrB,IAAM,IAAW,EAAS,IAAI,CAAG;GAEjC,IAAI,GAAU;IAEZ,AADA,KACA,EAAgB;IAEhB,IAAI;KACF,MAAM,EAAS;IACjB,UAAU;KAER,AADA,KACA,EAAgB;IAClB;IAEA;GACF;GAGA,IAAM,IAAa,IAAI,gBAAgB;GAGvC,AADA,KACA,EAAgB;GAEhB,IAAM,IAAU,EAAQ,GAAG,EAAW,cAAc,MAAQ,CAAS,CAAC,CAAC,cAAc;IAGnF,AAFA,EAAS,OAAO,CAAG,GACnB,KACA,EAAgB;GAClB,CAAC;GAGD,AADA,EAAS,IAAI,GAAK;IAAE;IAAY;GAAQ,CAAC,GACzC,MAAM;EACR;CACF;AACF"}
1
+ {"version":3,"file":"fetchManager.js","names":[],"sources":["../src/fetchManager.ts"],"sourcesContent":["/**\n * Internal fetch deduplication and in-flight request manager.\n *\n * - Deduplicates identical concurrent queries: joiners await the existing in-flight promise.\n * - Aborts superseded in-flight requests when a new query key arrives.\n * - Tracks pending count so callers can reflect loading state.\n *\n * Not exported from the public API — consumed via `asyncSource.ts` by remoteSource,\n * cursorSource, and infiniteSource.\n *\n * Deliberately not `@vielzeug/courier`'s query cache: retry itself already comes from the\n * one shared implementation both packages use (`retry` from `@vielzeug/arsenal` — see\n * `_utils.ts`), so there's no retry logic duplicated here to remove. What's left — \"only one\n * query is ever active per source instance; a new query always supersedes and aborts the\n * previous one, regardless of key\" — is a narrower, different contract than courier's, which\n * is a shared cross-consumer cache keyed by query (many independent entries, each with its own\n * staleness window, meant to be read by multiple observers). Forcing this single-active-query\n * source to route through a per-key shared cache would need as much glue code to reconcile the\n * two models as this file already is, for no behavioral win. See `docs/sourcerer/examples/\n * sourcerer-with-courier.md` for the actually-intended integration point: pass a courier-backed\n * client call as the `fetch` callback into `createRemoteSource()` — composition at the\n * transport layer, not inside this dedup manager.\n */\n\ntype InFlightEntry = { controller: AbortController; promise: Promise<void> };\n\nexport type FetchManager<TQuery> = {\n /** Abort all in-flight requests and clear the registry. */\n dispose(): void;\n /** Number of currently in-flight requests (including joiners). */\n readonly pendingCount: number;\n /**\n * Run a fetch for query `q`.\n *\n * - If an identical key is already in-flight, joins as a waiter.\n * - If a different key is in-flight, aborts it and starts a new request.\n * - Calls `onPendingChange()` whenever `pendingCount` changes.\n * - Passes an `isLatest()` predicate to `execute` so it can skip stale responses.\n */\n run(\n q: TQuery,\n execute: (q: TQuery, signal: AbortSignal, isLatest: () => boolean) => Promise<void>,\n onPendingChange: () => void,\n ): Promise<void>;\n};\n\nexport function createFetchManager<TQuery>(keyOf: (q: TQuery) => string): FetchManager<TQuery> {\n const inflight = new Map<string, InFlightEntry>();\n let latestKey = '';\n let _pendingCount = 0;\n\n return {\n dispose() {\n for (const entry of inflight.values()) {\n entry.controller.abort();\n }\n\n inflight.clear();\n _pendingCount = 0;\n },\n\n get pendingCount() {\n return _pendingCount;\n },\n\n async run(q, execute, onPendingChange) {\n const key = keyOf(q);\n\n latestKey = key;\n\n // Abort and remove all superseded in-flight requests.\n for (const [k, entry] of inflight) {\n if (k !== key) {\n entry.controller.abort();\n inflight.delete(k);\n }\n }\n\n // Join an identical in-flight request rather than issuing a duplicate.\n const existing = inflight.get(key);\n\n if (existing) {\n _pendingCount++;\n onPendingChange();\n\n try {\n await existing.promise;\n } finally {\n _pendingCount--;\n onPendingChange();\n }\n\n return;\n }\n\n // New request.\n const controller = new AbortController();\n\n _pendingCount++;\n onPendingChange();\n\n const promise = execute(q, controller.signal, () => key === latestKey).finally(() => {\n inflight.delete(key);\n _pendingCount--;\n onPendingChange();\n });\n\n inflight.set(key, { controller, promise });\n await promise;\n },\n };\n}\n"],"mappings":";AA8CA,SAAgB,EAA2B,GAAoD;CAC7F,IAAM,oBAAW,IAAI,IAA2B,GAC5C,IAAY,IACZ,IAAgB;CAEpB,OAAO;EACL,UAAU;GACR,KAAK,IAAM,KAAS,EAAS,OAAO,GAClC,EAAM,WAAW,MAAM;GAIzB,AADA,EAAS,MAAM,GACf,IAAgB;EAClB;EAEA,IAAI,eAAe;GACjB,OAAO;EACT;EAEA,MAAM,IAAI,GAAG,GAAS,GAAiB;GACrC,IAAM,IAAM,EAAM,CAAC;GAEnB,IAAY;GAGZ,KAAK,IAAM,CAAC,GAAG,MAAU,GACvB,AAAI,MAAM,MACR,EAAM,WAAW,MAAM,GACvB,EAAS,OAAO,CAAC;GAKrB,IAAM,IAAW,EAAS,IAAI,CAAG;GAEjC,IAAI,GAAU;IAEZ,AADA,KACA,EAAgB;IAEhB,IAAI;KACF,MAAM,EAAS;IACjB,UAAU;KAER,AADA,KACA,EAAgB;IAClB;IAEA;GACF;GAGA,IAAM,IAAa,IAAI,gBAAgB;GAGvC,AADA,KACA,EAAgB;GAEhB,IAAM,IAAU,EAAQ,GAAG,EAAW,cAAc,MAAQ,CAAS,CAAC,CAAC,cAAc;IAGnF,AAFA,EAAS,OAAO,CAAG,GACnB,KACA,EAAgB;GAClB,CAAC;GAGD,AADA,EAAS,IAAI,GAAK;IAAE;IAAY;GAAQ,CAAC,GACzC,MAAM;EACR;CACF;AACF"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./applyQuery.cjs"),t=require("./errors.cjs"),n=require("./codecs.cjs"),r=require("./cursorSource.cjs"),i=require("./derive.cjs"),a=require("./infiniteSource.cjs"),o=require("./pagination.cjs"),s=require("./localSource.cjs"),c=require("./merge.cjs"),l=require("./remoteSource.cjs"),u=require("./prefetch.cjs"),d=require("./presets.cjs"),f=require("./state.cjs");exports.SourceDisposedError=t.SourceDisposedError,exports.SourceTimeoutError=t.SourceTimeoutError,exports.SourcererError=t.SourcererError,exports.applyQuery=e.applyQuery,exports.createCursorSource=r.createCursorSource,exports.createInfiniteSource=a.createInfiniteSource,exports.createLocalSource=s.createLocalSource,exports.createRemoteSource=l.createRemoteSource,exports.decodeQuery=n.decodeQuery,exports.deriveSource=i.deriveSource,exports.encodeQuery=n.encodeQuery,exports.filterContains=d.filterContains,exports.filterEquals=d.filterEquals,exports.filterRange=d.filterRange,exports.itemRange=o.itemRange,exports.mergeSource=c.mergeSource,exports.prefetchSource=u.prefetchSource,exports.prefetchSourceAndKeep=u.prefetchSourceAndKeep,exports.searchBy=d.searchBy,exports.sortBy=d.sortBy,exports.sourceState=f.sourceState;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./codecs.cjs"),n=require("./cursorSource.cjs"),r=require("./derive.cjs"),i=require("./infiniteSource.cjs"),a=require("./pagination.cjs"),o=require("./localSource.cjs"),s=require("./merge.cjs"),c=require("./remoteSource.cjs"),l=require("./prefetch.cjs"),u=require("./presets.cjs"),d=require("./state.cjs");exports.SourcererDisposedError=e.SourcererDisposedError,exports.SourcererError=e.SourcererError,exports.SourcererTimeoutError=e.SourcererTimeoutError,exports.createCursorSource=n.createCursorSource,exports.createInfiniteSource=i.createInfiniteSource,exports.createLocalSource=o.createLocalSource,exports.createRemoteSource=c.createRemoteSource,exports.decodeQuery=t.decodeQuery,exports.deriveSource=r.deriveSource,exports.encodeQuery=t.encodeQuery,exports.filterContains=u.filterContains,exports.filterEquals=u.filterEquals,exports.filterRange=u.filterRange,exports.itemRange=a.itemRange,exports.mergeSource=s.mergeSource,exports.prefetchSource=l.prefetchSource,exports.prefetchSourceAndKeep=l.prefetchSourceAndKeep,exports.searchBy=u.searchBy,exports.sortBy=u.sortBy,exports.sourceState=d.sourceState;