@noy-db/hub 0.2.0-pre.30 → 0.2.0-pre.31

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 (143) hide show
  1. package/dist/adapter/index.d.ts +9 -0
  2. package/dist/adapter/index.js +14 -0
  3. package/dist/attestation/index.d.ts +1 -1
  4. package/dist/attestation/index.js +3 -3
  5. package/dist/blobs/index.d.ts +3 -3
  6. package/dist/bundle/index.d.ts +3 -3
  7. package/dist/bundle/index.js +1 -1
  8. package/dist/{chunk-Y6YUWZTS.js → chunk-L5HHBOMS.js} +2 -2
  9. package/dist/{chunk-BVKLJBJJ.js → chunk-W5NVNJDX.js} +2 -1
  10. package/dist/{chunk-BVKLJBJJ.js.map → chunk-W5NVNJDX.js.map} +1 -1
  11. package/dist/consent/index.d.ts +2 -2
  12. package/dist/{decrypt-partition-Mhjh6nnG.d.ts → decrypt-partition-CXS5KopB.d.ts} +1 -1
  13. package/dist/derivations/index.d.ts +3 -3
  14. package/dist/{dev-unlock-DJ3IhgY9.d.ts → dev-unlock-CnHTTVea.d.ts} +1 -1
  15. package/dist/guards/index.d.ts +3 -3
  16. package/dist/{hash-DLwkYf1d.d.ts → hash-oc5paYl0.d.ts} +1 -1
  17. package/dist/history/index.d.ts +3 -3
  18. package/dist/i18n/index.d.ts +2 -2
  19. package/dist/{index-CMRIx_zx.d.ts → index-BmhGztUi.d.ts} +1 -1
  20. package/dist/index.d.ts +11 -11
  21. package/dist/index.js +2 -2
  22. package/dist/kernel/index.d.ts +2 -2
  23. package/dist/materialized-views/index.d.ts +3 -3
  24. package/dist/{mime-magic-CacDBwYp.d.ts → mime-magic-JeLKHRng.d.ts} +1 -1
  25. package/dist/{noydb-5MIBD77B.js → noydb-NAU2DTFP.js} +2 -2
  26. package/dist/noydb-NAU2DTFP.js.map +1 -0
  27. package/dist/overlay-views/index.d.ts +3 -3
  28. package/dist/periods/index.d.ts +2 -2
  29. package/dist/session/index.d.ts +3 -3
  30. package/dist/shadow/index.d.ts +2 -2
  31. package/dist/snapshots/index.d.ts +2 -2
  32. package/dist/store/index.d.ts +2 -2
  33. package/dist/sync/index.d.ts +1 -1
  34. package/dist/team/index.d.ts +2 -2
  35. package/dist/{transition-guard-J04-jime.d.ts → transition-guard-Dy3NioQr.d.ts} +1 -1
  36. package/dist/tx/index.d.ts +2 -2
  37. package/dist/{with-materialized-view-DNfzC5lH.d.ts → with-materialized-view-DIVeez0W.d.ts} +1 -1
  38. package/dist/{with-overlayed-view-qk3lbhOd.d.ts → with-overlayed-view-DVZrc5Hb.d.ts} +1 -1
  39. package/dist/{with-rollup-Dd3_ZG4b.d.ts → with-rollup-CbU-O4wP.d.ts} +1 -1
  40. package/package.json +62 -221
  41. package/dist/aggregate/index.cjs +0 -1144
  42. package/dist/aggregate/index.cjs.map +0 -1
  43. package/dist/aggregate/index.d.cts +0 -39
  44. package/dist/attestation/index.cjs +0 -305
  45. package/dist/attestation/index.cjs.map +0 -1
  46. package/dist/attestation/index.d.cts +0 -54
  47. package/dist/blobs/index.cjs +0 -1967
  48. package/dist/blobs/index.cjs.map +0 -1
  49. package/dist/blobs/index.d.cts +0 -48
  50. package/dist/bundle/index.cjs +0 -41045
  51. package/dist/bundle/index.cjs.map +0 -1
  52. package/dist/bundle/index.d.cts +0 -187
  53. package/dist/consent/index.cjs +0 -204
  54. package/dist/consent/index.cjs.map +0 -1
  55. package/dist/consent/index.d.cts +0 -27
  56. package/dist/crdt/index.cjs +0 -152
  57. package/dist/crdt/index.cjs.map +0 -1
  58. package/dist/crdt/index.d.cts +0 -30
  59. package/dist/decrypt-partition-C0iDpbSd.d.cts +0 -558
  60. package/dist/derivations/index.cjs +0 -469
  61. package/dist/derivations/index.cjs.map +0 -1
  62. package/dist/derivations/index.d.cts +0 -74
  63. package/dist/dev-unlock-0Z6yxfS7.d.cts +0 -263
  64. package/dist/discriminant-BN9REW3o.d.cts +0 -60
  65. package/dist/errors-BAWO5Z5a.d.cts +0 -1500
  66. package/dist/forget/index.cjs +0 -43
  67. package/dist/forget/index.cjs.map +0 -1
  68. package/dist/forget/index.d.cts +0 -1
  69. package/dist/guards/index.cjs +0 -455
  70. package/dist/guards/index.cjs.map +0 -1
  71. package/dist/guards/index.d.cts +0 -39
  72. package/dist/hash-Db8ncXGr.d.cts +0 -63
  73. package/dist/history/index.cjs +0 -1245
  74. package/dist/history/index.cjs.map +0 -1
  75. package/dist/history/index.d.cts +0 -65
  76. package/dist/i18n/index.cjs +0 -1280
  77. package/dist/i18n/index.cjs.map +0 -1
  78. package/dist/i18n/index.d.cts +0 -41
  79. package/dist/index-BMmajblo.d.cts +0 -362
  80. package/dist/index-C1SC1EPe.d.cts +0 -1325
  81. package/dist/index-xJEPkvMb.d.cts +0 -93
  82. package/dist/index.cjs +0 -48100
  83. package/dist/index.cjs.map +0 -1
  84. package/dist/index.d.cts +0 -1050
  85. package/dist/indexing/index.cjs +0 -803
  86. package/dist/indexing/index.cjs.map +0 -1
  87. package/dist/indexing/index.d.cts +0 -36
  88. package/dist/kernel/index.cjs +0 -751
  89. package/dist/kernel/index.cjs.map +0 -1
  90. package/dist/kernel/index.d.cts +0 -11
  91. package/dist/lazy-builder-eYZzLEL1.d.cts +0 -304
  92. package/dist/materialized-views/index.cjs +0 -1518
  93. package/dist/materialized-views/index.cjs.map +0 -1
  94. package/dist/materialized-views/index.d.cts +0 -187
  95. package/dist/mime-magic-sdMzsfVK.d.cts +0 -103
  96. package/dist/overlay-views/index.cjs +0 -399
  97. package/dist/overlay-views/index.cjs.map +0 -1
  98. package/dist/overlay-views/index.d.cts +0 -102
  99. package/dist/periods/index.cjs +0 -1041
  100. package/dist/periods/index.cjs.map +0 -1
  101. package/dist/periods/index.d.cts +0 -24
  102. package/dist/predicate-BmhBSPCH.d.cts +0 -267
  103. package/dist/query/index.cjs +0 -3480
  104. package/dist/query/index.cjs.map +0 -1
  105. package/dist/query/index.d.cts +0 -4
  106. package/dist/sealed-record/index.cjs +0 -139
  107. package/dist/sealed-record/index.cjs.map +0 -1
  108. package/dist/sealed-record/index.d.cts +0 -123
  109. package/dist/session/index.cjs +0 -495
  110. package/dist/session/index.cjs.map +0 -1
  111. package/dist/session/index.d.cts +0 -48
  112. package/dist/shadow/index.cjs +0 -133
  113. package/dist/shadow/index.cjs.map +0 -1
  114. package/dist/shadow/index.d.cts +0 -19
  115. package/dist/snapshots/index.cjs +0 -937
  116. package/dist/snapshots/index.cjs.map +0 -1
  117. package/dist/snapshots/index.d.cts +0 -30
  118. package/dist/store/index.cjs +0 -1091
  119. package/dist/store/index.cjs.map +0 -1
  120. package/dist/store/index.d.cts +0 -501
  121. package/dist/strategy-BSxFXGzb.d.cts +0 -110
  122. package/dist/strategy-mJExw4LQ.d.cts +0 -1069
  123. package/dist/sync/index.cjs +0 -1062
  124. package/dist/sync/index.cjs.map +0 -1
  125. package/dist/sync/index.d.cts +0 -45
  126. package/dist/team/index.cjs +0 -2805
  127. package/dist/team/index.cjs.map +0 -1
  128. package/dist/team/index.d.cts +0 -120
  129. package/dist/transition-guard-tWZIsaXe.d.cts +0 -165
  130. package/dist/tx/index.cjs +0 -614
  131. package/dist/tx/index.cjs.map +0 -1
  132. package/dist/tx/index.d.cts +0 -39
  133. package/dist/types-CdVfiQgt.d.cts +0 -15275
  134. package/dist/ulid-DRH25k3y.d.cts +0 -66
  135. package/dist/util/index.cjs +0 -237
  136. package/dist/util/index.cjs.map +0 -1
  137. package/dist/util/index.d.cts +0 -79
  138. package/dist/with-materialized-view-DfgPcvi9.d.cts +0 -27
  139. package/dist/with-overlayed-view-Cvfkx1lg.d.cts +0 -13
  140. package/dist/with-rollup-nIxo7h9z.d.cts +0 -47
  141. /package/dist/{noydb-5MIBD77B.js.map → adapter/index.js.map} +0 -0
  142. /package/dist/{chunk-Y6YUWZTS.js.map → chunk-L5HHBOMS.js.map} +0 -0
  143. /package/dist/{types-DHn9lEP0.d.ts → index-DHn9lEP0.d.ts} +0 -0
@@ -1,1144 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/aggregate/index.ts
21
- var aggregate_exports = {};
22
- __export(aggregate_exports, {
23
- Aggregation: () => Aggregation,
24
- GROUPBY_MAX_CARDINALITY: () => GROUPBY_MAX_CARDINALITY,
25
- GROUPBY_WARN_CARDINALITY: () => GROUPBY_WARN_CARDINALITY,
26
- GroupedAggregation: () => GroupedAggregation,
27
- GroupedQuery: () => GroupedQuery,
28
- GroupedQueryN: () => GroupedQueryN,
29
- avg: () => avg,
30
- buildLiveAggregation: () => buildLiveAggregation,
31
- count: () => count,
32
- groupAndReduce: () => groupAndReduce,
33
- max: () => max,
34
- min: () => min,
35
- reduceRecords: () => reduceRecords,
36
- resetGroupByWarnings: () => resetGroupByWarnings,
37
- sum: () => sum,
38
- withAggregate: () => withAggregate
39
- });
40
- module.exports = __toCommonJS(aggregate_exports);
41
-
42
- // src/aggregate/aggregation.ts
43
- function reduceRecords(records, spec) {
44
- const state = {};
45
- for (const key of Object.keys(spec)) {
46
- state[key] = spec[key].init();
47
- }
48
- for (const record of records) {
49
- for (const key of Object.keys(spec)) {
50
- state[key] = spec[key].step(state[key], record);
51
- }
52
- }
53
- const result = {};
54
- for (const key of Object.keys(spec)) {
55
- result[key] = spec[key].finalize(state[key]);
56
- }
57
- return result;
58
- }
59
- var LiveAggregationImpl = class {
60
- constructor(recompute, upstreams) {
61
- this.recompute = recompute;
62
- try {
63
- this.value = recompute();
64
- this.error = void 0;
65
- } catch (err) {
66
- this.value = void 0;
67
- this.error = err;
68
- }
69
- for (const upstream of upstreams) {
70
- const unsub = upstream.subscribe(() => this.refresh());
71
- this.unsubscribes.push(unsub);
72
- }
73
- }
74
- recompute;
75
- value;
76
- error;
77
- listeners = /* @__PURE__ */ new Set();
78
- unsubscribes = [];
79
- stopped = false;
80
- refresh() {
81
- if (this.stopped) return;
82
- try {
83
- this.value = this.recompute();
84
- this.error = void 0;
85
- } catch (err) {
86
- this.error = err;
87
- }
88
- for (const listener of this.listeners) {
89
- try {
90
- listener();
91
- } catch (err) {
92
- console.warn("[noy-db] LiveAggregation listener threw:", err);
93
- }
94
- }
95
- }
96
- subscribe(cb) {
97
- if (this.stopped) {
98
- return () => {
99
- };
100
- }
101
- this.listeners.add(cb);
102
- return () => {
103
- this.listeners.delete(cb);
104
- };
105
- }
106
- stop() {
107
- if (this.stopped) return;
108
- this.stopped = true;
109
- for (const unsub of this.unsubscribes) {
110
- try {
111
- unsub();
112
- } catch (err) {
113
- console.warn("[noy-db] LiveAggregation upstream unsubscribe threw:", err);
114
- }
115
- }
116
- this.unsubscribes.length = 0;
117
- this.listeners.clear();
118
- }
119
- };
120
- var Aggregation = class {
121
- constructor(executeRecords, spec, upstreams) {
122
- this.executeRecords = executeRecords;
123
- this.spec = spec;
124
- this.upstreams = upstreams;
125
- }
126
- executeRecords;
127
- spec;
128
- upstreams;
129
- /**
130
- * Execute the query and reduce the results synchronously.
131
- * Returns the reduced shape matching the spec — e.g. a spec of
132
- * `{ total: sum('amount'), n: count() }` returns
133
- * `{ total: number, n: number }`.
134
- */
135
- run() {
136
- return reduceRecords(this.executeRecords(), this.spec);
137
- }
138
- /**
139
- * Build a reactive `LiveAggregation<R>` that re-runs the reduction
140
- * whenever any upstream source notifies of a change. The initial
141
- * value is computed eagerly in the constructor, so consumers can
142
- * read `live.value` immediately after calling `.live()`.
143
- *
144
- * Always call `live.stop()` when finished — it tears down the
145
- * upstream subscriptions. Vue's `onUnmounted` is the canonical
146
- * place.
147
- *
148
- * **Implementation note:** every upstream change triggers a full
149
- * re-reduction. Incremental maintenance (O(1) per delta for
150
- * sum/count/avg via the reducer protocol's `remove()` method) is a
151
- * planned follow-up optimization — the protocol already supports
152
- * it, but the executor doesn't drive it yet. Consumers get
153
- * correct, reactive values today; future PRs can switch to
154
- * delta-based maintenance without changing this API.
155
- */
156
- live() {
157
- const recompute = () => reduceRecords(this.executeRecords(), this.spec);
158
- return new LiveAggregationImpl(recompute, this.upstreams);
159
- }
160
- };
161
- function buildLiveAggregation(recompute, upstreams) {
162
- return new LiveAggregationImpl(recompute, upstreams);
163
- }
164
-
165
- // src/money/fixed-point.ts
166
- function expandExponent(s) {
167
- const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
168
- if (!m) return s;
169
- const sign = m[1] === "-" ? "-" : "";
170
- const intp = m[2];
171
- const frac = m[3] ?? "";
172
- const exp = Number(m[4]);
173
- const digits = intp + frac;
174
- const pointPos = intp.length + exp;
175
- let body;
176
- if (pointPos <= 0) {
177
- body = "0." + "0".repeat(-pointPos) + digits;
178
- } else if (pointPos >= digits.length) {
179
- body = digits + "0".repeat(pointPos - digits.length);
180
- } else {
181
- body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
182
- }
183
- return sign + body;
184
- }
185
- function toCanonicalDecimalString(input) {
186
- let s;
187
- if (typeof input === "number") {
188
- if (!Number.isFinite(input)) return null;
189
- s = String(input);
190
- } else {
191
- s = input.trim();
192
- }
193
- s = expandExponent(s);
194
- if (s.startsWith("+")) s = s.slice(1);
195
- if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
196
- return s;
197
- }
198
- function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
199
- switch (mode) {
200
- case "up":
201
- return true;
202
- case "down":
203
- return false;
204
- case "ceil":
205
- return !negative;
206
- case "floor":
207
- return negative;
208
- case "half-up":
209
- return firstDiscarded >= 5;
210
- case "half-down":
211
- return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
212
- case "half-even":
213
- if (firstDiscarded > 5) return true;
214
- if (firstDiscarded < 5) return false;
215
- return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
216
- }
217
- }
218
- function parseToScaledInt(input, scale, rounding) {
219
- const canonical = toCanonicalDecimalString(input);
220
- if (canonical === null) return { ok: false, reason: "nonfinite" };
221
- const negative = canonical.startsWith("-");
222
- const unsigned = negative ? canonical.slice(1) : canonical;
223
- const dot = unsigned.indexOf(".");
224
- const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
225
- const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
226
- const intDigits = intPart === "" ? "0" : intPart;
227
- if (fracPart.length <= scale) {
228
- const keep2 = fracPart.padEnd(scale, "0");
229
- const magnitude2 = BigInt(intDigits + keep2);
230
- return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
231
- }
232
- const keep = fracPart.slice(0, scale);
233
- const tail = fracPart.slice(scale);
234
- const magnitudeDigits = intDigits + keep;
235
- let magnitude = BigInt(magnitudeDigits);
236
- if (/^0+$/.test(tail)) {
237
- return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
238
- }
239
- if (rounding === void 0) return { ok: false, reason: "precision" };
240
- const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
241
- const firstDiscarded = Number(tail[0]);
242
- const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
243
- if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
244
- magnitude += 1n;
245
- }
246
- return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
247
- }
248
- function formatScaledInt(value, scale) {
249
- const negative = value < 0n;
250
- const abs = (negative ? -value : value).toString();
251
- if (scale === 0) return (negative ? "-" : "") + abs;
252
- const padded = abs.padStart(scale + 1, "0");
253
- const cut = padded.length - scale;
254
- const intPart = padded.slice(0, cut);
255
- const fracPart = padded.slice(cut);
256
- return (negative ? "-" : "") + intPart + "." + fracPart;
257
- }
258
-
259
- // src/money/iso4217.ts
260
- var MINOR_UNITS = {
261
- // 2-decimal majors
262
- EUR: 2,
263
- USD: 2,
264
- GBP: 2,
265
- CHF: 2,
266
- CAD: 2,
267
- AUD: 2,
268
- NZD: 2,
269
- SGD: 2,
270
- HKD: 2,
271
- CNY: 2,
272
- INR: 2,
273
- BRL: 2,
274
- MXN: 2,
275
- ZAR: 2,
276
- RUB: 2,
277
- TRY: 2,
278
- PLN: 2,
279
- SEK: 2,
280
- NOK: 2,
281
- DKK: 2,
282
- CZK: 2,
283
- HUF: 2,
284
- RON: 2,
285
- ILS: 2,
286
- THB: 2,
287
- PHP: 2,
288
- MYR: 2,
289
- IDR: 2,
290
- AED: 2,
291
- SAR: 2,
292
- QAR: 2,
293
- EGP: 2,
294
- // 0-decimal
295
- JPY: 0,
296
- KRW: 0,
297
- ISK: 0,
298
- CLP: 0,
299
- VND: 0,
300
- XOF: 0,
301
- XAF: 0,
302
- PYG: 0,
303
- // 3-decimal
304
- BHD: 3,
305
- KWD: 3,
306
- OMR: 3,
307
- TND: 3,
308
- JOD: 3,
309
- IQD: 3,
310
- LYD: 3
311
- };
312
- function scaleForCurrency(code) {
313
- const v = MINOR_UNITS[code];
314
- return v === void 0 ? null : v;
315
- }
316
-
317
- // src/errors.ts
318
- var NoydbError = class extends Error {
319
- /** Machine-readable error code. Stable across library versions. */
320
- code;
321
- constructor(code, message) {
322
- super(message);
323
- this.name = "NoydbError";
324
- this.code = code;
325
- }
326
- };
327
- var GroupCardinalityError = class extends NoydbError {
328
- /** The field being grouped on. */
329
- field;
330
- /** Observed number of distinct groups at the moment the cap tripped. */
331
- cardinality;
332
- /** The cap that was exceeded. */
333
- maxGroups;
334
- constructor(field, cardinality, maxGroups) {
335
- super(
336
- "GROUP_CARDINALITY",
337
- `.groupBy("${field}") produced ${cardinality} distinct groups, exceeding the ${maxGroups}-group ceiling. This is almost always a query mistake \u2014 grouping on a high-uniqueness field like "id" or "createdAt" produces one bucket per record. Narrow the query with .where() before grouping, or group on a lower-cardinality field (status, category, clientId). If you genuinely need high-cardinality grouping, file an issue with your use case.`
338
- );
339
- this.name = "GroupCardinalityError";
340
- this.field = field;
341
- this.cardinality = cardinality;
342
- this.maxGroups = maxGroups;
343
- }
344
- };
345
- var LocaleNotSpecifiedError = class extends NoydbError {
346
- /** The field name that required a locale. */
347
- field;
348
- constructor(field, message) {
349
- super(
350
- "LOCALE_NOT_SPECIFIED",
351
- message ?? `Cannot read i18nText field "${field}" without a locale. Pass { locale } to get()/list()/query() or set a default via openVault(name, { locale }).`
352
- );
353
- this.name = "LocaleNotSpecifiedError";
354
- this.field = field;
355
- }
356
- };
357
-
358
- // src/money/descriptor.ts
359
- var MoneyUnsupportedError = class extends NoydbError {
360
- constructor(field, message) {
361
- super(
362
- "MONEY_UNSUPPORTED",
363
- message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
364
- );
365
- this.field = field;
366
- this.name = "MoneyUnsupportedError";
367
- }
368
- field;
369
- };
370
-
371
- // src/query/predicate.ts
372
- function readPath(record, path) {
373
- if (record === null || record === void 0) return void 0;
374
- if (!path.includes(".")) {
375
- return record[path];
376
- }
377
- const segments = path.split(".");
378
- let cursor = record;
379
- for (const segment of segments) {
380
- if (cursor === null || cursor === void 0) return void 0;
381
- cursor = cursor[segment];
382
- }
383
- return cursor;
384
- }
385
-
386
- // src/aggregate/canonical-key.ts
387
- function canonicalGroupKey(fields, row) {
388
- const sorted = [...fields].sort();
389
- const parts = [];
390
- for (const name of sorted) {
391
- const v = row[name];
392
- const serialised = v === void 0 ? "undefined" : JSON.stringify(v);
393
- parts.push(`${name}=${serialised}`);
394
- }
395
- return parts.join("|");
396
- }
397
-
398
- // src/money/money-reducer.ts
399
- function toScaledIntFromAny(v, scale) {
400
- if (typeof v === "bigint") return v;
401
- if (typeof v === "number") {
402
- const r = parseToScaledInt(v, scale);
403
- return r.ok ? r.value : null;
404
- }
405
- if (typeof v === "string") {
406
- if (!v.includes(".")) {
407
- try {
408
- return BigInt(v);
409
- } catch {
410
- return null;
411
- }
412
- }
413
- const r = parseToScaledInt(v, scale);
414
- return r.ok ? r.value : null;
415
- }
416
- return null;
417
- }
418
- function readMoney(record, field, desc) {
419
- const raw = readPath(record, field);
420
- if (raw === null || raw === void 0) return null;
421
- if (desc.mode === "fixed") {
422
- const cur = desc.fixedCurrency;
423
- const value2 = toScaledIntFromAny(raw, desc.scaleFor(cur));
424
- return value2 === null ? null : { currency: cur, value: value2 };
425
- }
426
- if (typeof raw !== "object") return null;
427
- const o = raw;
428
- if (typeof o.currency !== "string") return null;
429
- const scale = desc.allows(o.currency) ? desc.scaleFor(o.currency) : 0;
430
- const value = toScaledIntFromAny(o.amount, scale);
431
- return value === null ? null : { currency: o.currency, value };
432
- }
433
- function targetScaleFor(desc, currency) {
434
- if (desc.allows(currency)) return desc.scaleFor(currency);
435
- const s = scaleForCurrency(currency);
436
- if (s === null) {
437
- throw new Error(`money: cannot determine scale for conversion target "${currency}"`);
438
- }
439
- return s;
440
- }
441
- function parseRate(rate) {
442
- const s = String(rate).trim();
443
- const neg = s.startsWith("-");
444
- const body = neg ? s.slice(1) : s;
445
- const dot = body.indexOf(".");
446
- const intPart = dot === -1 ? body : body.slice(0, dot);
447
- const fracPart = dot === -1 ? "" : body.slice(dot + 1);
448
- const int = BigInt((intPart === "" ? "0" : intPart) + fracPart);
449
- return { int: neg ? -int : int, scale: fracPart.length };
450
- }
451
- function divRoundHalfEven(n, d) {
452
- const q = n / d;
453
- const r = n % d;
454
- const twiceR = (r < 0n ? -r : r) * 2n;
455
- if (twiceR < d) return q;
456
- if (twiceR > d) return q + (n < 0n ? -1n : 1n);
457
- return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n);
458
- }
459
- function convertScaled(value, srcScale, rate, targetScale) {
460
- const { int: rateInt, scale: rateScale } = parseRate(rate);
461
- const product = value * rateInt;
462
- const curScale = srcScale + rateScale;
463
- if (curScale === targetScale) return product;
464
- if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale);
465
- return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale));
466
- }
467
- function finalizeSum(state, desc, convertTo, fx) {
468
- if (convertTo !== void 0) {
469
- if (fx === void 0) {
470
- throw new Error(`money: sum convertTo "${convertTo}" requires an fx rate map`);
471
- }
472
- const targetScale = targetScaleFor(desc, convertTo);
473
- let total = 0n;
474
- for (const [cur, v] of state) {
475
- if (cur === convertTo) {
476
- total += convertScaled(v, desc.scaleFor(cur), 1, targetScale);
477
- continue;
478
- }
479
- const rate = fx[`${cur}->${convertTo}`];
480
- if (rate === void 0) {
481
- throw new Error(`money: no fx rate for "${cur}->${convertTo}"`);
482
- }
483
- total += convertScaled(v, desc.scaleFor(cur), rate, targetScale);
484
- }
485
- return formatScaledInt(total, targetScale);
486
- }
487
- if (desc.mode === "fixed") {
488
- const cur = desc.fixedCurrency;
489
- return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur));
490
- }
491
- const out = {};
492
- for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur));
493
- return out;
494
- }
495
- function moneySumReducer(field, desc, convertTo, fx) {
496
- return {
497
- op: "sum",
498
- field,
499
- init: () => /* @__PURE__ */ new Map(),
500
- step: (state, record) => {
501
- const m = readMoney(record, field, desc);
502
- if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value);
503
- return state;
504
- },
505
- remove: (state, record) => {
506
- const m = readMoney(record, field, desc);
507
- if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value);
508
- return state;
509
- },
510
- finalize: (state) => finalizeSum(state, desc, convertTo, fx)
511
- };
512
- }
513
- function extremum(values, op) {
514
- let out = values[0];
515
- for (let i = 1; i < values.length; i++) {
516
- const v = values[i];
517
- if (op === "min" ? v < out : v > out) out = v;
518
- }
519
- return out;
520
- }
521
- function moneyMinMaxReducer(op, field, desc) {
522
- return {
523
- op,
524
- field,
525
- init: () => /* @__PURE__ */ new Map(),
526
- step: (state, record) => {
527
- const m = readMoney(record, field, desc);
528
- if (m) {
529
- const arr = state.get(m.currency);
530
- if (arr) arr.push(m.value);
531
- else state.set(m.currency, [m.value]);
532
- }
533
- return state;
534
- },
535
- remove: (state, record) => {
536
- const m = readMoney(record, field, desc);
537
- if (m) {
538
- const arr = state.get(m.currency);
539
- if (arr) {
540
- const idx = arr.indexOf(m.value);
541
- if (idx >= 0) arr.splice(idx, 1);
542
- }
543
- }
544
- return state;
545
- },
546
- finalize: (state) => {
547
- if (desc.mode === "fixed") {
548
- const cur = desc.fixedCurrency;
549
- const arr = state.get(cur);
550
- if (!arr || arr.length === 0) return null;
551
- return formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
552
- }
553
- const out = {};
554
- for (const [cur, arr] of state) {
555
- if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
556
- }
557
- return out;
558
- }
559
- };
560
- }
561
- function wrapMoneyReducers(spec, moneyFields) {
562
- let changed = false;
563
- const out = {};
564
- for (const [key, reducer] of Object.entries(spec)) {
565
- const field = reducer.field;
566
- const desc = field ? moneyFields[field] : void 0;
567
- if (desc && reducer.op === "avg") {
568
- throw new MoneyUnsupportedError(
569
- field,
570
- `avg() is not supported on money field "${field}" in v1 \u2014 use sum() and count() and divide at the boundary.`
571
- );
572
- }
573
- if (desc && (reducer.op === "sum" || reducer.op === "min" || reducer.op === "max")) {
574
- changed = true;
575
- out[key] = reducer.op === "sum" ? moneySumReducer(field, desc, reducer.convertTo, reducer.fx) : moneyMinMaxReducer(reducer.op, field, desc);
576
- } else {
577
- out[key] = reducer;
578
- }
579
- }
580
- return changed ? out : spec;
581
- }
582
-
583
- // src/i18n/policy.ts
584
- function resolvePolicy(onMissing, layer) {
585
- const explicit = onMissing && typeof onMissing === "object" ? onMissing[layer] : void 0;
586
- const scalar = typeof onMissing === "string" ? onMissing : void 0;
587
- const layerDefault = layer === "guard" ? "substitute" : void 0;
588
- return explicit ?? layerDefault ?? scalar ?? "throw";
589
- }
590
-
591
- // src/i18n/script.ts
592
- var LATIN_BASE = /* @__PURE__ */ new Set([
593
- "en",
594
- "fr",
595
- "de",
596
- "es",
597
- "it",
598
- "pt",
599
- "nl",
600
- "sv",
601
- "no",
602
- "da",
603
- "fi",
604
- "is",
605
- "pl",
606
- "cs",
607
- "sk",
608
- "hu",
609
- "ro",
610
- "hr",
611
- "sl",
612
- "et",
613
- "lv",
614
- "lt",
615
- "tr",
616
- "vi",
617
- "id",
618
- "ms",
619
- "tl",
620
- "sw",
621
- "af",
622
- "ca",
623
- "gl",
624
- "eu",
625
- "cy",
626
- "ga"
627
- ]);
628
- var SCRIPT_TABLE = {
629
- th: ["Thai"],
630
- ko: ["Hangul", "Han"],
631
- ja: ["Han", "Hiragana", "Katakana"],
632
- zh: ["Han"],
633
- ar: ["Arabic"],
634
- fa: ["Arabic"],
635
- ur: ["Arabic"],
636
- ru: ["Cyrillic"],
637
- uk: ["Cyrillic"],
638
- bg: ["Cyrillic"],
639
- sr: ["Cyrillic"],
640
- he: ["Hebrew"],
641
- el: ["Greek"],
642
- hi: ["Devanagari"],
643
- ta: ["Tamil"],
644
- km: ["Khmer"],
645
- lo: ["Lao"],
646
- my: ["Myanmar"]
647
- };
648
- var SUBTAG_SCRIPTS = {
649
- Latn: ["Latin"],
650
- Cyrl: ["Cyrillic", "Latin"],
651
- Hans: ["Han", "Latin"],
652
- Hant: ["Han", "Latin"],
653
- Thai: ["Thai", "Latin"],
654
- Arab: ["Arabic", "Latin"]
655
- };
656
- function inferScripts(locale) {
657
- const parts = locale.split("-");
658
- const subtag = parts.find((t) => /^[A-Z][a-z]{3}$/.test(t));
659
- if (subtag && SUBTAG_SCRIPTS[subtag]) return SUBTAG_SCRIPTS[subtag];
660
- const base = (parts[0] ?? "").toLowerCase();
661
- if (LATIN_BASE.has(base)) return ["Latin"];
662
- const primary = SCRIPT_TABLE[base];
663
- if (primary) return [...primary, "Latin"];
664
- return ["Latin"];
665
- }
666
- var BASELINE = String.raw`\p{White_Space}\p{Script=Common}\p{Script=Inherited}\p{Mark}`;
667
-
668
- // src/i18n/core.ts
669
- function toChain(fallback) {
670
- return Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
671
- }
672
- function pickFromChain(value, chain) {
673
- for (const fb of chain) {
674
- if (fb === "any") {
675
- const any = Object.values(value).find((v) => v !== "");
676
- if (any !== void 0) return any;
677
- } else if (value[fb] !== void 0 && value[fb] !== "") {
678
- return value[fb];
679
- }
680
- }
681
- return void 0;
682
- }
683
- function resolveI18nText(value, locale, fallback, field, opts) {
684
- if (locale === "raw") {
685
- return value;
686
- }
687
- if (!locale) {
688
- throw new LocaleNotSpecifiedError(field ?? "<unknown>");
689
- }
690
- if (value[locale] !== void 0 && value[locale] !== "") {
691
- return value[locale];
692
- }
693
- const policy = opts?.policy ?? "throw";
694
- const callerChain = toChain(fallback);
695
- const callerHit = pickFromChain(value, callerChain);
696
- if (callerHit !== void 0) return callerHit;
697
- if (policy === "substitute") {
698
- const subHit = pickFromChain(value, toChain(opts?.substitute));
699
- if (subHit !== void 0) return subHit;
700
- if (opts?.smartSubstitute) {
701
- const smartHit = pickNearestScript(value, locale);
702
- if (smartHit !== void 0) return smartHit;
703
- }
704
- }
705
- if (policy === "throw") {
706
- throw new LocaleNotSpecifiedError(
707
- field ?? "<unknown>",
708
- `No translation available for locale "${locale}"` + (callerChain.length > 0 ? ` or fallback chain [${callerChain.join(", ")}]` : "") + "."
709
- );
710
- }
711
- return null;
712
- }
713
- function pickNearestScript(value, target) {
714
- const targetScript = inferScripts(target)[0] ?? "Latin";
715
- let best;
716
- for (const [loc, v] of Object.entries(value)) {
717
- if (typeof v !== "string" || v === "") continue;
718
- const s = inferScripts(loc)[0] ?? "Latin";
719
- const score = s === targetScript ? 0 : s === "Latin" ? 1 : 2;
720
- if (best === void 0 || score < best.score) best = { score, v };
721
- if (score === 0) break;
722
- }
723
- return best?.v;
724
- }
725
- function applyAtPath(obj, path, locale, fallback, opts) {
726
- const arrayIdx = path.indexOf("[].");
727
- if (arrayIdx !== -1) {
728
- const arrayKey = path.slice(0, arrayIdx);
729
- const restPath = path.slice(arrayIdx + 3);
730
- const arr = obj[arrayKey];
731
- if (!Array.isArray(arr)) return obj;
732
- return {
733
- ...obj,
734
- [arrayKey]: arr.map((item) => {
735
- if (!item || typeof item !== "object" || Array.isArray(item)) return item;
736
- return applyAtPath(item, restPath, locale, fallback, opts);
737
- })
738
- };
739
- }
740
- const dotIdx = path.indexOf(".");
741
- if (dotIdx !== -1) {
742
- const head = path.slice(0, dotIdx);
743
- const rest = path.slice(dotIdx + 1);
744
- const nested = obj[head];
745
- if (!nested || typeof nested !== "object" || Array.isArray(nested)) return obj;
746
- return {
747
- ...obj,
748
- [head]: applyAtPath(nested, rest, locale, fallback, opts)
749
- };
750
- }
751
- const raw = obj[path];
752
- if (raw === void 0 || raw === null) return obj;
753
- if (typeof raw !== "object" || Array.isArray(raw)) return obj;
754
- return {
755
- ...obj,
756
- [path]: resolveI18nText(raw, locale, fallback, path, opts)
757
- };
758
- }
759
- function applyI18nLocale(record, i18nFields, locale, fallback, layer = "read") {
760
- const fieldNames = Object.keys(i18nFields);
761
- if (fieldNames.length === 0) return record;
762
- let result = record;
763
- for (const [field, descriptor] of Object.entries(i18nFields)) {
764
- const { onMissing, substitute, smartSubstitute } = descriptor.options;
765
- const opts = {
766
- policy: resolvePolicy(onMissing, layer),
767
- ...substitute !== void 0 ? { substitute } : {},
768
- ...smartSubstitute ? { smartSubstitute } : {}
769
- };
770
- result = applyAtPath(result, field, locale, fallback, opts);
771
- }
772
- result = stripI18nFilled(result);
773
- return result;
774
- }
775
- function stripI18nFilled(record) {
776
- if (!Object.prototype.hasOwnProperty.call(record, "_i18nFilled")) return record;
777
- const rest = { ...record };
778
- delete rest._i18nFilled;
779
- return rest;
780
- }
781
-
782
- // src/aggregate/groupby.ts
783
- var GROUPBY_WARN_CARDINALITY = 1e4;
784
- var GROUPBY_MAX_CARDINALITY = 1e5;
785
- var warnedCardinalityFields = /* @__PURE__ */ new Set();
786
- function warnCardinalityApproaching(fields, observed) {
787
- const key = JSON.stringify([...fields].sort());
788
- if (warnedCardinalityFields.has(key)) return;
789
- warnedCardinalityFields.add(key);
790
- const label = `[${fields.join(", ")}]`;
791
- console.warn(
792
- `[noy-db] .groupBy(${label}) produced ${observed} distinct groups, ${Math.round(observed / GROUPBY_MAX_CARDINALITY * 100)}% of the ${GROUPBY_MAX_CARDINALITY}-group ceiling. Narrow the query with .where() before grouping, or switch to a lower-cardinality field.`
793
- );
794
- }
795
- function resetGroupByWarnings() {
796
- warnedCardinalityFields.clear();
797
- }
798
- var GroupedQueryBase = class {
799
- constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver, moneyFields) {
800
- this.executeRecords = executeRecords;
801
- this.upstreams = upstreams;
802
- this.dictLabelResolver = dictLabelResolver;
803
- this.moneyFields = moneyFields;
804
- this.fields = typeof fieldOrFields === "string" ? [fieldOrFields] : [...fieldOrFields];
805
- }
806
- executeRecords;
807
- upstreams;
808
- dictLabelResolver;
809
- moneyFields;
810
- /**
811
- * Field set this grouped query buckets on. Stored in declaration
812
- * order — the same order is preserved on every result row by
813
- * `groupAndReduce`. For the single-field constructor, this is
814
- * `[field]`.
815
- */
816
- fields;
817
- /** Apply money-aware reducer rewriting when money fields are declared. */
818
- wrapSpec(spec) {
819
- return this.moneyFields ? wrapMoneyReducers(spec, this.moneyFields) : spec;
820
- }
821
- };
822
- var GroupedQuery = class extends GroupedQueryBase {
823
- /**
824
- * Build a grouped aggregation. Returns a `GroupedAggregation`
825
- * with `.run()`, `.runAsync()`, and `.live()` terminals — same shape
826
- * as the non-grouped `.aggregate()` wrapper, just with an array
827
- * result (one row per bucket) instead of a single reduced object.
828
- */
829
- aggregate(spec) {
830
- return new GroupedAggregation(
831
- this.executeRecords,
832
- this.fields,
833
- this.wrapSpec(spec),
834
- this.upstreams,
835
- this.dictLabelResolver
836
- );
837
- }
838
- };
839
- var GroupedQueryN = class extends GroupedQueryBase {
840
- aggregate(spec) {
841
- return new GroupedAggregation(
842
- this.executeRecords,
843
- this.fields,
844
- this.wrapSpec(spec),
845
- this.upstreams,
846
- this.dictLabelResolver
847
- );
848
- }
849
- };
850
- function groupAndReduce(records, fieldOrFields, spec, moneyFields) {
851
- const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
852
- if (fields.length === 0) {
853
- throw new Error(".groupBy() requires at least one field");
854
- }
855
- if (moneyFields) {
856
- spec = wrapMoneyReducers(spec, moneyFields);
857
- }
858
- const buckets = /* @__PURE__ */ new Map();
859
- const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
860
- for (const record of records) {
861
- const keyValues = {};
862
- for (const f of fields) {
863
- keyValues[f] = readPath(record, f);
864
- }
865
- const dedupKey = canonicalGroupKey(fields, keyValues);
866
- let bucket = buckets.get(dedupKey);
867
- if (bucket === void 0) {
868
- if (buckets.size >= GROUPBY_MAX_CARDINALITY) {
869
- throw new GroupCardinalityError(
870
- fieldLabel,
871
- buckets.size + 1,
872
- GROUPBY_MAX_CARDINALITY
873
- );
874
- }
875
- bucket = { keyValues, records: [] };
876
- buckets.set(dedupKey, bucket);
877
- }
878
- bucket.records.push(record);
879
- }
880
- if (buckets.size >= GROUPBY_WARN_CARDINALITY) {
881
- warnCardinalityApproaching(fields, buckets.size);
882
- }
883
- const reducerKeys = Object.keys(spec);
884
- const out = [];
885
- for (const bucket of buckets.values()) {
886
- const state = {};
887
- for (const rk of reducerKeys) {
888
- state[rk] = spec[rk].init();
889
- }
890
- for (const record of bucket.records) {
891
- for (const rk of reducerKeys) {
892
- state[rk] = spec[rk].step(state[rk], record);
893
- }
894
- }
895
- const row = {};
896
- for (const f of fields) {
897
- row[f] = bucket.keyValues[f];
898
- }
899
- for (const rk of reducerKeys) {
900
- row[rk] = spec[rk].finalize(state[rk]);
901
- }
902
- out.push(row);
903
- }
904
- return out;
905
- }
906
- var GroupedAggregation = class {
907
- constructor(executeRecords, fields, spec, upstreams, dictLabelResolver) {
908
- this.executeRecords = executeRecords;
909
- this.spec = spec;
910
- this.upstreams = upstreams;
911
- this.dictLabelResolver = dictLabelResolver;
912
- this.fields = typeof fields === "string" ? [fields] : [...fields];
913
- }
914
- executeRecords;
915
- spec;
916
- upstreams;
917
- dictLabelResolver;
918
- fields;
919
- /**
920
- * Execute the query, group, reduce, and return an array of rows.
921
- *
922
- * `opts` (#285 query-form MV grouping): when a `locale` + `i18nFields` are
923
- * given, the declared group-key `i18nText` fields are resolved to that locale
924
- * at the `mv` layer BEFORE bucketing — so an i18n group key is a stable string
925
- * instead of a raw `{locale}` map. The MV executor passes the MV's
926
- * `i18nLocale`/`i18nFields`; ordinary `.run()` callers pass nothing and are
927
- * unaffected.
928
- */
929
- run(opts) {
930
- let records = this.executeRecords();
931
- if (opts?.locale !== void 0 && opts.i18nFields !== void 0) {
932
- const groupI18n = {};
933
- for (const f of this.fields) {
934
- const d = opts.i18nFields[f];
935
- if (d !== void 0) groupI18n[f] = d;
936
- }
937
- if (Object.keys(groupI18n).length > 0) {
938
- records = records.map((r) => applyI18nLocale(r, groupI18n, opts.locale, void 0, "mv"));
939
- }
940
- }
941
- return groupAndReduce(records, this.fields, this.spec);
942
- }
943
- /**
944
- * Execute the query, group, reduce, and resolve `<field>Label` for
945
- * each result row when the grouping field is a `dictKey` and a
946
- * `locale` is provided. Returns `R[]` synchronously when
947
- * no locale is specified (identical to `.run()`).
948
- *
949
- * The `<field>Label` field is appended to each row. Rows whose group
950
- * key has no dictionary entry get `<field>Label: undefined`.
951
- *
952
- * Dict-label resolution is single-field only — multi-key groupings
953
- * do not produce a `<field>Label`. The resolver is only attached
954
- * by the builder when `fields.length === 1`.
955
- */
956
- async runAsync(opts) {
957
- const rows = groupAndReduce(this.executeRecords(), this.fields, this.spec);
958
- if (!opts?.locale || !this.dictLabelResolver || this.fields.length !== 1) return rows;
959
- const resolve = this.dictLabelResolver;
960
- const locale = opts.locale;
961
- const fallback = opts.fallback;
962
- const field = this.fields[0];
963
- const labelKey = `${field}Label`;
964
- return Promise.all(
965
- rows.map(async (row) => {
966
- const key = row[field];
967
- if (typeof key !== "string") return row;
968
- const label = await resolve(key, locale, fallback);
969
- return { ...row, [labelKey]: label };
970
- })
971
- );
972
- }
973
- /**
974
- * Build a reactive `LiveAggregation<R[]>` that re-runs the full
975
- * group-and-reduce pipeline whenever any upstream source notifies
976
- * of a change. Same error-isolation and idempotent-stop contract
977
- * as `Aggregation.live()` — the implementation delegates to the
978
- * same `LiveAggregationImpl` class by threading a fresh
979
- * recompute closure through the existing constructor.
980
- *
981
- * uses naive full re-run on every change. Incremental
982
- * per-bucket maintenance (apply `step` on inserted records,
983
- * `remove` on deleted records, route by bucket key) is a future
984
- * optimization — the reducer protocol admits it, but wiring
985
- * delta-aware source subscriptions is a separate PR.
986
- *
987
- * Always call `live.stop()` when finished.
988
- */
989
- live() {
990
- const recompute = () => groupAndReduce(this.executeRecords(), this.fields, this.spec);
991
- return buildLiveAggregation(recompute, this.upstreams);
992
- }
993
- };
994
-
995
- // src/aggregate/active.ts
996
- function withAggregate() {
997
- return {
998
- aggregate(executeRecords, spec, upstreams) {
999
- return new Aggregation(executeRecords, spec, upstreams);
1000
- },
1001
- groupBy(executeRecords, field, upstreams, dictLabelResolver, moneyFields) {
1002
- return new GroupedQuery(executeRecords, field, upstreams, dictLabelResolver, moneyFields);
1003
- },
1004
- groupByN(executeRecords, fields, upstreams, moneyFields) {
1005
- return new GroupedQueryN(executeRecords, fields, upstreams, void 0, moneyFields);
1006
- },
1007
- async scanAggregate(iter, spec) {
1008
- const collected = [];
1009
- for await (const record of iter) collected.push(record);
1010
- return reduceRecords(collected, spec);
1011
- }
1012
- };
1013
- }
1014
-
1015
- // src/aggregate/reducers.ts
1016
- function count(opts) {
1017
- const _seed = opts?.seed;
1018
- void _seed;
1019
- return {
1020
- op: "count",
1021
- init: () => 0,
1022
- step: (state) => state + 1,
1023
- remove: (state) => state - 1,
1024
- finalize: (state) => state,
1025
- merge: (a, b) => a + b
1026
- };
1027
- }
1028
- function sum(field, opts) {
1029
- const _seed = opts?.seed;
1030
- void _seed;
1031
- return {
1032
- op: "sum",
1033
- field,
1034
- // Money-only metadata, read by `wrapMoneyReducers`. No effect on a
1035
- // generic numeric sum.
1036
- ...opts?.convertTo !== void 0 ? { convertTo: opts.convertTo } : {},
1037
- ...opts?.fx !== void 0 ? { fx: opts.fx } : {},
1038
- init: () => 0,
1039
- step: (state, record) => state + readNumber(record, field),
1040
- remove: (state, record) => state - readNumber(record, field),
1041
- finalize: (state) => state,
1042
- merge: (a, b) => a + b
1043
- };
1044
- }
1045
- function avg(field, opts) {
1046
- const _seed = opts?.seed;
1047
- void _seed;
1048
- return {
1049
- op: "avg",
1050
- field,
1051
- init: () => ({ sum: 0, count: 0 }),
1052
- step: (state, record) => ({
1053
- sum: state.sum + readNumber(record, field),
1054
- count: state.count + 1
1055
- }),
1056
- remove: (state, record) => ({
1057
- sum: state.sum - readNumber(record, field),
1058
- count: state.count - 1
1059
- }),
1060
- finalize: (state) => state.count === 0 ? null : state.sum / state.count,
1061
- merge: (a, b) => ({ sum: a.sum + b.sum, count: a.count + b.count })
1062
- };
1063
- }
1064
- function pushValue(state, value) {
1065
- return { values: [...state.values, value] };
1066
- }
1067
- function removeValue(state, value) {
1068
- const idx = state.values.indexOf(value);
1069
- if (idx < 0) return state;
1070
- const next = state.values.slice();
1071
- next.splice(idx, 1);
1072
- return { values: next };
1073
- }
1074
- function min(field, opts) {
1075
- const _seed = opts?.seed;
1076
- void _seed;
1077
- return {
1078
- op: "min",
1079
- field,
1080
- init: () => ({ values: [] }),
1081
- step: (state, record) => pushValue(state, readNumber(record, field)),
1082
- remove: (state, record) => removeValue(state, readNumber(record, field)),
1083
- finalize: (state) => {
1084
- if (state.values.length === 0) return null;
1085
- let out = state.values[0];
1086
- for (let i = 1; i < state.values.length; i++) {
1087
- const v = state.values[i];
1088
- if (v < out) out = v;
1089
- }
1090
- return out;
1091
- },
1092
- merge: (a, b) => ({ values: [...a.values, ...b.values] })
1093
- };
1094
- }
1095
- function max(field, opts) {
1096
- const _seed = opts?.seed;
1097
- void _seed;
1098
- return {
1099
- op: "max",
1100
- field,
1101
- init: () => ({ values: [] }),
1102
- step: (state, record) => pushValue(state, readNumber(record, field)),
1103
- remove: (state, record) => removeValue(state, readNumber(record, field)),
1104
- finalize: (state) => {
1105
- if (state.values.length === 0) return null;
1106
- let out = state.values[0];
1107
- for (let i = 1; i < state.values.length; i++) {
1108
- const v = state.values[i];
1109
- if (v > out) out = v;
1110
- }
1111
- return out;
1112
- },
1113
- merge: (a, b) => ({ values: [...a.values, ...b.values] })
1114
- };
1115
- }
1116
- function readNumber(record, field) {
1117
- const value = readPath(record, field);
1118
- if (typeof value === "object" && value !== null && "amount" in value && "currency" in value) {
1119
- throw new Error(
1120
- `aggregate: field "${field}" holds a money value but was not money-aware \u2014 declare it in the collection's moneyFields so sum/min/max stay exact`
1121
- );
1122
- }
1123
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
1124
- }
1125
- // Annotate the CommonJS export names for ESM import in node:
1126
- 0 && (module.exports = {
1127
- Aggregation,
1128
- GROUPBY_MAX_CARDINALITY,
1129
- GROUPBY_WARN_CARDINALITY,
1130
- GroupedAggregation,
1131
- GroupedQuery,
1132
- GroupedQueryN,
1133
- avg,
1134
- buildLiveAggregation,
1135
- count,
1136
- groupAndReduce,
1137
- max,
1138
- min,
1139
- reduceRecords,
1140
- resetGroupByWarnings,
1141
- sum,
1142
- withAggregate
1143
- });
1144
- //# sourceMappingURL=index.cjs.map