@jarenjs/linq 0.46.5 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +217 -0
- package/README.md +566 -17
- package/docs/APP-PEN.md +1143 -0
- package/docs/CONTRACT-PEN.md +1217 -0
- package/docs/DB-CLIENT.md +814 -0
- package/docs/FLOW-PEN.md +1026 -0
- package/docs/FORMS-PEN.md +940 -0
- package/docs/JSLT-PEN.md +955 -0
- package/docs/LINQ-FORMAT.md +774 -384
- package/docs/MIGRATION-PEN.md +781 -0
- package/docs/MODEL-PEN.md +1083 -0
- package/docs/QUERY-PEN.md +1636 -0
- package/docs/SCHEMA-PEN.md +1218 -0
- package/package.json +57 -4
- package/src/app/action.js +255 -0
- package/src/app/capture.js +63 -0
- package/src/app/define.js +260 -0
- package/src/app/index.js +20 -0
- package/src/app/patch.js +277 -0
- package/src/app/sub.js +106 -0
- package/src/async.js +329 -75
- package/src/capture-root.js +82 -0
- package/src/concurrency.js +9 -4
- package/src/contract/define.js +269 -0
- package/src/contract/http.js +247 -0
- package/src/contract/index.js +23 -0
- package/src/contract/operation.js +342 -0
- package/src/db/handle.js +86 -0
- package/src/db/include.js +316 -0
- package/src/db/index.js +19 -0
- package/src/db/live.js +43 -0
- package/src/db/membership.js +37 -0
- package/src/db/open.js +82 -0
- package/src/document.js +143 -13
- package/src/effect.js +65 -0
- package/src/errors.js +69 -6
- package/src/expression.js +532 -39
- package/src/flow/capture.js +33 -0
- package/src/flow/dag.js +302 -0
- package/src/flow/fsm.js +328 -0
- package/src/flow/index.js +22 -0
- package/src/forms/index.js +43 -0
- package/src/forms/rules.js +170 -0
- package/src/forms/submit.js +177 -0
- package/src/index.js +4 -2
- package/src/jslt/body.js +226 -0
- package/src/jslt/index.js +18 -0
- package/src/jslt/rules.js +207 -0
- package/src/json-boundary.js +90 -0
- package/src/migration/define.js +323 -0
- package/src/migration/index.js +15 -0
- package/src/migration/steps.js +248 -0
- package/src/model/collection.js +171 -0
- package/src/model/define.js +125 -0
- package/src/model/entity.js +307 -0
- package/src/model/index.js +47 -0
- package/src/model/relation.js +85 -0
- package/src/provider.js +137 -20
- package/src/schema/brand.js +31 -0
- package/src/schema/builders.js +526 -0
- package/src/schema/check.js +29 -0
- package/src/schema/emit.js +394 -0
- package/src/schema/factories.js +239 -0
- package/src/schema/index.js +37 -0
- package/src/schema-of.js +24 -0
- package/src/sequence.js +233 -103
- package/src/sources.js +10 -3
- package/types/app.d.ts +293 -0
- package/types/contract.d.ts +371 -0
- package/types/db.d.ts +188 -0
- package/types/flow.d.ts +285 -0
- package/types/forms.d.ts +253 -0
- package/types/index.d.ts +389 -41
- package/types/jslt.d.ts +193 -0
- package/types/migration.d.ts +201 -0
- package/types/model.d.ts +493 -0
- package/types/schema.d.ts +494 -0
package/types/index.d.ts
CHANGED
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* The nominal date-time brand: annotate a model property as `DateTime`
|
|
17
|
-
* and the date
|
|
18
|
-
*
|
|
19
|
-
* date. Purely a type-level marker — the runtime value is a plain
|
|
17
|
+
* and the whole §8.13 date family (`year()`, `startOf()`, `dateAdd()`,
|
|
18
|
+
* `timeBucket()`, …) becomes available on its expression without making
|
|
19
|
+
* EVERY string a date. Purely a type-level marker — the runtime value is a plain
|
|
20
20
|
* RFC 3339 string; there is no constructor and no runtime cost.
|
|
21
21
|
*/
|
|
22
|
-
export type DateTime = string & {
|
|
22
|
+
export type DateTime = string & { __jarenTag: 'date-time' };
|
|
23
|
+
|
|
24
|
+
import type { SchemaBuilder } from './schema.js';
|
|
23
25
|
|
|
24
26
|
/** The phantom carrier every expression type extends: `__value` never
|
|
25
27
|
* exists at runtime; it lets projections infer their unwrapped shape. */
|
|
@@ -59,9 +61,13 @@ export interface NumberExpr extends ExprBase<number>, EqExpr<number> {
|
|
|
59
61
|
idiv(value: number | NumberExpr): NumberExpr;
|
|
60
62
|
mod(value: number | NumberExpr): NumberExpr;
|
|
61
63
|
neg(): NumberExpr;
|
|
64
|
+
/** An epoch is a number, so the two instant operators live here too. */
|
|
65
|
+
datetime(): DateTimeExpr;
|
|
66
|
+
timeBucket(every: string | number, origin?: string | number | null,
|
|
67
|
+
context?: CalendarContext): NumberExpr;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
export interface StringExpr extends ExprBase<string>, EqExpr<string
|
|
70
|
+
export interface StringExpr extends ExprBase<string>, EqExpr<string>, SpatialMethods {
|
|
65
71
|
lt(value: string | StringExpr): BoolExpr;
|
|
66
72
|
le(value: string | StringExpr): BoolExpr;
|
|
67
73
|
gt(value: string | StringExpr): BoolExpr;
|
|
@@ -79,35 +85,235 @@ export interface StringExpr extends ExprBase<string>, EqExpr<string> {
|
|
|
79
85
|
replace(pattern: string, replacement: string): StringExpr;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
/** A
|
|
83
|
-
*
|
|
84
|
-
|
|
88
|
+
/** A calendar unit, as QUERY-FORMAT §8.13 fixes it. The unit is DATA
|
|
89
|
+
* rather than vocabulary, so an unknown one is a runtime `JQ2001`; this
|
|
90
|
+
* type is what keeps the common spelling mistake a compile error. */
|
|
91
|
+
export type DateUnit =
|
|
92
|
+
| 'year' | 'quarter' | 'month' | 'week' | 'day'
|
|
93
|
+
| 'hour' | 'minute' | 'second' | 'millisecond';
|
|
94
|
+
|
|
95
|
+
/** The date family (`$is-date` … `$date-format`), available on any
|
|
96
|
+
* expression whose value carries a date. Every method lowers to the §8.13
|
|
97
|
+
* operator of the same name; there is no LINQ-only date semantics. */
|
|
98
|
+
export interface DateMethods {
|
|
99
|
+
/** Lexical date components; a value with no date half is `JQ2001`. */
|
|
100
|
+
year(): NumberExpr;
|
|
101
|
+
month(): NumberExpr;
|
|
102
|
+
day(): NumberExpr;
|
|
103
|
+
/** Lexical time components; `seconds` carries its fraction. */
|
|
104
|
+
hours(): NumberExpr;
|
|
105
|
+
minutes(): NumberExpr;
|
|
106
|
+
seconds(): NumberExpr;
|
|
107
|
+
/** Minutes east of UTC; a bare `full-date` yields the empty sequence. */
|
|
108
|
+
offset(): NumberExpr;
|
|
109
|
+
/** ISO 8601 week number, and its week-numbering year. */
|
|
110
|
+
week(): NumberExpr;
|
|
111
|
+
weekYear(): NumberExpr;
|
|
112
|
+
/** Calendar quarter 1-4; ISO weekday 1 (Monday) to 7 (Sunday). */
|
|
113
|
+
quarter(): NumberExpr;
|
|
114
|
+
weekday(): NumberExpr;
|
|
115
|
+
/** Epoch milliseconds (`$epoch`) — the one shift to UTC. */
|
|
116
|
+
epoch(): NumberExpr;
|
|
117
|
+
/** The inverse: epoch milliseconds → a canonical UTC `date-time`. */
|
|
118
|
+
datetime(): DateTimeExpr;
|
|
119
|
+
/** The RFC 3339 lexical-form predicates; these never raise. */
|
|
120
|
+
isDate(): BoolExpr;
|
|
121
|
+
isTime(): BoolExpr;
|
|
122
|
+
isDatetime(): BoolExpr;
|
|
123
|
+
isDuration(): BoolExpr;
|
|
124
|
+
/** Truncate to a unit, keeping the lexical form (`$start-of`/`$end-of`). */
|
|
125
|
+
startOf(unit: DateUnit): DateTimeExpr;
|
|
126
|
+
endOf(unit: DateUnit): DateTimeExpr;
|
|
127
|
+
/** Shift by an ISO 8601 duration, or by an amount and a unit. */
|
|
128
|
+
dateAdd(duration: string): DateTimeExpr;
|
|
129
|
+
dateAdd(amount: number | NumberExpr, unit: DateUnit): DateTimeExpr;
|
|
130
|
+
dateSub(duration: string): DateTimeExpr;
|
|
131
|
+
dateSub(amount: number | NumberExpr, unit: DateUnit): DateTimeExpr;
|
|
132
|
+
/** Whole units from this value to another; negative when it precedes. */
|
|
133
|
+
dateDiff(to: string | DateTimeExpr, unit: DateUnit): NumberExpr;
|
|
134
|
+
/** Render through a Unicode LDML pattern (`yyyy-MM-dd`). */
|
|
135
|
+
dateFormat(pattern: string): StringExpr;
|
|
136
|
+
/** The instant labelling the bucket this one falls in (`$time-bucket`). */
|
|
137
|
+
timeBucket(every: string | number, origin?: string | number | null,
|
|
138
|
+
context?: CalendarContext): NumberExpr;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** A `DateTime`-branded string: the string surface plus the whole §8.13
|
|
142
|
+
* date family. */
|
|
143
|
+
export interface DateTimeExpr extends ExprBase<DateTime>, DateMethods {
|
|
85
144
|
eq(value: string | DateTimeExpr | null): BoolExpr;
|
|
86
145
|
ne(value: string | DateTimeExpr | null): BoolExpr;
|
|
87
146
|
lt(value: string | DateTimeExpr): BoolExpr;
|
|
88
147
|
le(value: string | DateTimeExpr): BoolExpr;
|
|
89
148
|
gt(value: string | DateTimeExpr): BoolExpr;
|
|
90
149
|
ge(value: string | DateTimeExpr): BoolExpr;
|
|
91
|
-
year(): NumberExpr;
|
|
92
|
-
month(): NumberExpr;
|
|
93
|
-
day(): NumberExpr;
|
|
94
|
-
/** Epoch milliseconds (`$epoch`). */
|
|
95
|
-
epoch(): NumberExpr;
|
|
96
150
|
}
|
|
97
151
|
|
|
98
|
-
|
|
152
|
+
/** The wall clock a calendar boundary falls on (QUERY-FORMAT §8.16).
|
|
153
|
+
* UTC is the default; a named `zone` needs an injected `zoneProvider`. */
|
|
154
|
+
export interface CalendarContext {
|
|
155
|
+
zone?: string;
|
|
156
|
+
offset?: number;
|
|
157
|
+
disambiguation?: 'reject' | 'earlier' | 'later';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The aggregates `$resample` and `$rolling` share. */
|
|
161
|
+
export type SeriesAggregate =
|
|
162
|
+
'sum' | 'mean' | 'min' | 'max' | 'first' | 'last' | 'count';
|
|
163
|
+
|
|
164
|
+
/** What an EMPTY bucket says, and nothing else. */
|
|
165
|
+
export type SeriesFill = 'omit' | 'null' | 'zero' | 'locf' | 'linear';
|
|
166
|
+
|
|
167
|
+
/** A width: an ISO 8601 duration, or a count of milliseconds. */
|
|
168
|
+
export type SeriesSpan = string | number;
|
|
169
|
+
|
|
170
|
+
/** An instant: epoch milliseconds, or an RFC 3339 string. */
|
|
171
|
+
export type SeriesInstant = string | number;
|
|
172
|
+
|
|
173
|
+
/** A row selector: a singular path whose `$` is the ROW rather than the
|
|
174
|
+
* document (`'$.on'`, `"$['recorded at']"`). */
|
|
175
|
+
export type RowSelector = string;
|
|
176
|
+
|
|
177
|
+
/** The `$resample` spec — a literal, read once when the query compiles. */
|
|
178
|
+
export interface ResampleSpec extends CalendarContext {
|
|
179
|
+
every: SeriesSpan;
|
|
180
|
+
origin?: SeriesInstant;
|
|
181
|
+
start?: SeriesInstant;
|
|
182
|
+
end?: SeriesInstant;
|
|
183
|
+
aggregate?: SeriesAggregate;
|
|
184
|
+
fill?: SeriesFill;
|
|
185
|
+
at?: RowSelector;
|
|
186
|
+
value?: RowSelector;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The `$rolling` spec — a window measured in time, not in rows. */
|
|
190
|
+
export interface RollingSpec extends CalendarContext {
|
|
191
|
+
width: SeriesSpan;
|
|
192
|
+
aggregate?: SeriesAggregate;
|
|
193
|
+
minPeriods?: number;
|
|
194
|
+
at?: RowSelector;
|
|
195
|
+
value?: RowSelector;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** The `$asof` spec — every member optional: backward, unkeyed, unbounded. */
|
|
199
|
+
export interface AsOfSpec {
|
|
200
|
+
direction?: 'backward' | 'forward' | 'nearest';
|
|
201
|
+
tolerance?: SeriesSpan;
|
|
202
|
+
by?: RowSelector;
|
|
203
|
+
leftAt?: RowSelector;
|
|
204
|
+
rightAt?: RowSelector;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** One canonical sample the series operators answer with. */
|
|
208
|
+
export interface SeriesBucket {
|
|
209
|
+
at: number;
|
|
210
|
+
value: number | null;
|
|
211
|
+
count: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** One row of an as-of join; `right` is `null` when nothing matched, and
|
|
215
|
+
* the row stays in the answer. */
|
|
216
|
+
export interface AsOfMatch {
|
|
217
|
+
left: unknown;
|
|
218
|
+
right: unknown;
|
|
219
|
+
distance: number | null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** A half-open interval: `[start, end)`, in epoch milliseconds. */
|
|
223
|
+
export interface Interval {
|
|
224
|
+
start: SeriesInstant;
|
|
225
|
+
end: SeriesInstant;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** The §8.16 operators that take a whole series and answer another one.
|
|
229
|
+
* Available wherever a MANY-cardinality expression is (an array member,
|
|
230
|
+
* or a fanned path). */
|
|
231
|
+
export interface SeriesMethods {
|
|
232
|
+
/** Sorted `{at, value, count}` buckets, one per `every` (`$resample`). */
|
|
233
|
+
resample(spec: ResampleSpec): Expr<SeriesBucket[]> & AggregatableExpr;
|
|
234
|
+
/** One row per input instant, over a window measured in time. */
|
|
235
|
+
rolling(spec: RollingSpec): Expr<SeriesBucket[]> & AggregatableExpr;
|
|
236
|
+
/** The right row that was current when each left row happened. */
|
|
237
|
+
asof(right: ExprBase<unknown> | readonly unknown[], spec?: AsOfSpec):
|
|
238
|
+
Expr<AsOfMatch[]> & AggregatableExpr;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** A geometry operand: a GeoJSON geometry or position, a WKT string,
|
|
242
|
+
* or an expression yielding one. */
|
|
243
|
+
export type GeoOperand = ExprBase<unknown> | object | readonly number[] | string;
|
|
244
|
+
|
|
245
|
+
/** The §8.14 family, one method per operator, on every expression kind
|
|
246
|
+
* a geometry can be — a position or bbox array, a GeoJSON object, a WKT
|
|
247
|
+
* or geohash string — and on the honest top. Measurements are numbers,
|
|
248
|
+
* cells and text are strings, a bbox and a centroid are positions; a
|
|
249
|
+
* result that is a geometry stays `UnknownExpr`, because a GeoJSON
|
|
250
|
+
* shape is not a TypeScript type this surface can promise. */
|
|
251
|
+
export interface SpatialMethods {
|
|
252
|
+
/** `[west, south, east, north]` of the value's positions. */
|
|
253
|
+
bbox(): ArrayExpr<number>;
|
|
254
|
+
/** Square metres of the value's polygons (`$area`). */
|
|
255
|
+
geoArea(): NumberExpr;
|
|
256
|
+
/** Metres of the value's lines and ring perimeters (`$length`). */
|
|
257
|
+
geoLength(): NumberExpr;
|
|
258
|
+
/** The mean of the value's positions, as a position. */
|
|
259
|
+
centroid(): ArrayExpr<number>;
|
|
260
|
+
/** Metres between the two representative positions. */
|
|
261
|
+
distance(other: GeoOperand): NumberExpr;
|
|
262
|
+
/** Is this value's representative position inside `region`'s surface? */
|
|
263
|
+
within(region: GeoOperand): BoolExpr;
|
|
264
|
+
/** Do the two bounding boxes overlap? Touching edges count. */
|
|
265
|
+
bboxIntersects(other: GeoOperand): BoolExpr;
|
|
266
|
+
/** The base-32 geohash cell (precision 1–12, default 9). */
|
|
267
|
+
geohash(precision?: number | NumberExpr): StringExpr;
|
|
268
|
+
/** A Well-Known Text string → the geometry it denotes. */
|
|
269
|
+
geoParse(): UnknownExpr;
|
|
270
|
+
/** Any value → its Well-Known Text. */
|
|
271
|
+
geoText(): StringExpr;
|
|
272
|
+
/** A cell string → the `Polygon` covering that cell. */
|
|
273
|
+
geohashBounds(): UnknownExpr;
|
|
274
|
+
/** A cell string → the cell and its neighbours, a SEQUENCE of up to
|
|
275
|
+
* nine cell strings (aggregate it, or `at()` is not available). */
|
|
276
|
+
geohashNeighbours(): StringExpr & AggregatableExpr;
|
|
277
|
+
/** The same value with vertices dropped; the tolerance is in degrees. */
|
|
278
|
+
geoSimplify(tolerance: number | NumberExpr): UnknownExpr;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface ArrayExpr<E> extends ExprBase<E[]>, SeriesMethods, SpatialMethods {
|
|
99
282
|
eq(value: readonly E[] | ArrayExpr<E> | null): BoolExpr;
|
|
100
283
|
ne(value: readonly E[] | ArrayExpr<E> | null): BoolExpr;
|
|
284
|
+
/** Do two half-open `{start, end}` intervals share an instant?
|
|
285
|
+
* Touching spans do not. */
|
|
286
|
+
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
101
287
|
/** Fan the elements out (`[*]`) — a MANY-cardinality expression the
|
|
102
|
-
* aggregates
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
288
|
+
* aggregates and the §8.16 sequence operators apply to
|
|
289
|
+
* (`u.tags.all().count()`, `rows.all().resample(spec)`); an object
|
|
290
|
+
* element's members are fanned with it (`lines.all().amount.sum()`). */
|
|
291
|
+
all(): FannedExpr<E>;
|
|
292
|
+
/** The element at a 0-based index; negative counts from the end. A
|
|
293
|
+
* computed index is an expression — the engine's `$get` — which is
|
|
294
|
+
* what an app-pen patch path lowers to a `$concat` pointer. */
|
|
295
|
+
at(index: number | NumberExpr): Expr<E>;
|
|
296
|
+
/** `$count`. Over a GROUP — a `groupBy`'s `items`, a group-join's
|
|
297
|
+
* group — this is the number of rows, because the chain knows those
|
|
298
|
+
* are rows and fans them (QUERY-PEN.md §13.3). Over an array a caller
|
|
299
|
+
* stored, it counts the array as one item, because at capture time an
|
|
300
|
+
* array member and a scalar member are the same path: fan it first
|
|
301
|
+
* (`u.tags.all().count()`), or ask `exists()` what this would
|
|
302
|
+
* otherwise be answering. */
|
|
108
303
|
count(): NumberExpr;
|
|
304
|
+
/** Cosine similarity to another vector (§8.15): a captured array
|
|
305
|
+
* embeds as a literal, a `params()` binding stays an external. Only a
|
|
306
|
+
* numeric array is a vector. */
|
|
307
|
+
similarity(this: ArrayExpr<number>, other: readonly number[] | ArrayExpr<number>): NumberExpr;
|
|
109
308
|
}
|
|
110
309
|
|
|
310
|
+
/** A fanned path (`$.lines[*]`): the element's expression, aggregatable,
|
|
311
|
+
* and — for an object element — every member is a fanned path too
|
|
312
|
+
* (`$.lines[*].amount`), so it aggregates without a second `all()`. */
|
|
313
|
+
export type FannedExpr<E> = Expr<E> & AggregatableExpr & SeriesMethods &
|
|
314
|
+
([E] extends [readonly unknown[]] ? {} :
|
|
315
|
+
[E] extends [object] ? { readonly [K in keyof E & string]-?: MemberExpr<E[K]> & AggregatableExpr } : {});
|
|
316
|
+
|
|
111
317
|
/** Aggregates available on any expression (a fanned path, a group). */
|
|
112
318
|
export interface AggregatableExpr {
|
|
113
319
|
count(): NumberExpr;
|
|
@@ -118,15 +324,25 @@ export interface AggregatableExpr {
|
|
|
118
324
|
}
|
|
119
325
|
|
|
120
326
|
/** An object's expression: exactly its properties, recursively typed —
|
|
121
|
-
* which is what makes a misspelled member a compile error.
|
|
122
|
-
|
|
123
|
-
|
|
327
|
+
* which is what makes a misspelled member a compile error. A GeoJSON
|
|
328
|
+
* object carries the spatial family; a `{ start, end }` object the
|
|
329
|
+
* interval test. */
|
|
330
|
+
export type ObjectExpr<T> = ExprBase<T> & EqExpr<T> & SpatialMethods & {
|
|
331
|
+
/** Do two half-open `{start, end}` intervals share an instant? */
|
|
332
|
+
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
333
|
+
} & {
|
|
334
|
+
readonly [K in keyof T & string]-?: MemberExpr<T[K]>;
|
|
124
335
|
};
|
|
125
336
|
|
|
337
|
+
/** A member's expression: an `unknown` (or `any`) member is the honest
|
|
338
|
+
* top, never the first conditional arm `Expr<>` would pick for it. */
|
|
339
|
+
export type MemberExpr<V> = unknown extends V ? UnknownExpr : Expr<NonNullable<V>>;
|
|
340
|
+
|
|
126
341
|
/** The honest top: everything is available, nothing is precise. Used
|
|
127
342
|
* where inference ends (dynamic `get`, post-operator members, unknown
|
|
128
343
|
* elements) — wide, never wrong. */
|
|
129
|
-
export interface UnknownExpr
|
|
344
|
+
export interface UnknownExpr
|
|
345
|
+
extends ExprBase<unknown>, AggregatableExpr, DateMethods, SeriesMethods, SpatialMethods {
|
|
130
346
|
eq(value: unknown): BoolExpr;
|
|
131
347
|
ne(value: unknown): BoolExpr;
|
|
132
348
|
lt(value: unknown): BoolExpr;
|
|
@@ -153,12 +369,12 @@ export interface UnknownExpr extends ExprBase<unknown>, AggregatableExpr {
|
|
|
153
369
|
concat(value: unknown): UnknownExpr;
|
|
154
370
|
substring(start: number, length?: number): UnknownExpr;
|
|
155
371
|
replace(pattern: string, replacement: string): UnknownExpr;
|
|
156
|
-
year(): NumberExpr;
|
|
157
|
-
month(): NumberExpr;
|
|
158
|
-
day(): NumberExpr;
|
|
159
|
-
epoch(): NumberExpr;
|
|
160
372
|
all(): UnknownExpr;
|
|
161
|
-
at(index: number): UnknownExpr;
|
|
373
|
+
at(index: number | NumberExpr): UnknownExpr;
|
|
374
|
+
/** Do two half-open `{start, end}` intervals share an instant? */
|
|
375
|
+
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
376
|
+
/** Cosine similarity (§8.15) — wide, on the honest top. */
|
|
377
|
+
similarity(other: unknown): NumberExpr;
|
|
162
378
|
}
|
|
163
379
|
|
|
164
380
|
/** Value type → expression type. Order matters: the DateTime brand is
|
|
@@ -199,17 +415,90 @@ export interface OrderOptions {
|
|
|
199
415
|
collation?: string;
|
|
200
416
|
}
|
|
201
417
|
|
|
202
|
-
/**
|
|
418
|
+
/** One row of a provider's relation table (MODEL-FORMAT §10.1, as a
|
|
419
|
+
* store spells it): the declared relation as plain data a hop lowers
|
|
420
|
+
* from. A foreign-key relation names its key column (`via`), the entity
|
|
421
|
+
* holding it (`fkEntity`), the entity it references (`fkTargets`) and the
|
|
422
|
+
* key it references there (`targetKey`); `kind` says which side holds the
|
|
423
|
+
* key. A many-to-many names its `joinTable` and is refused (`JL0105`). */
|
|
424
|
+
export interface RelationEntry {
|
|
425
|
+
readonly to: string;
|
|
426
|
+
readonly kind: 'oneToOne' | 'oneToMany' | 'manyToMany';
|
|
427
|
+
readonly via?: string;
|
|
428
|
+
readonly fkEntity?: string;
|
|
429
|
+
readonly fkTargets?: string;
|
|
430
|
+
readonly joinTable?: string;
|
|
431
|
+
readonly targetKey: string;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** A provider's relation table: one entry per relation member of the
|
|
435
|
+
* rows it serves. */
|
|
436
|
+
export type RelationTable = Readonly<Record<string, RelationEntry>>;
|
|
437
|
+
|
|
438
|
+
/** One relation hop a callback navigated (QUERY-PEN §4, relation
|
|
439
|
+
* navigation): the member read, the relation's kind, and the binding
|
|
440
|
+
* the lowered correlated phrase ranges over (`r1`, `r2`, …). */
|
|
441
|
+
export interface Hop {
|
|
442
|
+
member: string;
|
|
443
|
+
kind: 'oneToOne' | 'oneToMany';
|
|
444
|
+
binding: string;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/** The provider contract (D2, QUERY-PEN §8): any object exposing
|
|
203
448
|
* `execute(document, options)`. The document arrives whole; the return
|
|
204
|
-
* value uses the engine's result mapping.
|
|
205
|
-
|
|
449
|
+
* value uses the engine's result mapping. `T` is the item type — read
|
|
450
|
+
* from the `__item` phantom a typed provider carries (a typed entity
|
|
451
|
+
* set), stated by the caller (`from<T>(provider)`), or `unknown`. */
|
|
452
|
+
export interface Provider<T = unknown> {
|
|
453
|
+
/** The item phantom: never present at runtime; what `from` infers `T` from. */
|
|
454
|
+
readonly __item?: T;
|
|
206
455
|
execute(document: unknown, options: { externals: Record<string, unknown> }): unknown;
|
|
456
|
+
/** The root expression the items are bound through (`'$.Post[*]'`);
|
|
457
|
+
* absent means the whole input, `'$[*]'`. */
|
|
458
|
+
readonly root?: string;
|
|
459
|
+
/** The entity roots a store-level provider serves when it has no root
|
|
460
|
+
* of its own — `from()` refuses it (`JL0007`) naming them. */
|
|
461
|
+
readonly roots?: readonly string[];
|
|
462
|
+
/** An identity two providers share when their documents may be joined
|
|
463
|
+
* (one store's entity sets). When it carries `relations` — the tables
|
|
464
|
+
* of every root of the scope, keyed by root name — a hop continues
|
|
465
|
+
* into another root (`p.author.posts`). */
|
|
466
|
+
readonly scope?: unknown;
|
|
467
|
+
/** The relation table of the rows this provider serves: a relation
|
|
468
|
+
* member on the chain then hops (§3), lowered to a correlated phrase. */
|
|
469
|
+
readonly relations?: RelationTable;
|
|
207
470
|
}
|
|
208
471
|
|
|
472
|
+
/** The asynchronous provider (D8, §12): the same members, and `execute`
|
|
473
|
+
* may answer a promise — `fromAsync(provider)` awaits it, and the whole
|
|
474
|
+
* chain up to a `mapAsync` arrives as ONE document. */
|
|
475
|
+
export interface AsyncProvider<T = unknown> {
|
|
476
|
+
readonly __item?: T;
|
|
477
|
+
execute(document: unknown, options: { externals: Record<string, unknown> }): unknown | Promise<unknown>;
|
|
478
|
+
readonly root?: string;
|
|
479
|
+
readonly roots?: readonly string[];
|
|
480
|
+
readonly scope?: unknown;
|
|
481
|
+
readonly relations?: RelationTable;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** The engine's compile registries, under the engine's own option names
|
|
485
|
+
* (QUERY-PEN §8.1): what makes an expressible document executable in
|
|
486
|
+
* memory. Their shapes are the query engine's — wide here, never wrong. */
|
|
209
487
|
export interface LinqOptions {
|
|
210
488
|
/** Enables `ofType`/`cast` (schema operators); e.g.
|
|
211
489
|
* `createTypeTestCompiler()` from `@jarenjs/validate/query`. */
|
|
212
490
|
compileTypeTest?: (schema: unknown, docPath: string) => (value: unknown) => boolean;
|
|
491
|
+
/** Registered `$call` functions, for a hand-written or saved document. */
|
|
492
|
+
functions?: Readonly<Record<string, (...args: any[]) => unknown>>;
|
|
493
|
+
/** Named collations: `orderBy(…, { collation })` is `JQ0010` without one. */
|
|
494
|
+
collations?: Readonly<Record<string, (a: string, b: string) => number>>;
|
|
495
|
+
/** Custom RFC 9535 path function extensions. */
|
|
496
|
+
pathFunctions?: Readonly<Record<string, unknown>>;
|
|
497
|
+
/** Step, depth and sequence bounds — a SAVED document's guard. */
|
|
498
|
+
limits?: Readonly<Record<string, number>>;
|
|
499
|
+
/** An explicit cache-partition key, when the hooks above are rebuilt
|
|
500
|
+
* per call: compiled documents are shared per registry combination. */
|
|
501
|
+
registry?: object;
|
|
213
502
|
}
|
|
214
503
|
|
|
215
504
|
export interface Explanation {
|
|
@@ -221,6 +510,13 @@ export interface Explanation {
|
|
|
221
510
|
readonly functions: readonly string[];
|
|
222
511
|
readonly collations: readonly string[];
|
|
223
512
|
};
|
|
513
|
+
/** The relation hops the chain's callbacks navigated, in capture
|
|
514
|
+
* order; a join's inner sequence's hops precede the join's own. */
|
|
515
|
+
hops: Hop[];
|
|
516
|
+
/** The values `params()` bound, by name — what a provider receives as
|
|
517
|
+
* `externals` when the document runs, so a host handed the chain (a
|
|
518
|
+
* live registration) can carry them without a second spelling. */
|
|
519
|
+
bindings: Record<string, unknown>;
|
|
224
520
|
}
|
|
225
521
|
|
|
226
522
|
/** The deferred, immutable sequence of `T` with declared params `P`. */
|
|
@@ -246,6 +542,10 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
246
542
|
result: (outer: Expr<T>, inner: Expr<U>, p: ParamsExpr<P>) => R,
|
|
247
543
|
): Sequence<Unwrap<R>, P>;
|
|
248
544
|
|
|
545
|
+
/** The matching inner group is bound as an ARRAY value: index it
|
|
546
|
+
* (`g.at(0)`), fan it (`g.all()`), place it in a member (`{ all: g }`),
|
|
547
|
+
* and aggregate over its MEMBERS (`g.count()` is the number of
|
|
548
|
+
* matches, `g.exists()` whether there are any). */
|
|
249
549
|
groupJoin<U, R extends ExprResult>(
|
|
250
550
|
inner: Sequence<U, any>,
|
|
251
551
|
outerKey: (it: Expr<T>, p: ParamsExpr<P>) => ExprResult,
|
|
@@ -263,13 +563,21 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
263
563
|
concat(other: Sequence<T, any> | readonly T[]): Sequence<T, P>;
|
|
264
564
|
defaultIfEmpty(fallback?: T | null): Sequence<T | null, P>;
|
|
265
565
|
|
|
266
|
-
/** Keep items the schema accepts.
|
|
267
|
-
*
|
|
566
|
+
/** Keep items the schema accepts. A schema-pen builder carries its
|
|
567
|
+
* own shape (`Infer<>`); for a hand-written document `S` is
|
|
568
|
+
* caller-asserted (a JSON Schema is not a TypeScript type) and the
|
|
569
|
+
* default is honest `unknown`. */
|
|
570
|
+
ofType<S>(schema: SchemaBuilder<S, any, any>): Sequence<S, P>;
|
|
268
571
|
ofType<S = unknown>(schema: object): Sequence<S, P>;
|
|
572
|
+
cast<S>(schema: SchemaBuilder<S, any, any>): Sequence<S, P>;
|
|
269
573
|
cast<S = unknown>(schema: object): Sequence<S, P>;
|
|
270
574
|
|
|
271
|
-
/** Recorded unsupported:
|
|
272
|
-
|
|
575
|
+
/** Recorded unsupported (§4, §16): the grammar has no positional
|
|
576
|
+
* co-iteration. The parameter is `never`, not just the return type:
|
|
577
|
+
* a rest parameter accepts zero arguments, so `q.zip()` — the one
|
|
578
|
+
* spelling a caller would actually write — type-checked and failed at
|
|
579
|
+
* run time instead. JavaScript callers still get `JL0006`. */
|
|
580
|
+
zip(unsupported: never): never;
|
|
273
581
|
|
|
274
582
|
params<Q extends Record<string, unknown>>(bindings: Q): Sequence<T, P & Q>;
|
|
275
583
|
|
|
@@ -302,7 +610,7 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
302
610
|
any(predicate?: (it: Expr<T>, p: ParamsExpr<P>) => BoolExpr | boolean): boolean;
|
|
303
611
|
all(predicate: (it: Expr<T>, p: ParamsExpr<P>) => BoolExpr | boolean): boolean;
|
|
304
612
|
|
|
305
|
-
/** Cross into the async surface (
|
|
613
|
+
/** Cross into the async surface (QUERY-PEN.md §11): the sync chain
|
|
306
614
|
* becomes the pushed prefix; the element re-types to the callback's
|
|
307
615
|
* RESOLVED type. */
|
|
308
616
|
mapAsync<R>(fn: (item: T, signal: AbortSignal) => R, options: MapAsyncOptions):
|
|
@@ -310,7 +618,9 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
310
618
|
}
|
|
311
619
|
|
|
312
620
|
export function from<T>(source: Iterable<T>, options?: LinqOptions): Sequence<T, {}>;
|
|
313
|
-
|
|
621
|
+
/** A provider: `T` from its `__item` phantom (a typed entity set infers
|
|
622
|
+
* without a cast), or as the caller states it, or `unknown`. */
|
|
623
|
+
export function from<T = unknown>(source: Provider<T>, options?: LinqOptions): Sequence<T, {}>;
|
|
314
624
|
|
|
315
625
|
export function fromDocument<T = unknown>(
|
|
316
626
|
source: Iterable<unknown> | Provider,
|
|
@@ -318,7 +628,7 @@ export function fromDocument<T = unknown>(
|
|
|
318
628
|
options?: LinqOptions,
|
|
319
629
|
): Sequence<T, {}>;
|
|
320
630
|
|
|
321
|
-
/** The runtime code table, synced to
|
|
631
|
+
/** The runtime code table, synced to QUERY-PEN.md §9 by a test. */
|
|
322
632
|
export const LINQ_CODES: Readonly<Record<string, string>>;
|
|
323
633
|
|
|
324
634
|
export class LinqBuildError extends Error {
|
|
@@ -333,7 +643,7 @@ export class LinqRuntimeError extends Error {
|
|
|
333
643
|
readonly docPath: string | undefined;
|
|
334
644
|
}
|
|
335
645
|
|
|
336
|
-
// ————— The asynchronous surface (
|
|
646
|
+
// ————— The asynchronous surface (QUERY-PEN.md §§10–12) —————
|
|
337
647
|
|
|
338
648
|
export interface MapAsyncOptions {
|
|
339
649
|
/** REQUIRED: the in-flight bound (a positive integer, `JL0005`
|
|
@@ -347,6 +657,10 @@ export interface MapAsyncOptions {
|
|
|
347
657
|
|
|
348
658
|
export interface AsyncExplanation {
|
|
349
659
|
barriers: { operator: string, reason: string }[];
|
|
660
|
+
/** The relation hops the chain's callbacks navigated (as `Explanation`). */
|
|
661
|
+
hops: Hop[];
|
|
662
|
+
/** The values `params()` bound, by name (as `Explanation`). */
|
|
663
|
+
bindings: Record<string, unknown>;
|
|
350
664
|
/** Present when the chain is document-representable (no mapAsync). */
|
|
351
665
|
document?: unknown;
|
|
352
666
|
/** Present when a mapAsync splits the chain. */
|
|
@@ -374,6 +688,24 @@ export class AsyncSequence<T = unknown, P = {}> {
|
|
|
374
688
|
groupBy<R extends ExprResult>(key: (it: Expr<T>, p: ParamsExpr<P>) => R):
|
|
375
689
|
AsyncSequence<{ key: Unwrap<R> | null, items: T[] }, P>;
|
|
376
690
|
aggregate<A>(seed: A, step: (acc: Expr<A>, it: Expr<T>, p: ParamsExpr<P>) => ExprResult): AsyncSequence<A, P>;
|
|
691
|
+
|
|
692
|
+
/** Equi-join, pushed WHOLE: only over a provider origin, before any
|
|
693
|
+
* `mapAsync`, with an inner async sequence over the same provider or
|
|
694
|
+
* one sharing its scope (`JL0005` otherwise — a single-pass source
|
|
695
|
+
* cannot be joined; QUERY-PEN §10). */
|
|
696
|
+
join<U, R extends ExprResult>(
|
|
697
|
+
inner: AsyncSequence<U, any>,
|
|
698
|
+
outerKey: (it: Expr<T>, p: ParamsExpr<P>) => ExprResult,
|
|
699
|
+
innerKey: (it: Expr<U>, p: ParamsExpr<P>) => ExprResult,
|
|
700
|
+
result: (outer: Expr<T>, inner: Expr<U>, p: ParamsExpr<P>) => R,
|
|
701
|
+
): AsyncSequence<Unwrap<R>, P>;
|
|
702
|
+
/** Group-join, pushed WHOLE under the same rule as `join`. */
|
|
703
|
+
groupJoin<U, R extends ExprResult>(
|
|
704
|
+
inner: AsyncSequence<U, any>,
|
|
705
|
+
outerKey: (it: Expr<T>, p: ParamsExpr<P>) => ExprResult,
|
|
706
|
+
innerKey: (it: Expr<U>, p: ParamsExpr<P>) => ExprResult,
|
|
707
|
+
result: (outer: Expr<T>, group: ArrayExpr<U> & AggregatableExpr, p: ParamsExpr<P>) => R,
|
|
708
|
+
): AsyncSequence<Unwrap<R>, P>;
|
|
377
709
|
skip(count: number): AsyncSequence<T, P>;
|
|
378
710
|
take(count: number): AsyncSequence<T, P>;
|
|
379
711
|
distinct(): AsyncSequence<T, P>;
|
|
@@ -381,9 +713,12 @@ export class AsyncSequence<T = unknown, P = {}> {
|
|
|
381
713
|
/** Only a CONSTANT array can join an async stream (§10). */
|
|
382
714
|
concat(other: readonly T[]): AsyncSequence<T, P>;
|
|
383
715
|
defaultIfEmpty(fallback?: T | null): AsyncSequence<T | null, P>;
|
|
716
|
+
ofType<S>(schema: SchemaBuilder<S, any, any>): AsyncSequence<S, P>;
|
|
384
717
|
ofType<S = unknown>(schema: object): AsyncSequence<S, P>;
|
|
718
|
+
cast<S>(schema: SchemaBuilder<S, any, any>): AsyncSequence<S, P>;
|
|
385
719
|
cast<S = unknown>(schema: object): AsyncSequence<S, P>;
|
|
386
|
-
|
|
720
|
+
/** Recorded unsupported, as on the synchronous surface. */
|
|
721
|
+
zip(unsupported: never): never;
|
|
387
722
|
params<Q extends Record<string, unknown>>(bindings: Q): AsyncSequence<T, P & Q>;
|
|
388
723
|
|
|
389
724
|
/** The bounded-concurrency boundary (§11): re-types the element to
|
|
@@ -417,6 +752,19 @@ export class AsyncSequence<T = unknown, P = {}> {
|
|
|
417
752
|
all(predicate: (it: Expr<T>, p: ParamsExpr<P>) => BoolExpr | boolean): Promise<boolean>;
|
|
418
753
|
}
|
|
419
754
|
|
|
755
|
+
/** Build an async sequence over a provider (an `execute` duck, asked for
|
|
756
|
+
* before the iterable shapes): the document arrives whole and `execute`
|
|
757
|
+
* may answer a promise; `T` from the `__item` phantom, the caller, or
|
|
758
|
+
* `unknown`. */
|
|
759
|
+
export function fromAsync<T = unknown>(
|
|
760
|
+
source: AsyncProvider<T>,
|
|
761
|
+
options?: LinqOptions,
|
|
762
|
+
): AsyncSequence<T, {}>;
|
|
763
|
+
/** Build an async sequence over an async iterable, a sync iterable, a
|
|
764
|
+
* cursor or a push queue. A STRING is refused (`JL0001`): on this
|
|
765
|
+
* surface a string is a chunk source — feed it through a push queue —
|
|
766
|
+
* never a character stream, which is where the twins deliberately
|
|
767
|
+
* differ from `from('abc')`. */
|
|
420
768
|
export function fromAsync<T>(
|
|
421
769
|
source: AsyncIterable<T> | Iterable<T> | AsyncCursor<T>,
|
|
422
770
|
options?: LinqOptions,
|