@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.
- package/dist/adapter/index.d.ts +9 -0
- package/dist/adapter/index.js +14 -0
- package/dist/attestation/index.d.ts +1 -1
- package/dist/attestation/index.js +3 -3
- package/dist/blobs/index.d.ts +3 -3
- package/dist/bundle/index.d.ts +3 -3
- package/dist/bundle/index.js +1 -1
- package/dist/{chunk-Y6YUWZTS.js → chunk-L5HHBOMS.js} +2 -2
- package/dist/{chunk-BVKLJBJJ.js → chunk-W5NVNJDX.js} +2 -1
- package/dist/{chunk-BVKLJBJJ.js.map → chunk-W5NVNJDX.js.map} +1 -1
- package/dist/consent/index.d.ts +2 -2
- package/dist/{decrypt-partition-Mhjh6nnG.d.ts → decrypt-partition-CXS5KopB.d.ts} +1 -1
- package/dist/derivations/index.d.ts +3 -3
- package/dist/{dev-unlock-DJ3IhgY9.d.ts → dev-unlock-CnHTTVea.d.ts} +1 -1
- package/dist/guards/index.d.ts +3 -3
- package/dist/{hash-DLwkYf1d.d.ts → hash-oc5paYl0.d.ts} +1 -1
- package/dist/history/index.d.ts +3 -3
- package/dist/i18n/index.d.ts +2 -2
- package/dist/{index-CMRIx_zx.d.ts → index-BmhGztUi.d.ts} +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +2 -2
- package/dist/kernel/index.d.ts +2 -2
- package/dist/materialized-views/index.d.ts +3 -3
- package/dist/{mime-magic-CacDBwYp.d.ts → mime-magic-JeLKHRng.d.ts} +1 -1
- package/dist/{noydb-5MIBD77B.js → noydb-NAU2DTFP.js} +2 -2
- package/dist/noydb-NAU2DTFP.js.map +1 -0
- package/dist/overlay-views/index.d.ts +3 -3
- package/dist/periods/index.d.ts +2 -2
- package/dist/session/index.d.ts +3 -3
- package/dist/shadow/index.d.ts +2 -2
- package/dist/snapshots/index.d.ts +2 -2
- package/dist/store/index.d.ts +2 -2
- package/dist/sync/index.d.ts +1 -1
- package/dist/team/index.d.ts +2 -2
- package/dist/{transition-guard-J04-jime.d.ts → transition-guard-Dy3NioQr.d.ts} +1 -1
- package/dist/tx/index.d.ts +2 -2
- package/dist/{with-materialized-view-DNfzC5lH.d.ts → with-materialized-view-DIVeez0W.d.ts} +1 -1
- package/dist/{with-overlayed-view-qk3lbhOd.d.ts → with-overlayed-view-DVZrc5Hb.d.ts} +1 -1
- package/dist/{with-rollup-Dd3_ZG4b.d.ts → with-rollup-CbU-O4wP.d.ts} +1 -1
- package/package.json +62 -221
- package/dist/aggregate/index.cjs +0 -1144
- package/dist/aggregate/index.cjs.map +0 -1
- package/dist/aggregate/index.d.cts +0 -39
- package/dist/attestation/index.cjs +0 -305
- package/dist/attestation/index.cjs.map +0 -1
- package/dist/attestation/index.d.cts +0 -54
- package/dist/blobs/index.cjs +0 -1967
- package/dist/blobs/index.cjs.map +0 -1
- package/dist/blobs/index.d.cts +0 -48
- package/dist/bundle/index.cjs +0 -41045
- package/dist/bundle/index.cjs.map +0 -1
- package/dist/bundle/index.d.cts +0 -187
- package/dist/consent/index.cjs +0 -204
- package/dist/consent/index.cjs.map +0 -1
- package/dist/consent/index.d.cts +0 -27
- package/dist/crdt/index.cjs +0 -152
- package/dist/crdt/index.cjs.map +0 -1
- package/dist/crdt/index.d.cts +0 -30
- package/dist/decrypt-partition-C0iDpbSd.d.cts +0 -558
- package/dist/derivations/index.cjs +0 -469
- package/dist/derivations/index.cjs.map +0 -1
- package/dist/derivations/index.d.cts +0 -74
- package/dist/dev-unlock-0Z6yxfS7.d.cts +0 -263
- package/dist/discriminant-BN9REW3o.d.cts +0 -60
- package/dist/errors-BAWO5Z5a.d.cts +0 -1500
- package/dist/forget/index.cjs +0 -43
- package/dist/forget/index.cjs.map +0 -1
- package/dist/forget/index.d.cts +0 -1
- package/dist/guards/index.cjs +0 -455
- package/dist/guards/index.cjs.map +0 -1
- package/dist/guards/index.d.cts +0 -39
- package/dist/hash-Db8ncXGr.d.cts +0 -63
- package/dist/history/index.cjs +0 -1245
- package/dist/history/index.cjs.map +0 -1
- package/dist/history/index.d.cts +0 -65
- package/dist/i18n/index.cjs +0 -1280
- package/dist/i18n/index.cjs.map +0 -1
- package/dist/i18n/index.d.cts +0 -41
- package/dist/index-BMmajblo.d.cts +0 -362
- package/dist/index-C1SC1EPe.d.cts +0 -1325
- package/dist/index-xJEPkvMb.d.cts +0 -93
- package/dist/index.cjs +0 -48100
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -1050
- package/dist/indexing/index.cjs +0 -803
- package/dist/indexing/index.cjs.map +0 -1
- package/dist/indexing/index.d.cts +0 -36
- package/dist/kernel/index.cjs +0 -751
- package/dist/kernel/index.cjs.map +0 -1
- package/dist/kernel/index.d.cts +0 -11
- package/dist/lazy-builder-eYZzLEL1.d.cts +0 -304
- package/dist/materialized-views/index.cjs +0 -1518
- package/dist/materialized-views/index.cjs.map +0 -1
- package/dist/materialized-views/index.d.cts +0 -187
- package/dist/mime-magic-sdMzsfVK.d.cts +0 -103
- package/dist/overlay-views/index.cjs +0 -399
- package/dist/overlay-views/index.cjs.map +0 -1
- package/dist/overlay-views/index.d.cts +0 -102
- package/dist/periods/index.cjs +0 -1041
- package/dist/periods/index.cjs.map +0 -1
- package/dist/periods/index.d.cts +0 -24
- package/dist/predicate-BmhBSPCH.d.cts +0 -267
- package/dist/query/index.cjs +0 -3480
- package/dist/query/index.cjs.map +0 -1
- package/dist/query/index.d.cts +0 -4
- package/dist/sealed-record/index.cjs +0 -139
- package/dist/sealed-record/index.cjs.map +0 -1
- package/dist/sealed-record/index.d.cts +0 -123
- package/dist/session/index.cjs +0 -495
- package/dist/session/index.cjs.map +0 -1
- package/dist/session/index.d.cts +0 -48
- package/dist/shadow/index.cjs +0 -133
- package/dist/shadow/index.cjs.map +0 -1
- package/dist/shadow/index.d.cts +0 -19
- package/dist/snapshots/index.cjs +0 -937
- package/dist/snapshots/index.cjs.map +0 -1
- package/dist/snapshots/index.d.cts +0 -30
- package/dist/store/index.cjs +0 -1091
- package/dist/store/index.cjs.map +0 -1
- package/dist/store/index.d.cts +0 -501
- package/dist/strategy-BSxFXGzb.d.cts +0 -110
- package/dist/strategy-mJExw4LQ.d.cts +0 -1069
- package/dist/sync/index.cjs +0 -1062
- package/dist/sync/index.cjs.map +0 -1
- package/dist/sync/index.d.cts +0 -45
- package/dist/team/index.cjs +0 -2805
- package/dist/team/index.cjs.map +0 -1
- package/dist/team/index.d.cts +0 -120
- package/dist/transition-guard-tWZIsaXe.d.cts +0 -165
- package/dist/tx/index.cjs +0 -614
- package/dist/tx/index.cjs.map +0 -1
- package/dist/tx/index.d.cts +0 -39
- package/dist/types-CdVfiQgt.d.cts +0 -15275
- package/dist/ulid-DRH25k3y.d.cts +0 -66
- package/dist/util/index.cjs +0 -237
- package/dist/util/index.cjs.map +0 -1
- package/dist/util/index.d.cts +0 -79
- package/dist/with-materialized-view-DfgPcvi9.d.cts +0 -27
- package/dist/with-overlayed-view-Cvfkx1lg.d.cts +0 -13
- package/dist/with-rollup-nIxo7h9z.d.cts +0 -47
- /package/dist/{noydb-5MIBD77B.js.map → adapter/index.js.map} +0 -0
- /package/dist/{chunk-Y6YUWZTS.js.map → chunk-L5HHBOMS.js.map} +0 -0
- /package/dist/{types-DHn9lEP0.d.ts → index-DHn9lEP0.d.ts} +0 -0
|
@@ -1,1069 +0,0 @@
|
|
|
1
|
-
import { N as NoydbError } from './errors-BAWO5Z5a.cjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Aggregation reducers for the query DSL.
|
|
5
|
-
*
|
|
6
|
-
* the reducer protocol plus five built-in factories
|
|
7
|
-
* (`count`, `sum`, `avg`, `min`, `max`) consumed by `Query.aggregate()`
|
|
8
|
-
* and, in the future, `Scan.aggregate()`. Every factory accepts
|
|
9
|
-
* an optional `{ seed }` parameter that is plumbed through the
|
|
10
|
-
* protocol but unused by the executor — that's the load-bearing
|
|
11
|
-
* half of constraint #2. When partition-aware aggregation
|
|
12
|
-
* lands, the seed carries the previous partition's running total into
|
|
13
|
-
* the next partition without requiring a protocol change.
|
|
14
|
-
*
|
|
15
|
-
* Reducers are intentionally generic over their internal state type
|
|
16
|
-
* `S` so compound reducers (avg keeps `{sum, count}`, min/max keep a
|
|
17
|
-
* value bag) can model internal bookkeeping without leaking the
|
|
18
|
-
* implementation through the accumulator's public shape. `finalize`
|
|
19
|
-
* collapses `S` back into the user-visible `R`.
|
|
20
|
-
*
|
|
21
|
-
* Reducers are pure data — `init` / `step` / `finalize` / optional
|
|
22
|
-
* `remove` are stateless functions that receive and return `S`. This
|
|
23
|
-
* is the shape that admits O(1) incremental maintenance in a future
|
|
24
|
-
* optimization (delta-aware `LiveAggregation` applies `step` or
|
|
25
|
-
* `remove` per delta), without blocking the simpler "full re-run on
|
|
26
|
-
* source change" that ships.
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* A single reducer: factory-produced, ready to plug into an
|
|
30
|
-
* `.aggregate()` spec.
|
|
31
|
-
*
|
|
32
|
-
* Type parameters:
|
|
33
|
-
* - `R` — user-visible result type (what the aggregation returns
|
|
34
|
-
* for this slot, e.g. `number` for `sum()`)
|
|
35
|
-
* - `S` — internal state type, defaults to `R` for simple reducers
|
|
36
|
-
* that don't need compound bookkeeping
|
|
37
|
-
*
|
|
38
|
-
* A reducer is stateless: every method is pure over `S`. `init()` is
|
|
39
|
-
* called once per aggregation run to build the initial state; `step()`
|
|
40
|
-
* folds a record into the state; `remove()` (optional) un-folds a
|
|
41
|
-
* record, enabling incremental live maintenance; `finalize()` reads
|
|
42
|
-
* the final answer out of the state at the end of the run.
|
|
43
|
-
*/
|
|
44
|
-
interface Reducer<R, S = R> {
|
|
45
|
-
/** Build the initial state for a fresh aggregation run. */
|
|
46
|
-
init(): S;
|
|
47
|
-
/** Fold a record into the state. Returns the new state. */
|
|
48
|
-
step(state: S, record: unknown): S;
|
|
49
|
-
/**
|
|
50
|
-
* Un-fold a record from the state. Returns the new state.
|
|
51
|
-
*
|
|
52
|
-
* Optional — reducers without `remove` cannot be maintained
|
|
53
|
-
* incrementally and must be re-run from scratch when the underlying
|
|
54
|
-
* record set changes. `sum`, `count`, `avg` implement `remove` in
|
|
55
|
-
* O(1); `min` and `max` implement it in O(N) worst case (when the
|
|
56
|
-
* extremum itself is removed and the next extremum must be
|
|
57
|
-
* recomputed from the remaining contributing values).
|
|
58
|
-
*/
|
|
59
|
-
remove?(state: S, record: unknown): S;
|
|
60
|
-
/** Collapse the internal state into the user-visible result. */
|
|
61
|
-
finalize(state: S): R;
|
|
62
|
-
/**
|
|
63
|
-
* Combine two independent partial states into one (then `finalize` once).
|
|
64
|
-
* Optional. MUST be associative + commutative with `init()` as identity.
|
|
65
|
-
* Never merge finalized results — only states. Enables parallel /
|
|
66
|
-
* hierarchical aggregation (e.g. cross-shard or advisor→firm rollup).
|
|
67
|
-
*/
|
|
68
|
-
merge?(a: S, b: S): S;
|
|
69
|
-
/**
|
|
70
|
-
* Identifying operation tag stamped by each built-in factory.
|
|
71
|
-
* Used by `summariseAggregateOp` in the introspection walker to
|
|
72
|
-
* render human-readable aggregate descriptors in `dumpSchema()`.
|
|
73
|
-
* Optional so third-party custom reducers are unaffected.
|
|
74
|
-
*/
|
|
75
|
-
readonly op?: 'count' | 'sum' | 'avg' | 'min' | 'max';
|
|
76
|
-
/**
|
|
77
|
-
* Field name for field-based reducers (`sum`, `avg`, `min`, `max`).
|
|
78
|
-
* Absent on `count` which aggregates over record count, not a field.
|
|
79
|
-
*/
|
|
80
|
-
readonly field?: string;
|
|
81
|
-
/**
|
|
82
|
-
* Money-only: target currency for `sum` over a multi-currency money
|
|
83
|
-
* field. Consumed by `wrapMoneyReducers` to convert per-currency
|
|
84
|
-
* subtotals to one figure. Ignored for non-money fields.
|
|
85
|
-
*/
|
|
86
|
-
readonly convertTo?: string;
|
|
87
|
-
/**
|
|
88
|
-
* Money-only: FX rate map (`'USD->EUR' → rate`) used with `convertTo`.
|
|
89
|
-
*/
|
|
90
|
-
readonly fx?: Record<string, number | string>;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Common options accepted by every reducer factory.
|
|
94
|
-
*
|
|
95
|
-
* `seed` — optional initial value for the internal state. **Unused by
|
|
96
|
-
* the executor**, plumbed through the protocol for constraint
|
|
97
|
-
* #2 (partition-aware aggregation seam). In, partitioned
|
|
98
|
-
* aggregations will pass the previous partition's carry as `seed` so
|
|
99
|
-
* a long time series can be rolled forward one partition at a time
|
|
100
|
-
* without re-aggregating closed partitions.
|
|
101
|
-
*
|
|
102
|
-
* always uses `init()` with the factory's zero value, regardless
|
|
103
|
-
* of whether `seed` was passed. Do not remove the parameter — that's
|
|
104
|
-
* the whole point of having it exist now.
|
|
105
|
-
*/
|
|
106
|
-
interface ReducerOptions<TSeed = unknown> {
|
|
107
|
-
/** constraint #2 — seed is plumbed through but unused in. */
|
|
108
|
-
readonly seed?: TSeed;
|
|
109
|
-
/**
|
|
110
|
-
* Money-only (honored by `sum` over a multi-currency money field):
|
|
111
|
-
* convert per-currency subtotals to this currency for a single figure.
|
|
112
|
-
*/
|
|
113
|
-
readonly convertTo?: string;
|
|
114
|
-
/** Money-only: FX rate map (`'USD->EUR' → rate`) used with `convertTo`. */
|
|
115
|
-
readonly fx?: Record<string, number | string>;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Count the number of records that match the query. Ignores field
|
|
119
|
-
* values entirely — the count is over the number of records, not over
|
|
120
|
-
* the number of non-null field values in any column.
|
|
121
|
-
*/
|
|
122
|
-
declare function count(opts?: ReducerOptions<number>): Reducer<number>;
|
|
123
|
-
/**
|
|
124
|
-
* Sum a numeric field across all matching records. Non-number values
|
|
125
|
-
* at the field path are coerced to 0 — consumers who want a different
|
|
126
|
-
* behavior (throw, skip, treat as NaN) should filter upstream via
|
|
127
|
-
* `.where()` or write a custom reducer.
|
|
128
|
-
*/
|
|
129
|
-
declare function sum(field: string, opts?: ReducerOptions<number>): Reducer<number>;
|
|
130
|
-
/**
|
|
131
|
-
* Arithmetic mean of a numeric field across all matching records.
|
|
132
|
-
*
|
|
133
|
-
* Returns `null` for an empty result set (zero records is not a
|
|
134
|
-
* well-defined denominator — returning NaN would poison downstream
|
|
135
|
-
* arithmetic, and throwing would force every consumer to wrap in
|
|
136
|
-
* try/catch just to handle "no matches"). Consumers who want an
|
|
137
|
-
* explicit zero should coalesce with `?? 0`.
|
|
138
|
-
*
|
|
139
|
-
* Internal state is `{sum, count}` so the running average can be
|
|
140
|
-
* maintained incrementally — on each delta, both fields update in
|
|
141
|
-
* O(1) and `finalize` divides. Directly storing `avg` as state would
|
|
142
|
-
* not admit incremental removal without also tracking count.
|
|
143
|
-
*/
|
|
144
|
-
declare function avg(field: string, opts?: ReducerOptions<{
|
|
145
|
-
sum: number;
|
|
146
|
-
count: number;
|
|
147
|
-
}>): Reducer<number | null, {
|
|
148
|
-
sum: number;
|
|
149
|
-
count: number;
|
|
150
|
-
}>;
|
|
151
|
-
interface MinMaxState {
|
|
152
|
-
/**
|
|
153
|
-
* Multiset of contributing field values. Stored as a plain array
|
|
154
|
-
* because we need to support `remove` and a plain array gives us
|
|
155
|
-
* O(1) push + O(N) worst-case removal — which matches the
|
|
156
|
-
* documented min/max removal complexity. A sorted structure would
|
|
157
|
-
* let us drop the O(N) rescan but adds complexity that doesn't
|
|
158
|
-
* need; consumers hitting the O(N) ceiling should file an issue.
|
|
159
|
-
*/
|
|
160
|
-
readonly values: number[];
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Smallest numeric value of a field across all matching records.
|
|
164
|
-
* Returns `null` for an empty result set. See `avg()` for the
|
|
165
|
-
* reasoning on `null` vs NaN vs throwing.
|
|
166
|
-
*
|
|
167
|
-
* Incremental complexity: O(1) for `step`, O(N) worst case for
|
|
168
|
-
* `remove` when the current minimum is removed (the state holds the
|
|
169
|
-
* full multiset of contributing values and `finalize` scans for the
|
|
170
|
-
* new minimum). Consumers with very large result sets and frequent
|
|
171
|
-
* removals of the current extremum should either accept the cost or
|
|
172
|
-
* wait for a future optimization.
|
|
173
|
-
*/
|
|
174
|
-
declare function min(field: string, opts?: ReducerOptions<number>): Reducer<number | null, MinMaxState>;
|
|
175
|
-
/**
|
|
176
|
-
* Largest numeric value of a field across all matching records.
|
|
177
|
-
* Mirror of `min()` — see that doc for semantics, null-on-empty
|
|
178
|
-
* behavior, and the O(N) removal caveat.
|
|
179
|
-
*/
|
|
180
|
-
declare function max(field: string, opts?: ReducerOptions<number>): Reducer<number | null, MinMaxState>;
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Aggregate execution — the runtime behind `Query.aggregate()`.
|
|
184
|
-
*
|
|
185
|
-
* takes an `AggregateSpec` (a record of named reducers
|
|
186
|
-
* built from `reducers.ts`) and runs every reducer over the records
|
|
187
|
-
* produced by the underlying query. Two terminal surfaces:
|
|
188
|
-
*
|
|
189
|
-
* - `.run(): R` — synchronous one-shot reduction. Matches the
|
|
190
|
-
* existing `Query.toArray()` / `.first()` / `.count()` style.
|
|
191
|
-
* - `.live(): LiveAggregation<R>` — reactive primitive that
|
|
192
|
-
* re-runs the reduction whenever the query's source notifies of
|
|
193
|
-
* a change. uses naive full re-run; incremental delta
|
|
194
|
-
* maintenance is admitted by the reducer protocol (`remove()`)
|
|
195
|
-
* but not wired to the executor yet — a follow-up optimization
|
|
196
|
-
* can switch from full re-run to delta-based without breaking
|
|
197
|
-
* the public API. Consumers get correct, reactive values today.
|
|
198
|
-
*
|
|
199
|
-
* The `Aggregation<R>` wrapper is deliberately tiny — it exists so
|
|
200
|
-
* `.aggregate(spec)` can be chained with either `.run()` or `.live()`
|
|
201
|
-
* without the builder needing two separate terminal methods. It
|
|
202
|
-
* holds the closure over the query execution (produces the current
|
|
203
|
-
* matching record set) and the spec, and stitches them together in
|
|
204
|
-
* either mode.
|
|
205
|
-
*
|
|
206
|
-
* This file depends ONLY on `reducers.ts` — it has no knowledge of
|
|
207
|
-
* the `Query` class. Tests can therefore exercise the reduction
|
|
208
|
-
* surface with plain record arrays, without spinning up a Collection.
|
|
209
|
-
*/
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* A named set of reducers, keyed by output field name. Each key
|
|
213
|
-
* becomes a field on the aggregated result.
|
|
214
|
-
*
|
|
215
|
-
* ```ts
|
|
216
|
-
* const spec = {
|
|
217
|
-
* total: sum('amount'),
|
|
218
|
-
* n: count(),
|
|
219
|
-
* avgAmount: avg('amount'),
|
|
220
|
-
* }
|
|
221
|
-
* ```
|
|
222
|
-
*/
|
|
223
|
-
type AggregateSpec = Readonly<Record<string, Reducer<unknown, unknown>>>;
|
|
224
|
-
/**
|
|
225
|
-
* Map an `AggregateSpec` to its reduced result shape — each key
|
|
226
|
-
* carries the finalized result type from its reducer. A spec built
|
|
227
|
-
* from `{ total: sum('amount'), n: count() }` yields a result of
|
|
228
|
-
* `{ total: number, n: number }`.
|
|
229
|
-
*
|
|
230
|
-
* This uses a mapped type with a conditional to extract `R` from
|
|
231
|
-
* each `Reducer<R, _>`. The `infer` captures the user-visible result
|
|
232
|
-
* type, discarding the internal state type `S`.
|
|
233
|
-
*/
|
|
234
|
-
type AggregateResult<Spec extends AggregateSpec> = {
|
|
235
|
-
[K in keyof Spec]: Spec[K] extends Reducer<infer R, unknown> ? R : never;
|
|
236
|
-
};
|
|
237
|
-
/**
|
|
238
|
-
* Pure reduction over a record array. Runs every reducer's
|
|
239
|
-
* `init → step* → finalize` pipeline exactly once over the records.
|
|
240
|
-
*
|
|
241
|
-
* Called by `Aggregation.run()` and by the live-mode refresh path.
|
|
242
|
-
* Exported for tests and for future `scan().aggregate()` reuse
|
|
243
|
-
* — the streaming path will call the same reducer protocol with a
|
|
244
|
-
* per-page loop instead of a single array.
|
|
245
|
-
*/
|
|
246
|
-
declare function reduceRecords<Spec extends AggregateSpec>(records: readonly unknown[], spec: Spec): AggregateResult<Spec>;
|
|
247
|
-
/**
|
|
248
|
-
* A minimal reactive primitive for aggregation results.
|
|
249
|
-
*
|
|
250
|
-
* Same spirit as the `LiveQuery` in : frame-agnostic, a plain
|
|
251
|
-
* object with `value` / `error` fields and a `subscribe(cb)`
|
|
252
|
-
* notification channel that Vue / React / Solid adapters wrap in
|
|
253
|
-
* their own primitive. Intentionally NOT a Promise — aggregations
|
|
254
|
-
* have a well-defined "current value" at every instant, and the
|
|
255
|
-
* reactive consumer wants to read that value synchronously.
|
|
256
|
-
*
|
|
257
|
-
* Error semantics mirror `LiveQuery`: if a re-run throws, the
|
|
258
|
-
* previous successful `value` is preserved and the error is stored
|
|
259
|
-
* in `error` so consumers can render an error state without losing
|
|
260
|
-
* the last-known-good result. The throw does NOT propagate out of
|
|
261
|
-
* the source's change handler (which would tear down the upstream
|
|
262
|
-
* emitter).
|
|
263
|
-
*
|
|
264
|
-
* `stop()` tears down the upstream subscription. It is idempotent —
|
|
265
|
-
* calling it multiple times is safe — and subscribe calls after
|
|
266
|
-
* stop are no-ops (they immediately return a no-op unsubscribe).
|
|
267
|
-
* Always call `stop()` when done; Vue's `onUnmounted` is the
|
|
268
|
-
* canonical place. Raw consumers must do it themselves.
|
|
269
|
-
*/
|
|
270
|
-
interface LiveAggregation<R> {
|
|
271
|
-
/** Current reduced value. Undefined only if the first compute threw. */
|
|
272
|
-
readonly value: R | undefined;
|
|
273
|
-
/** Last execution error, if any. Cleared on the next successful run. */
|
|
274
|
-
readonly error: unknown;
|
|
275
|
-
/** Notify on every recomputation (success or error). Returns unsubscribe. */
|
|
276
|
-
subscribe(cb: () => void): () => void;
|
|
277
|
-
/** Tear down the upstream subscription. Idempotent. */
|
|
278
|
-
stop(): void;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Upstream change-notification hook for live aggregation.
|
|
282
|
-
*
|
|
283
|
-
* Matches the shape that `QuerySource.subscribe` already uses — a
|
|
284
|
-
* single method that accepts a callback and returns an unsubscribe
|
|
285
|
-
* function. The `Aggregation` wrapper collects upstreams from the
|
|
286
|
-
* query's source and wires them into a single re-run trigger.
|
|
287
|
-
*/
|
|
288
|
-
interface AggregationUpstream {
|
|
289
|
-
subscribe(cb: () => void): () => void;
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Chainable wrapper returned by `Query.aggregate(spec)`. Holds the
|
|
293
|
-
* execute-records closure and the spec; terminal methods (`run`,
|
|
294
|
-
* `live`) stitch them together in either mode.
|
|
295
|
-
*
|
|
296
|
-
* Why a wrapper instead of two terminal methods on `Query` directly?
|
|
297
|
-
*
|
|
298
|
-
* The `.aggregate(spec)` call is where the spec is bound — both
|
|
299
|
-
* `.run()` and `.live()` need the same spec, and the consumer's
|
|
300
|
-
* fluent style is `query.where(...).aggregate(spec).run()` or
|
|
301
|
-
* `.aggregate(spec).live()`. Wrapping lets the spec be named once
|
|
302
|
-
* and reused for either terminal, and keeps the `Query` class
|
|
303
|
-
* from growing a pair of near-duplicate method overloads
|
|
304
|
-
* (`aggregateRun` / `aggregateLive`) that would be harder to
|
|
305
|
-
* discover.
|
|
306
|
-
*/
|
|
307
|
-
declare class Aggregation<R> {
|
|
308
|
-
private readonly executeRecords;
|
|
309
|
-
private readonly spec;
|
|
310
|
-
private readonly upstreams;
|
|
311
|
-
constructor(executeRecords: () => readonly unknown[], spec: AggregateSpec, upstreams: readonly AggregationUpstream[]);
|
|
312
|
-
/**
|
|
313
|
-
* Execute the query and reduce the results synchronously.
|
|
314
|
-
* Returns the reduced shape matching the spec — e.g. a spec of
|
|
315
|
-
* `{ total: sum('amount'), n: count() }` returns
|
|
316
|
-
* `{ total: number, n: number }`.
|
|
317
|
-
*/
|
|
318
|
-
run(): R;
|
|
319
|
-
/**
|
|
320
|
-
* Build a reactive `LiveAggregation<R>` that re-runs the reduction
|
|
321
|
-
* whenever any upstream source notifies of a change. The initial
|
|
322
|
-
* value is computed eagerly in the constructor, so consumers can
|
|
323
|
-
* read `live.value` immediately after calling `.live()`.
|
|
324
|
-
*
|
|
325
|
-
* Always call `live.stop()` when finished — it tears down the
|
|
326
|
-
* upstream subscriptions. Vue's `onUnmounted` is the canonical
|
|
327
|
-
* place.
|
|
328
|
-
*
|
|
329
|
-
* **Implementation note:** every upstream change triggers a full
|
|
330
|
-
* re-reduction. Incremental maintenance (O(1) per delta for
|
|
331
|
-
* sum/count/avg via the reducer protocol's `remove()` method) is a
|
|
332
|
-
* planned follow-up optimization — the protocol already supports
|
|
333
|
-
* it, but the executor doesn't drive it yet. Consumers get
|
|
334
|
-
* correct, reactive values today; future PRs can switch to
|
|
335
|
-
* delta-based maintenance without changing this API.
|
|
336
|
-
*/
|
|
337
|
-
live(): LiveAggregation<R>;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Build a `LiveAggregation<V>` from a recompute closure and a list
|
|
341
|
-
* of upstreams. Exposed so sibling files in the query DSL
|
|
342
|
-
* (currently `groupby.ts`) can reuse the reactive primitive
|
|
343
|
-
* without reaching into `LiveAggregationImpl` directly. This keeps
|
|
344
|
-
* the implementation class private while still allowing planned
|
|
345
|
-
* composition with `.groupBy().aggregate().live()`.
|
|
346
|
-
*/
|
|
347
|
-
declare function buildLiveAggregation<V>(recompute: () => V, upstreams: readonly AggregationUpstream[]): LiveAggregation<V>;
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Pure fixed-point decimal core for the money descriptor.
|
|
351
|
-
*
|
|
352
|
-
* All conversion goes decimal-string ⇄ scaled `BigInt`. There is no
|
|
353
|
-
* floating-point arithmetic anywhere: a value like `123.45` becomes
|
|
354
|
-
* `12345n` purely by string manipulation, never by `value * 100` (which
|
|
355
|
-
* would reintroduce the very drift money() exists to eliminate). BigInt
|
|
356
|
-
* has no magnitude ceiling, so values past `Number.MAX_SAFE_INTEGER`
|
|
357
|
-
* stay exact end-to-end.
|
|
358
|
-
*
|
|
359
|
-
* This module knows nothing about currencies, descriptors, or storage —
|
|
360
|
-
* it is the isolated, exhaustively-tested arithmetic kernel.
|
|
361
|
-
*/
|
|
362
|
-
type RoundingMode = 'half-up' | 'half-even' | 'half-down' | 'up' | 'down' | 'ceil' | 'floor';
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* The `money()` field descriptor — a branded schema-layer descriptor, a
|
|
366
|
-
* sibling of `i18nText()` / `dictKey()`. It owns currency, scale, and
|
|
367
|
-
* rounding policy for a field; the pure arithmetic lives in
|
|
368
|
-
* {@link ./fixed-point} and default scale resolution in
|
|
369
|
-
* {@link ./iso4217}.
|
|
370
|
-
*
|
|
371
|
-
* Two modes:
|
|
372
|
-
* - **fixed** `money({ currency: 'EUR' })` — one currency for the
|
|
373
|
-
* field; the value stores a bare scaled-integer string.
|
|
374
|
-
* - **multi** `money({ currencies: 'any' | [...] })` — currency travels
|
|
375
|
-
* per record; the value stores `{ amount, currency }`.
|
|
376
|
-
*
|
|
377
|
-
* `currency` and `currencies` are mutually exclusive.
|
|
378
|
-
*/
|
|
379
|
-
|
|
380
|
-
interface MoneyOptionsFixed {
|
|
381
|
-
currency: string;
|
|
382
|
-
/** Override the ISO-4217 default scale (required for unlisted codes). */
|
|
383
|
-
scale?: number;
|
|
384
|
-
rounding?: RoundingMode;
|
|
385
|
-
}
|
|
386
|
-
interface MoneyOptionsMulti {
|
|
387
|
-
currencies: 'any' | readonly string[];
|
|
388
|
-
/** Per-currency scale overrides (required for unlisted codes). */
|
|
389
|
-
scaleOverrides?: Record<string, number>;
|
|
390
|
-
rounding?: RoundingMode;
|
|
391
|
-
}
|
|
392
|
-
type MoneyOptions = MoneyOptionsFixed | MoneyOptionsMulti;
|
|
393
|
-
interface MoneyDescriptor {
|
|
394
|
-
readonly _noydbMoney: true;
|
|
395
|
-
readonly mode: 'fixed' | 'multi';
|
|
396
|
-
readonly options: MoneyOptions;
|
|
397
|
-
readonly rounding: RoundingMode | undefined;
|
|
398
|
-
/** The currency for fixed mode; `undefined` in multi mode. */
|
|
399
|
-
readonly fixedCurrency: string | undefined;
|
|
400
|
-
/** Resolve the scale for a currency, throwing if not allowed / unknown. */
|
|
401
|
-
scaleFor(currency: string): number;
|
|
402
|
-
/** Whether this descriptor permits the given currency. */
|
|
403
|
-
allows(currency: string): boolean;
|
|
404
|
-
/**
|
|
405
|
-
* The single currency this descriptor implies, if any — fixed mode, or
|
|
406
|
-
* multi mode with exactly one allow-listed currency. Lets a multi field
|
|
407
|
-
* accept a bare amount unambiguously. `undefined` otherwise.
|
|
408
|
-
*/
|
|
409
|
-
soleCurrency(): string | undefined;
|
|
410
|
-
}
|
|
411
|
-
/** Raised when a written value carries more precision than `scale` allows. */
|
|
412
|
-
declare class MoneyPrecisionError extends NoydbError {
|
|
413
|
-
readonly field: string;
|
|
414
|
-
readonly value: unknown;
|
|
415
|
-
readonly scale: number;
|
|
416
|
-
constructor(field: string, value: unknown, scale: number);
|
|
417
|
-
}
|
|
418
|
-
/** Raised when a currency is disallowed or has no resolvable scale. */
|
|
419
|
-
declare class MoneyCurrencyError extends NoydbError {
|
|
420
|
-
readonly currency: string;
|
|
421
|
-
readonly reason: 'not-allowed' | 'unknown-scale';
|
|
422
|
-
readonly field?: string | undefined;
|
|
423
|
-
constructor(currency: string, reason: 'not-allowed' | 'unknown-scale', field?: string | undefined);
|
|
424
|
-
}
|
|
425
|
-
/** Raised when an aggregate operation is not supported on a money field. */
|
|
426
|
-
declare class MoneyUnsupportedError extends NoydbError {
|
|
427
|
-
readonly field: string;
|
|
428
|
-
constructor(field: string, message?: string);
|
|
429
|
-
}
|
|
430
|
-
/** Create a {@link MoneyDescriptor}. */
|
|
431
|
-
declare function money(options: MoneyOptions): MoneyDescriptor;
|
|
432
|
-
/** Runtime predicate for detecting a {@link MoneyDescriptor}. */
|
|
433
|
-
declare function isMoneyDescriptor(x: unknown): x is MoneyDescriptor;
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Per-layer i18n resolution policy.
|
|
437
|
-
*
|
|
438
|
-
* `onMissing` governs what happens when a multilingual field is resolved
|
|
439
|
-
* to a target locale that is absent. It may be a single scalar policy or
|
|
440
|
-
* a per-layer map, so a field can be lenient at the app read boundary but
|
|
441
|
-
* strict inside a materialized view.
|
|
442
|
-
*
|
|
443
|
-
* Effective policy for layer `λ`:
|
|
444
|
-
*
|
|
445
|
-
* ```
|
|
446
|
-
* explicit(λ) = typeof onMissing === 'object' ? onMissing[λ] : undefined
|
|
447
|
-
* scalar = typeof onMissing === 'string' ? onMissing : undefined
|
|
448
|
-
* policy(λ) = explicit(λ) ?? layerDefault(λ) ?? scalar ?? 'throw'
|
|
449
|
-
* ```
|
|
450
|
-
*
|
|
451
|
-
* - `layerDefault('guard') = 'substitute'` — guards are lenient unless
|
|
452
|
-
* EXPLICITLY overridden; they never inherit a scalar policy (a guard
|
|
453
|
-
* reading a display value must not hard-fail on a missing locale).
|
|
454
|
-
* - every other layer has no default, so it inherits the scalar, else
|
|
455
|
-
* falls back to `'throw'` (today's behavior — zero breaking change).
|
|
456
|
-
*
|
|
457
|
-
* @public
|
|
458
|
-
*/
|
|
459
|
-
type OnMissing = 'substitute' | 'null' | 'throw';
|
|
460
|
-
/**
|
|
461
|
-
* The contexts in which a multilingual field is resolved. Each can carry
|
|
462
|
-
* its own `onMissing` policy.
|
|
463
|
-
*
|
|
464
|
-
* - `read` — ordinary app reads (`get`/`list`/query projection).
|
|
465
|
-
* - `guard` — a guard callback reading a value.
|
|
466
|
-
* - `join` — a joined record expanded onto a row.
|
|
467
|
-
* - `mv` — materialized-view input.
|
|
468
|
-
* - `derivation` — derivation input.
|
|
469
|
-
* - `export` — bundle/public-envelope export.
|
|
470
|
-
*/
|
|
471
|
-
type Layer = 'read' | 'guard' | 'join' | 'mv' | 'derivation' | 'export';
|
|
472
|
-
/** Field-level policy: a single scalar, or a per-layer map. */
|
|
473
|
-
type OnMissingPolicy = OnMissing | Partial<Record<Layer, OnMissing>>;
|
|
474
|
-
/**
|
|
475
|
-
* Resolve the effective `OnMissing` for a layer from a field's declared
|
|
476
|
-
* policy. See module docs for the resolution rule.
|
|
477
|
-
*/
|
|
478
|
-
declare function resolvePolicy(onMissing: OnMissingPolicy | undefined, layer: Layer): OnMissing;
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* i18nText schema type —
|
|
482
|
-
*
|
|
483
|
-
* `i18nText({ languages, required })` creates a descriptor for a
|
|
484
|
-
* multi-language content field whose value is stored as a
|
|
485
|
-
* `{ [locale]: string }` map (e.g. `{ en: 'Consulting', th: 'ที่ปรึกษา' }`).
|
|
486
|
-
*
|
|
487
|
-
* On put, the descriptor validates that required languages are present.
|
|
488
|
-
* On read (when a `locale` option is passed), the map is collapsed to the
|
|
489
|
-
* caller's locale string via the fallback chain.
|
|
490
|
-
*
|
|
491
|
-
* Design decisions
|
|
492
|
-
* ────────────────
|
|
493
|
-
*
|
|
494
|
-
* **Descriptor pattern (not a Zod type).**
|
|
495
|
-
* `i18nText()` returns a plain descriptor object used in the collection's
|
|
496
|
-
* `i18nFields` option — same pattern as `ref()` / `dictKey()`. This keeps
|
|
497
|
-
* `@noy-db/core` at zero runtime dependencies and avoids Zod v3 field-type
|
|
498
|
-
* constraints. TypeScript inference is handled via the descriptor's type.
|
|
499
|
-
*
|
|
500
|
-
* **Enforcement at the collection boundary.**
|
|
501
|
-
* The `required` option is checked by `Collection.put()` via the compartment's
|
|
502
|
-
* registered `i18nFields`. Failed validation throws `MissingTranslationError`
|
|
503
|
-
* — a distinct class from `SchemaValidationError` so callers can tell
|
|
504
|
-
* "wrong shape" from "missing translations".
|
|
505
|
-
*
|
|
506
|
-
* **Resolution is post-decryption.**
|
|
507
|
-
* Locale resolution happens AFTER `decryptRecord()`, as a pure in-memory
|
|
508
|
-
* transform. No additional crypto work is needed. The resolved record is
|
|
509
|
-
* returned in place of the stored one, with i18nText fields replaced by
|
|
510
|
-
* their locale-resolved strings.
|
|
511
|
-
*
|
|
512
|
-
* **`locale: 'raw'`.**
|
|
513
|
-
* Passing `{ locale: 'raw' }` skips resolution and returns the full
|
|
514
|
-
* `{ [locale]: string }` map — useful for bilingual exports, admin UIs,
|
|
515
|
-
* and any context where all translations must be visible at once.
|
|
516
|
-
*
|
|
517
|
-
* **Out of scope.**
|
|
518
|
-
* Pluralization, RTL rendering, date/number formatting, per-locale CRDT
|
|
519
|
-
* merging.
|
|
520
|
-
*/
|
|
521
|
-
|
|
522
|
-
/** Flatten an intersection into a single object literal for nicer hovers. */
|
|
523
|
-
type Prettify<T> = {
|
|
524
|
-
[K in keyof T]: T[K];
|
|
525
|
-
} & {};
|
|
526
|
-
/**
|
|
527
|
-
* The stored shape of a multilingual field, inferred from its `required`
|
|
528
|
-
* mode — so the compiler forces you to handle an absent optional locale
|
|
529
|
-
* (`string | undefined`) instead of silently yielding `undefined`.
|
|
530
|
-
*
|
|
531
|
-
* Mirrors `i18nText({ languages, required })`:
|
|
532
|
-
* - `'all'` (default) — every locale required: `{ th: string; en: string }`
|
|
533
|
-
* - `'any'` — every locale optional: `{ th?: string; en?: string }`
|
|
534
|
-
* (the "at least one present" guarantee is runtime-only — not expressible
|
|
535
|
-
* in TypeScript — so each key is optional)
|
|
536
|
-
* - `readonly L[]` — listed locales required, the rest optional:
|
|
537
|
-
* `I18nMap<'th'|'en', ['th']>` → `{ th: string; en?: string }`
|
|
538
|
-
*
|
|
539
|
-
* @example
|
|
540
|
-
* ```ts
|
|
541
|
-
* type Lang = 'th' | 'en'
|
|
542
|
-
* interface Contact {
|
|
543
|
-
* name: I18nMap<Lang, 'any'> // { th?: string; en?: string }
|
|
544
|
-
* legalName: I18nMap<Lang, ['th']> // { th: string; en?: string }
|
|
545
|
-
* slug: I18nMap<Lang> // { th: string; en: string }
|
|
546
|
-
* }
|
|
547
|
-
* ```
|
|
548
|
-
*
|
|
549
|
-
* @public
|
|
550
|
-
*/
|
|
551
|
-
type I18nMap<Langs extends string, Required extends 'all' | 'any' | readonly Langs[] = 'all'> = Required extends 'all' ? Record<Langs, string> : Required extends 'any' ? Partial<Record<Langs, string>> : Required extends readonly (infer R extends Langs)[] ? Prettify<Record<R, string> & Partial<Record<Exclude<Langs, R>, string>>> : never;
|
|
552
|
-
/**
|
|
553
|
-
* Options for `i18nText()`.
|
|
554
|
-
*
|
|
555
|
-
* `languages` declares the full set of supported locales. `required`
|
|
556
|
-
* controls which must be present on every `put()`.
|
|
557
|
-
*
|
|
558
|
-
* `autoTranslate` is the per-field opt-in for the `plaintextTranslator`
|
|
559
|
-
* hook. When `true` and a `plaintextTranslator` is configured
|
|
560
|
-
* on `createNoydb()`, missing translations are generated before `put()`.
|
|
561
|
-
* Default: `false`.
|
|
562
|
-
*/
|
|
563
|
-
interface I18nTextOptions {
|
|
564
|
-
/** All supported locale codes (BCP 47). */
|
|
565
|
-
readonly languages: readonly string[];
|
|
566
|
-
/**
|
|
567
|
-
* Which locales must be present on every `put()`.
|
|
568
|
-
*
|
|
569
|
-
* - `'all'` — every declared language must be present.
|
|
570
|
-
* - `'any'` — at least one declared language must be present.
|
|
571
|
-
* - `string[]` — listed locales are required; others are optional.
|
|
572
|
-
*/
|
|
573
|
-
readonly required: 'all' | 'any' | readonly string[];
|
|
574
|
-
/**
|
|
575
|
-
* Per-field opt-in for the `plaintextTranslator` hook.
|
|
576
|
-
* When `true`, missing required translations are auto-generated
|
|
577
|
-
* before `put()` if a translator is configured. Default: `false`.
|
|
578
|
-
*/
|
|
579
|
-
readonly autoTranslate?: boolean;
|
|
580
|
-
/**
|
|
581
|
-
* What to do when this field is resolved to a locale that is absent.
|
|
582
|
-
* A single policy, or a per-layer map (read/guard/join/mv/derivation/
|
|
583
|
-
* export). Default `'throw'` — today's behavior, zero breaking change.
|
|
584
|
-
* See {@link OnMissingPolicy}.
|
|
585
|
-
*
|
|
586
|
-
* NOTE (current wiring): ALL layers are enforced — `read` (`get`/`list`),
|
|
587
|
-
* `guard`, `derivation`, `mv`, `join`, `export`. Guard / derivation
|
|
588
|
-
* `ctx.vault` reads resolve under their own layer policy (`guard` defaults to
|
|
589
|
-
* the lenient `'substitute'`). The `mv` layer fires for materialized views
|
|
590
|
-
* that declare `{ i18nLocale, i18nFields }` — UNION (group-key i18n fields
|
|
591
|
-
* resolve before the unified-row bucketing) and query-form (resolved in
|
|
592
|
-
* `GroupedAggregation.run` before `groupAndReduce`); grouping a raw i18n field
|
|
593
|
-
* without a locale throws. The `join` layer resolves a joined right-side i18n
|
|
594
|
-
* field to the query locale (`toArray({ locale })` or the vault default; raw
|
|
595
|
-
* when locale-less). The `export` layer fires for
|
|
596
|
-
* `exportStream`/`exportJSON({ resolveLabels })` — records collapse to the
|
|
597
|
-
* export locale.
|
|
598
|
-
*/
|
|
599
|
-
readonly onMissing?: OnMissingPolicy;
|
|
600
|
-
/**
|
|
601
|
-
* Ordered preferred-substitute locales used when `onMissing` resolves
|
|
602
|
-
* to `'substitute'` and the target locale is absent. `'any'` as an
|
|
603
|
-
* element means "first non-empty value". A caller-supplied `fallback`
|
|
604
|
-
* at read time takes precedence over this declared list.
|
|
605
|
-
*/
|
|
606
|
-
readonly substitute?: readonly string[];
|
|
607
|
-
/**
|
|
608
|
-
* #285 smart-substitute. When `true`, a missing-locale `substitute` walk that
|
|
609
|
-
* misses the explicit chain prefers the available locale whose script is
|
|
610
|
-
* nearest the target (same script, then Latin) rather than an arbitrary value
|
|
611
|
-
* — e.g. a missing Thai label prefers another Thai (or Latin) translation over
|
|
612
|
-
* an unreadable script. Default `false` (legacy first-non-empty behavior).
|
|
613
|
-
*/
|
|
614
|
-
readonly smartSubstitute?: boolean;
|
|
615
|
-
/**
|
|
616
|
-
* Per-locale script enforcement (write-time). `'auto'` infers the
|
|
617
|
-
* allowed Unicode scripts per locale (asymmetric Latin tolerance); an
|
|
618
|
-
* object overrides per slot. Absent ⇒ no check. See `./script.ts`.
|
|
619
|
-
*/
|
|
620
|
-
readonly script?: 'auto' | Partial<Record<string, readonly string[]>>;
|
|
621
|
-
/**
|
|
622
|
-
* What to do when a slot's value contains characters outside its
|
|
623
|
-
* allowed script set. Default `'reject'`.
|
|
624
|
-
*/
|
|
625
|
-
readonly onScriptViolation?: 'reject' | 'filter' | 'warn';
|
|
626
|
-
/**
|
|
627
|
-
* #435 v1.x — eager-fill empty locale slots from the substitute chain at
|
|
628
|
-
* write time, recording provenance in the internal `_i18nFilled` marker.
|
|
629
|
-
* Mutually exclusive with an EXPLICIT `'throw'` onMissing policy (densify
|
|
630
|
-
* fills every hole, so a throw would be unreachable). Without an explicit
|
|
631
|
-
* `substitute`, fills from `'any'` (first non-empty). Default absent.
|
|
632
|
-
*/
|
|
633
|
-
readonly densifyOnWrite?: boolean;
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Descriptor returned by `i18nText()`. Attach to the collection's
|
|
637
|
-
* `i18nFields` option:
|
|
638
|
-
*
|
|
639
|
-
* ```ts
|
|
640
|
-
* const lineItems = company.collection<LineItem>('line-items', {
|
|
641
|
-
* i18nFields: {
|
|
642
|
-
* description: i18nText({ languages: ['en', 'th'], required: 'all' }),
|
|
643
|
-
* },
|
|
644
|
-
* })
|
|
645
|
-
* ```
|
|
646
|
-
*/
|
|
647
|
-
interface I18nTextDescriptor {
|
|
648
|
-
readonly _noydbI18nText: true;
|
|
649
|
-
readonly options: I18nTextOptions;
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* Create an `I18nTextDescriptor` for a multi-language content field.
|
|
653
|
-
*
|
|
654
|
-
* @param options Language list + enforcement mode.
|
|
655
|
-
*
|
|
656
|
-
* @example
|
|
657
|
-
* ```ts
|
|
658
|
-
* i18nText({ languages: ['en', 'th'], required: 'all' })
|
|
659
|
-
* i18nText({ languages: ['en', 'th'], required: ['th'], autoTranslate: true })
|
|
660
|
-
* ```
|
|
661
|
-
*/
|
|
662
|
-
declare function i18nText(options: I18nTextOptions): I18nTextDescriptor;
|
|
663
|
-
/** Runtime predicate for detecting an `I18nTextDescriptor`. */
|
|
664
|
-
declare function isI18nTextDescriptor(x: unknown): x is I18nTextDescriptor;
|
|
665
|
-
/**
|
|
666
|
-
* Validate that a value is a valid `{ [locale]: string }` map and that
|
|
667
|
-
* all required locales are present. Throws `MissingTranslationError`
|
|
668
|
-
* when the required constraint is violated.
|
|
669
|
-
*
|
|
670
|
-
* Called by `Collection.put()` for each registered `i18nField`.
|
|
671
|
-
*
|
|
672
|
-
* @param value The raw field value from the record being put.
|
|
673
|
-
* @param field The field name (used in the thrown error message).
|
|
674
|
-
* @param descriptor The `i18nText()` descriptor for this field.
|
|
675
|
-
*/
|
|
676
|
-
declare function validateI18nTextValue(value: unknown, field: string, descriptor: I18nTextDescriptor): void;
|
|
677
|
-
/**
|
|
678
|
-
* Resolve an i18nText value (`{ [locale]: string }` map) to a string
|
|
679
|
-
* for the given locale.
|
|
680
|
-
*
|
|
681
|
-
* @param value The stored locale map.
|
|
682
|
-
* @param locale The requested locale code, or `'raw'` to return the map.
|
|
683
|
-
* @param fallback Single locale or ordered list; use `'any'` as the last
|
|
684
|
-
* element to fall back to any available translation.
|
|
685
|
-
* @param field Field name used in `LocaleNotSpecifiedError` messages.
|
|
686
|
-
* @returns The resolved string, OR the original map when `locale === 'raw'`.
|
|
687
|
-
*/
|
|
688
|
-
/** Options for the policy-aware form of {@link resolveI18nText}. */
|
|
689
|
-
interface ResolveI18nOptions {
|
|
690
|
-
/** Effective policy for the resolution layer. Default `'throw'`. */
|
|
691
|
-
readonly policy?: OnMissing;
|
|
692
|
-
/** Declared substitute chain; applied only under policy `'substitute'`. */
|
|
693
|
-
readonly substitute?: readonly string[];
|
|
694
|
-
/**
|
|
695
|
-
* #285 smart-substitute. When `true` and policy is `'substitute'`, after the
|
|
696
|
-
* explicit chain misses, pick the available locale whose script is nearest the
|
|
697
|
-
* target (same script first, then Latin's broad readability) instead of an
|
|
698
|
-
* arbitrary value. Default `false`.
|
|
699
|
-
*/
|
|
700
|
-
readonly smartSubstitute?: boolean;
|
|
701
|
-
}
|
|
702
|
-
declare function resolveI18nText(value: Record<string, string>, locale: string, fallback?: string | readonly string[], field?: string): string | Record<string, string>;
|
|
703
|
-
declare function resolveI18nText(value: Record<string, string>, locale: string, fallback: string | readonly string[] | undefined, field: string | undefined, opts: ResolveI18nOptions): string | Record<string, string> | null;
|
|
704
|
-
/**
|
|
705
|
-
* Return all leaf values at `path`, expanding `[].` array wildcards.
|
|
706
|
-
*
|
|
707
|
-
* - `'name'` → `[obj.name]`
|
|
708
|
-
* - `'address.lineOne'` → `[obj.address.lineOne]`
|
|
709
|
-
* - `'contacts[].title'` → `[obj.contacts[0].title, obj.contacts[1].title, …]`
|
|
710
|
-
*
|
|
711
|
-
* Returns an empty array when the path does not resolve (missing key,
|
|
712
|
-
* wrong type, etc.). Used by `enforceI18nOnPut` to validate nested fields.
|
|
713
|
-
*/
|
|
714
|
-
declare function getAtPath(obj: Record<string, unknown>, path: string): unknown[];
|
|
715
|
-
/**
|
|
716
|
-
* Mutate `obj` in-place, setting `value` at the nested `path`.
|
|
717
|
-
* Supports dot notation (`'address.lineOne'`) but not array wildcards —
|
|
718
|
-
* auto-translate on `contacts[].title` style paths is not supported.
|
|
719
|
-
*/
|
|
720
|
-
declare function setAtPathInPlace(obj: Record<string, unknown>, path: string, value: unknown): void;
|
|
721
|
-
/**
|
|
722
|
-
* Apply locale resolution to a single record, returning a new copy.
|
|
723
|
-
*
|
|
724
|
-
* For each field registered as an `i18nText` descriptor:
|
|
725
|
-
* - If `locale === 'raw'`, the field value is left as the stored map.
|
|
726
|
-
* - Otherwise, the field value is replaced with the resolved string.
|
|
727
|
-
*
|
|
728
|
-
* Field paths support dot notation (`'address.lineOne'`) and array
|
|
729
|
-
* wildcards (`'contacts[].title'`). Top-level fields work as before.
|
|
730
|
-
*
|
|
731
|
-
* @param record The decrypted record.
|
|
732
|
-
* @param i18nFields Map of field path → `I18nTextDescriptor`.
|
|
733
|
-
* @param locale The requested locale (or `'raw'`).
|
|
734
|
-
* @param fallback Fallback chain (optional).
|
|
735
|
-
* @param layer Resolution layer (default `'read'`). Each field's
|
|
736
|
-
* `onMissing` policy is resolved for this layer, so the
|
|
737
|
-
* same record resolves leniently on a get but strictly
|
|
738
|
-
* inside an mv/derivation.
|
|
739
|
-
*/
|
|
740
|
-
declare function applyI18nLocale(record: Record<string, unknown>, i18nFields: Record<string, I18nTextDescriptor>, locale: string, fallback?: string | readonly string[], layer?: Layer): Record<string, unknown>;
|
|
741
|
-
/**
|
|
742
|
-
* Remove the internal densify provenance marker (`_i18nFilled`) from a
|
|
743
|
-
* read-facing record (#435). NON-mutating: returns the same object when the
|
|
744
|
-
* marker is absent, otherwise a shallow copy without the marker.
|
|
745
|
-
*
|
|
746
|
-
* MUST be applied on every user-facing read return — even locale-less ones,
|
|
747
|
-
* which bypass {@link applyI18nLocale}. MUST NOT be applied on the internal
|
|
748
|
-
* prior-read path (decryptRecord / resolveDensifyPrior), where densify needs
|
|
749
|
-
* the marker.
|
|
750
|
-
*/
|
|
751
|
-
declare function stripI18nFilled<T extends Record<string, unknown>>(record: T): T;
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* Query DSL `.groupBy()` —.
|
|
755
|
-
*
|
|
756
|
-
* Chains after `.where()` / `.filter()` / `.or()` / `.and()` on a
|
|
757
|
-
* Query and before a reducer spec, so consumers can compute
|
|
758
|
-
* per-bucket aggregates without folding in userland:
|
|
759
|
-
*
|
|
760
|
-
* ```ts
|
|
761
|
-
* const byClient = invoices.query()
|
|
762
|
-
* .where('status', '==', 'open')
|
|
763
|
-
* .groupBy('clientId')
|
|
764
|
-
* .aggregate({ total: sum('amount'), n: count() })
|
|
765
|
-
* .run()
|
|
766
|
-
* // → [ { clientId: 'c1', total: 5250, n: 3 }, … ]
|
|
767
|
-
* ```
|
|
768
|
-
*
|
|
769
|
-
* Execution pipeline:
|
|
770
|
-
*
|
|
771
|
-
* 1. Run the query's where/filter clauses (same candidate /
|
|
772
|
-
* filter pipeline as `.aggregate()` directly on Query).
|
|
773
|
-
* 2. Partition the matching records into buckets keyed by
|
|
774
|
-
* `readPath(record, field)`. JS `Map` preserves insertion
|
|
775
|
-
* order, so the first-seen key for a bucket determines its
|
|
776
|
-
* position in the result array — consumers who want a
|
|
777
|
-
* specific ordering should `.sort()` downstream.
|
|
778
|
-
* 3. Enforce cardinality: warn once per field at 10% of the cap
|
|
779
|
-
* (10_000 buckets), throw `GroupCardinalityError` at 100% of
|
|
780
|
-
* the cap (100_000 buckets).
|
|
781
|
-
* 4. For each bucket, build a per-group reducer state and
|
|
782
|
-
* step every record in the bucket through it.
|
|
783
|
-
* 5. Emit one result row per bucket, shaped as
|
|
784
|
-
* `{ [field]: key, ...reduced }`.
|
|
785
|
-
*
|
|
786
|
-
* **Null / undefined keys:** `Map` distinguishes `null` from
|
|
787
|
-
* `undefined`, so records with a missing group field get their own
|
|
788
|
-
* bucket, and records with an explicit `null` value get a separate
|
|
789
|
-
* bucket from that. Consumers who want them merged can coalesce
|
|
790
|
-
* upstream with `.filter()`.
|
|
791
|
-
*
|
|
792
|
-
* **Live mode:** `.groupBy().aggregate().live()` re-runs the full
|
|
793
|
-
* grouping pipeline on every source change. Per-bucket incremental
|
|
794
|
-
* delta maintenance is a future optimization — the reducer
|
|
795
|
-
* protocol's `remove()` hook admits it, but ships naive
|
|
796
|
-
* re-grouping for simplicity.
|
|
797
|
-
*
|
|
798
|
-
* **Type-level stable-key narrowing:** when
|
|
799
|
-
* `dictKey` lands, `groupBy<DictField>()` will narrow the group key
|
|
800
|
-
* type to the stable dictionary key rather than the resolved locale
|
|
801
|
-
* label. That prevents grouping by the locale-resolved label,
|
|
802
|
-
* which would produce different buckets per reader. types the
|
|
803
|
-
* key as `unknown` at the result shape; the dictKey narrowing
|
|
804
|
-
* layers on top without an API break.
|
|
805
|
-
*
|
|
806
|
-
* Partition-awareness seam: when partitioned collections land,
|
|
807
|
-
* per-partition grouping will need to merge sub-results across
|
|
808
|
-
* partitions. The reducer protocol's `{ seed }` parameter
|
|
809
|
-
* (already plumbed through in `reducers.ts`) is the mechanism —
|
|
810
|
-
* groupBy doesn't need its own seam for the moment, because it
|
|
811
|
-
* delegates to the reducer protocol for all per-bucket state.
|
|
812
|
-
*/
|
|
813
|
-
|
|
814
|
-
/**
|
|
815
|
-
* Cardinality thresholds for `.groupBy()`. The warn threshold gives
|
|
816
|
-
* consumers a heads-up before the hard error; the cap is a fixed
|
|
817
|
-
* constant in (not overridable). A `{ maxGroups }` override
|
|
818
|
-
* can be added later without a break if a real consumer asks.
|
|
819
|
-
*/
|
|
820
|
-
declare const GROUPBY_WARN_CARDINALITY = 10000;
|
|
821
|
-
declare const GROUPBY_MAX_CARDINALITY = 100000;
|
|
822
|
-
/**
|
|
823
|
-
* Test-only: clear the per-field cardinality warning dedup between
|
|
824
|
-
* tests. Production code never calls this — matching the
|
|
825
|
-
* `resetJoinWarnings` pattern in `join.ts`.
|
|
826
|
-
*/
|
|
827
|
-
declare function resetGroupByWarnings(): void;
|
|
828
|
-
/**
|
|
829
|
-
* Result row shape for a grouped aggregation. Each row carries the
|
|
830
|
-
* group key value under the grouping field name plus every reducer
|
|
831
|
-
* output from the spec.
|
|
832
|
-
*
|
|
833
|
-
* types the group key as `unknown` at the result shape — the
|
|
834
|
-
* runtime read via `readPath` can return any value, and narrowing
|
|
835
|
-
* to a specific type would require the caller to assert at the
|
|
836
|
-
* call site. `dictKey` narrowing layers on top of this by
|
|
837
|
-
* adding an overload that constrains `F` when the grouping field
|
|
838
|
-
* is a `dictKey`.
|
|
839
|
-
*/
|
|
840
|
-
type GroupedRow<F extends string, R> = {
|
|
841
|
-
[K in F]: unknown;
|
|
842
|
-
} & R;
|
|
843
|
-
/**
|
|
844
|
-
* Multi-key variant — result-row shape for variadic
|
|
845
|
-
* `.groupBy(...fields)`. Every grouped field name appears on the row
|
|
846
|
-
* (typed as `unknown` for the same reason as `GroupedRow`), plus the
|
|
847
|
-
* reducer outputs from the spec.
|
|
848
|
-
*/
|
|
849
|
-
type GroupedRowN<F extends readonly string[], R> = {
|
|
850
|
-
[K in F[number]]: unknown;
|
|
851
|
-
} & R;
|
|
852
|
-
/**
|
|
853
|
-
* Shared base class for the chainable grouped-query wrappers. Holds
|
|
854
|
-
* the constructor + protected fields that both single-key
|
|
855
|
-
* `GroupedQuery<T, F>` and variadic `GroupedQueryN<T, F>` need; each
|
|
856
|
-
* subclass only overrides `aggregate()` with its own result-row
|
|
857
|
-
* generic.
|
|
858
|
-
*
|
|
859
|
-
* Not exported — implementation detail. Adding `.having()` /
|
|
860
|
-
* `.live()` / `.orderByGroup()` etc. in the future lands here once
|
|
861
|
-
* and both subclasses pick it up automatically.
|
|
862
|
-
*
|
|
863
|
-
* @internal
|
|
864
|
-
*/
|
|
865
|
-
declare abstract class GroupedQueryBase {
|
|
866
|
-
protected readonly executeRecords: () => readonly unknown[];
|
|
867
|
-
protected readonly upstreams: readonly AggregationUpstream[];
|
|
868
|
-
/**
|
|
869
|
-
* Optional dict label resolver attached by the query builder when
|
|
870
|
-
* the grouping field is a dictKey. Variadic groupings always pass
|
|
871
|
-
* `undefined` — `<field>Label` projection has no meaningful shape
|
|
872
|
-
* for composite keys.
|
|
873
|
-
*/
|
|
874
|
-
protected readonly dictLabelResolver?: ((key: string, locale: string, fallback?: string | readonly string[]) => Promise<string | undefined>) | undefined;
|
|
875
|
-
/**
|
|
876
|
-
* Money field descriptors for the backing collection — used to
|
|
877
|
-
* rewrite `sum`/`min`/`max` over money fields into exact BigInt
|
|
878
|
-
* reducers when `.aggregate(spec)` is terminated.
|
|
879
|
-
*/
|
|
880
|
-
protected readonly moneyFields?: Record<string, MoneyDescriptor> | undefined;
|
|
881
|
-
/**
|
|
882
|
-
* Field set this grouped query buckets on. Stored in declaration
|
|
883
|
-
* order — the same order is preserved on every result row by
|
|
884
|
-
* `groupAndReduce`. For the single-field constructor, this is
|
|
885
|
-
* `[field]`.
|
|
886
|
-
*/
|
|
887
|
-
protected readonly fields: readonly string[];
|
|
888
|
-
constructor(executeRecords: () => readonly unknown[], fieldOrFields: string | readonly string[], upstreams: readonly AggregationUpstream[],
|
|
889
|
-
/**
|
|
890
|
-
* Optional dict label resolver attached by the query builder when
|
|
891
|
-
* the grouping field is a dictKey. Variadic groupings always pass
|
|
892
|
-
* `undefined` — `<field>Label` projection has no meaningful shape
|
|
893
|
-
* for composite keys.
|
|
894
|
-
*/
|
|
895
|
-
dictLabelResolver?: ((key: string, locale: string, fallback?: string | readonly string[]) => Promise<string | undefined>) | undefined,
|
|
896
|
-
/**
|
|
897
|
-
* Money field descriptors for the backing collection — used to
|
|
898
|
-
* rewrite `sum`/`min`/`max` over money fields into exact BigInt
|
|
899
|
-
* reducers when `.aggregate(spec)` is terminated.
|
|
900
|
-
*/
|
|
901
|
-
moneyFields?: Record<string, MoneyDescriptor> | undefined);
|
|
902
|
-
/** Apply money-aware reducer rewriting when money fields are declared. */
|
|
903
|
-
protected wrapSpec<Spec extends AggregateSpec>(spec: Spec): Spec;
|
|
904
|
-
}
|
|
905
|
-
/**
|
|
906
|
-
* Chainable wrapper returned by `Query.groupBy(field)`. Terminates
|
|
907
|
-
* with `.aggregate(spec)` which returns a `GroupedAggregation`.
|
|
908
|
-
*
|
|
909
|
-
* Kept minimal — the only operation on a grouped query is
|
|
910
|
-
* aggregation. Ordering, limiting, and further filtering belong on
|
|
911
|
-
* the underlying `Query` before `.groupBy()` is called; applying
|
|
912
|
-
* them post-group would be a different operation (`having` /
|
|
913
|
-
* `groupOrderBy`), out of scope for.
|
|
914
|
-
*/
|
|
915
|
-
declare class GroupedQuery<T, F extends string> extends GroupedQueryBase {
|
|
916
|
-
/**
|
|
917
|
-
* Build a grouped aggregation. Returns a `GroupedAggregation`
|
|
918
|
-
* with `.run()`, `.runAsync()`, and `.live()` terminals — same shape
|
|
919
|
-
* as the non-grouped `.aggregate()` wrapper, just with an array
|
|
920
|
-
* result (one row per bucket) instead of a single reduced object.
|
|
921
|
-
*/
|
|
922
|
-
aggregate<Spec extends AggregateSpec>(spec: Spec): GroupedAggregation<GroupedRow<F, AggregateResult<Spec>>>;
|
|
923
|
-
}
|
|
924
|
-
/**
|
|
925
|
-
* Variadic-keyed sibling of `GroupedQuery<T, F>`. Constructed by the
|
|
926
|
-
* multi-arg `Query.groupBy(...fields)` overload. The runtime shape is
|
|
927
|
-
* identical — only the type-level result-row narrowing differs.
|
|
928
|
-
*/
|
|
929
|
-
declare class GroupedQueryN<T, F extends readonly string[]> extends GroupedQueryBase {
|
|
930
|
-
aggregate<Spec extends AggregateSpec>(spec: Spec): GroupedAggregation<GroupedRowN<F, AggregateResult<Spec>>>;
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Execute the group-and-reduce pipeline. Pure function over a
|
|
934
|
-
* record array and a spec — shared by `GroupedAggregation.run()`
|
|
935
|
-
* and the live-mode refresh path. Exported for tests and for any
|
|
936
|
-
* future `scan().groupBy().aggregate()` reuse.
|
|
937
|
-
*
|
|
938
|
-
* Enforces the cardinality cap incrementally during the partition
|
|
939
|
-
* loop, so a runaway grouping throws at the moment the 100_001st
|
|
940
|
-
* bucket would be created — the consumer doesn't have to wait for
|
|
941
|
-
* the full partition to materialize before the error fires.
|
|
942
|
-
*/
|
|
943
|
-
declare function groupAndReduce<R>(records: readonly unknown[], fieldOrFields: string | readonly string[], spec: AggregateSpec, moneyFields?: Record<string, MoneyDescriptor>): R[];
|
|
944
|
-
/**
|
|
945
|
-
* Grouped aggregation wrapper — the `.groupBy(field).aggregate(spec)`
|
|
946
|
-
* terminal. Shape mirrors `Aggregation<R>` from aggregate.ts: two
|
|
947
|
-
* terminals (`.run()` and `.live()`), spec bound at construction
|
|
948
|
-
* time, upstreams collected for live mode.
|
|
949
|
-
*
|
|
950
|
-
* The generic `R` is the per-row result shape (i.e. a single
|
|
951
|
-
* grouped row), and the terminals return `R[]` — one row per
|
|
952
|
-
* bucket.
|
|
953
|
-
*/
|
|
954
|
-
declare class GroupedAggregation<R> {
|
|
955
|
-
private readonly executeRecords;
|
|
956
|
-
private readonly spec;
|
|
957
|
-
private readonly upstreams;
|
|
958
|
-
/**
|
|
959
|
-
* Optional dict label resolver for `<field>Label` projection
|
|
960
|
-
*. Present when the grouping field is a dictKey.
|
|
961
|
-
*/
|
|
962
|
-
private readonly dictLabelResolver?;
|
|
963
|
-
private readonly fields;
|
|
964
|
-
constructor(executeRecords: () => readonly unknown[], fields: string | readonly string[], spec: AggregateSpec, upstreams: readonly AggregationUpstream[],
|
|
965
|
-
/**
|
|
966
|
-
* Optional dict label resolver for `<field>Label` projection
|
|
967
|
-
*. Present when the grouping field is a dictKey.
|
|
968
|
-
*/
|
|
969
|
-
dictLabelResolver?: ((key: string, locale: string, fallback?: string | readonly string[]) => Promise<string | undefined>) | undefined);
|
|
970
|
-
/**
|
|
971
|
-
* Execute the query, group, reduce, and return an array of rows.
|
|
972
|
-
*
|
|
973
|
-
* `opts` (#285 query-form MV grouping): when a `locale` + `i18nFields` are
|
|
974
|
-
* given, the declared group-key `i18nText` fields are resolved to that locale
|
|
975
|
-
* at the `mv` layer BEFORE bucketing — so an i18n group key is a stable string
|
|
976
|
-
* instead of a raw `{locale}` map. The MV executor passes the MV's
|
|
977
|
-
* `i18nLocale`/`i18nFields`; ordinary `.run()` callers pass nothing and are
|
|
978
|
-
* unaffected.
|
|
979
|
-
*/
|
|
980
|
-
run(opts?: {
|
|
981
|
-
locale?: string;
|
|
982
|
-
i18nFields?: Record<string, I18nTextDescriptor>;
|
|
983
|
-
}): R[];
|
|
984
|
-
/**
|
|
985
|
-
* Execute the query, group, reduce, and resolve `<field>Label` for
|
|
986
|
-
* each result row when the grouping field is a `dictKey` and a
|
|
987
|
-
* `locale` is provided. Returns `R[]` synchronously when
|
|
988
|
-
* no locale is specified (identical to `.run()`).
|
|
989
|
-
*
|
|
990
|
-
* The `<field>Label` field is appended to each row. Rows whose group
|
|
991
|
-
* key has no dictionary entry get `<field>Label: undefined`.
|
|
992
|
-
*
|
|
993
|
-
* Dict-label resolution is single-field only — multi-key groupings
|
|
994
|
-
* do not produce a `<field>Label`. The resolver is only attached
|
|
995
|
-
* by the builder when `fields.length === 1`.
|
|
996
|
-
*/
|
|
997
|
-
runAsync(opts?: {
|
|
998
|
-
locale?: string;
|
|
999
|
-
fallback?: string | readonly string[];
|
|
1000
|
-
}): Promise<R[]>;
|
|
1001
|
-
/**
|
|
1002
|
-
* Build a reactive `LiveAggregation<R[]>` that re-runs the full
|
|
1003
|
-
* group-and-reduce pipeline whenever any upstream source notifies
|
|
1004
|
-
* of a change. Same error-isolation and idempotent-stop contract
|
|
1005
|
-
* as `Aggregation.live()` — the implementation delegates to the
|
|
1006
|
-
* same `LiveAggregationImpl` class by threading a fresh
|
|
1007
|
-
* recompute closure through the existing constructor.
|
|
1008
|
-
*
|
|
1009
|
-
* uses naive full re-run on every change. Incremental
|
|
1010
|
-
* per-bucket maintenance (apply `step` on inserted records,
|
|
1011
|
-
* `remove` on deleted records, route by bucket key) is a future
|
|
1012
|
-
* optimization — the reducer protocol admits it, but wiring
|
|
1013
|
-
* delta-aware source subscriptions is a separate PR.
|
|
1014
|
-
*
|
|
1015
|
-
* Always call `live.stop()` when finished.
|
|
1016
|
-
*/
|
|
1017
|
-
live(): LiveAggregation<R[]>;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* Strategy seam between the core Query / ScanBuilder chain and the
|
|
1022
|
-
* optional aggregate / groupBy subsystem. Core imports
|
|
1023
|
-
* `AggregateStrategy` as a TYPE-ONLY symbol and `NO_AGGREGATE` as a
|
|
1024
|
-
* tiny runtime stub.
|
|
1025
|
-
*
|
|
1026
|
-
* The heavy machinery — `Aggregation`, `GroupedQuery`, the
|
|
1027
|
-
* reducer-step logic — is only reachable from `withAggregate()` in
|
|
1028
|
-
* `./active.ts`, which is only exported through the
|
|
1029
|
-
* `@noy-db/hub/aggregate` subpath. Consumers that don't import the
|
|
1030
|
-
* subpath ship none of the ~886 LOC.
|
|
1031
|
-
*
|
|
1032
|
-
* @internal
|
|
1033
|
-
*/
|
|
1034
|
-
|
|
1035
|
-
/**
|
|
1036
|
-
* Seam interface. `@internal` — will promote to public only when the
|
|
1037
|
-
* aggregate subsystem is extracted into its own package.
|
|
1038
|
-
*
|
|
1039
|
-
* @internal
|
|
1040
|
-
*/
|
|
1041
|
-
interface AggregateStrategy {
|
|
1042
|
-
/**
|
|
1043
|
-
* Build an `Aggregation<R>` for `Query.aggregate(spec)`. `executeRecords`
|
|
1044
|
-
* is a closure that produces the matching record set when the
|
|
1045
|
-
* aggregation runs. NO_AGGREGATE throws; the active strategy
|
|
1046
|
-
* constructs a real `Aggregation`.
|
|
1047
|
-
*/
|
|
1048
|
-
aggregate<Spec extends AggregateSpec>(executeRecords: () => readonly unknown[], spec: Spec, upstreams: readonly AggregationUpstream[]): Aggregation<AggregateResult<Spec>>;
|
|
1049
|
-
/**
|
|
1050
|
-
* Build a `GroupedQuery<T, F>` for `Query.groupBy(field)`. Same
|
|
1051
|
-
* closure / upstream inputs as `aggregate` plus the group key field.
|
|
1052
|
-
*/
|
|
1053
|
-
groupBy<T, F extends string>(executeRecords: () => readonly unknown[], field: F, upstreams: readonly AggregationUpstream[], dictLabelResolver?: (key: string, locale: string, fallback?: string | readonly string[]) => Promise<string | undefined>, moneyFields?: Record<string, MoneyDescriptor>): GroupedQuery<T, F>;
|
|
1054
|
-
/**
|
|
1055
|
-
* Variadic-keyed sibling — builds a `GroupedQueryN<T, F>` for
|
|
1056
|
-
* `Query.groupBy(...fields)`. No dictLabelResolver — `<field>Label`
|
|
1057
|
-
* projection only applies to single-field groupings, which dispatch
|
|
1058
|
-
* through `groupBy` above.
|
|
1059
|
-
*/
|
|
1060
|
-
groupByN<T, F extends readonly string[]>(executeRecords: () => readonly unknown[], fields: F, upstreams: readonly AggregationUpstream[], moneyFields?: Record<string, MoneyDescriptor>): GroupedQueryN<T, F>;
|
|
1061
|
-
/**
|
|
1062
|
-
* Terminal streaming aggregator for `ScanBuilder.aggregate(spec)`.
|
|
1063
|
-
* Takes an async iterable of decrypted records + the spec and
|
|
1064
|
-
* returns the reduced result.
|
|
1065
|
-
*/
|
|
1066
|
-
scanAggregate<Spec extends AggregateSpec>(iter: AsyncIterable<unknown>, spec: Spec): Promise<AggregateResult<Spec>>;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
export { type AggregateStrategy as A, buildLiveAggregation as B, count as C, groupAndReduce as D, max as E, min as F, GROUPBY_MAX_CARDINALITY as G, reduceRecords as H, type I18nMap as I, resetGroupByWarnings as J, sum as K, type Layer as L, type RoundingMode as M, MoneyCurrencyError as N, type OnMissing as O, type MoneyDescriptor as P, type MoneyOptions as Q, type ResolveI18nOptions as R, type MoneyOptionsFixed as S, type MoneyOptionsMulti as T, MoneyPrecisionError as U, MoneyUnsupportedError as V, isMoneyDescriptor as W, money as X, type I18nTextDescriptor as a, type I18nTextOptions as b, type OnMissingPolicy as c, applyI18nLocale as d, isI18nTextDescriptor as e, resolvePolicy as f, getAtPath as g, stripI18nFilled as h, i18nText as i, type AggregateResult as j, type AggregateSpec as k, Aggregation as l, type AggregationUpstream as m, GROUPBY_WARN_CARDINALITY as n, GroupedAggregation as o, GroupedQuery as p, GroupedQueryN as q, resolveI18nText as r, setAtPathInPlace as s, type GroupedRow as t, type GroupedRowN as u, validateI18nTextValue as v, type LiveAggregation as w, type Reducer as x, type ReducerOptions as y, avg as z };
|