@jarenjs/linq 0.49.2 → 0.66.1
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 +227 -0
- package/README.md +650 -17
- package/docs/APP-PEN.md +1143 -0
- package/docs/CONTRACT-PEN.md +1221 -0
- package/docs/DB-CLIENT.md +882 -0
- package/docs/FLOW-PEN.md +1033 -0
- package/docs/FORMS-PEN.md +940 -0
- package/docs/JSLT-PEN.md +955 -0
- package/docs/LINQ-FORMAT.md +778 -383
- package/docs/MIGRATION-PEN.md +781 -0
- package/docs/MODEL-PEN.md +1092 -0
- package/docs/QUERY-PEN.md +1724 -0
- package/docs/SCHEMA-PEN.md +1218 -0
- package/package.json +57 -4
- package/src/app/action.js +251 -0
- package/src/app/capture.js +63 -0
- package/src/app/define.js +255 -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 +377 -75
- package/src/capture-root.js +82 -0
- package/src/concurrency.js +48 -11
- package/src/contract/define.js +282 -0
- package/src/contract/http.js +247 -0
- package/src/contract/index.js +23 -0
- package/src/contract/operation.js +338 -0
- package/src/db/handle.js +89 -0
- package/src/db/include.js +351 -0
- package/src/db/index.js +24 -0
- package/src/db/ledger.js +195 -0
- package/src/db/live.js +43 -0
- package/src/db/membership.js +37 -0
- package/src/db/open.js +130 -0
- package/src/document.js +143 -13
- package/src/effect.js +65 -0
- package/src/errors.js +78 -6
- package/src/expression.js +463 -36
- package/src/federate.js +531 -0
- package/src/flow/capture.js +33 -0
- package/src/flow/dag.js +316 -0
- package/src/flow/fsm.js +323 -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 +5 -2
- package/src/jslt/body.js +226 -0
- package/src/jslt/index.js +18 -0
- package/src/jslt/rules.js +202 -0
- package/src/json-boundary.js +90 -0
- package/src/migration/define.js +318 -0
- package/src/migration/index.js +15 -0
- package/src/migration/steps.js +244 -0
- package/src/model/collection.js +273 -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 +468 -0
- package/types/db.d.ts +359 -0
- package/types/flow.d.ts +285 -0
- package/types/forms.d.ts +253 -0
- package/types/index.d.ts +296 -26
- package/types/jslt.d.ts +193 -0
- package/types/migration.d.ts +201 -0
- package/types/model.d.ts +526 -0
- package/types/schema.d.ts +494 -0
package/types/index.d.ts
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
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. */
|
|
@@ -65,7 +67,7 @@ export interface NumberExpr extends ExprBase<number>, EqExpr<number> {
|
|
|
65
67
|
context?: CalendarContext): NumberExpr;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
export interface StringExpr extends ExprBase<string>, EqExpr<string
|
|
70
|
+
export interface StringExpr extends ExprBase<string>, EqExpr<string>, SpatialMethods {
|
|
69
71
|
lt(value: string | StringExpr): BoolExpr;
|
|
70
72
|
le(value: string | StringExpr): BoolExpr;
|
|
71
73
|
gt(value: string | StringExpr): BoolExpr;
|
|
@@ -236,22 +238,82 @@ export interface SeriesMethods {
|
|
|
236
238
|
Expr<AsOfMatch[]> & AggregatableExpr;
|
|
237
239
|
}
|
|
238
240
|
|
|
239
|
-
|
|
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 {
|
|
240
282
|
eq(value: readonly E[] | ArrayExpr<E> | null): BoolExpr;
|
|
241
283
|
ne(value: readonly E[] | ArrayExpr<E> | null): BoolExpr;
|
|
242
284
|
/** Do two half-open `{start, end}` intervals share an instant?
|
|
243
285
|
* Touching spans do not. */
|
|
244
286
|
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
245
287
|
/** Fan the elements out (`[*]`) — a MANY-cardinality expression the
|
|
246
|
-
* aggregates
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
*
|
|
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. */
|
|
252
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;
|
|
253
308
|
}
|
|
254
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
|
+
|
|
255
317
|
/** Aggregates available on any expression (a fanned path, a group). */
|
|
256
318
|
export interface AggregatableExpr {
|
|
257
319
|
count(): NumberExpr;
|
|
@@ -262,16 +324,25 @@ export interface AggregatableExpr {
|
|
|
262
324
|
}
|
|
263
325
|
|
|
264
326
|
/** An object's expression: exactly its properties, recursively typed —
|
|
265
|
-
* which is what makes a misspelled member a compile error.
|
|
266
|
-
|
|
267
|
-
|
|
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]>;
|
|
268
335
|
};
|
|
269
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
|
+
|
|
270
341
|
/** The honest top: everything is available, nothing is precise. Used
|
|
271
342
|
* where inference ends (dynamic `get`, post-operator members, unknown
|
|
272
343
|
* elements) — wide, never wrong. */
|
|
273
344
|
export interface UnknownExpr
|
|
274
|
-
extends ExprBase<unknown>, AggregatableExpr, DateMethods, SeriesMethods {
|
|
345
|
+
extends ExprBase<unknown>, AggregatableExpr, DateMethods, SeriesMethods, SpatialMethods {
|
|
275
346
|
eq(value: unknown): BoolExpr;
|
|
276
347
|
ne(value: unknown): BoolExpr;
|
|
277
348
|
lt(value: unknown): BoolExpr;
|
|
@@ -299,9 +370,11 @@ export interface UnknownExpr
|
|
|
299
370
|
substring(start: number, length?: number): UnknownExpr;
|
|
300
371
|
replace(pattern: string, replacement: string): UnknownExpr;
|
|
301
372
|
all(): UnknownExpr;
|
|
302
|
-
at(index: number): UnknownExpr;
|
|
373
|
+
at(index: number | NumberExpr): UnknownExpr;
|
|
303
374
|
/** Do two half-open `{start, end}` intervals share an instant? */
|
|
304
375
|
overlaps(other: Interval | ExprBase<unknown>): BoolExpr;
|
|
376
|
+
/** Cosine similarity (§8.15) — wide, on the honest top. */
|
|
377
|
+
similarity(other: unknown): NumberExpr;
|
|
305
378
|
}
|
|
306
379
|
|
|
307
380
|
/** Value type → expression type. Order matters: the DateTime brand is
|
|
@@ -342,17 +415,97 @@ export interface OrderOptions {
|
|
|
342
415
|
collation?: string;
|
|
343
416
|
}
|
|
344
417
|
|
|
345
|
-
/**
|
|
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
|
|
346
448
|
* `execute(document, options)`. The document arrives whole; the return
|
|
347
|
-
* value uses the engine's result mapping.
|
|
348
|
-
|
|
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;
|
|
349
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;
|
|
470
|
+
}
|
|
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
|
+
/** The cursor protocol, optional: the same document as an item
|
|
479
|
+
* cursor, one item per pull, `return()` releasing whatever it holds.
|
|
480
|
+
* A `for await` over the chain hands its pushed document here when
|
|
481
|
+
* the provider offers it, so iteration never materialises what the
|
|
482
|
+
* provider can stream; a provider without it is run whole. */
|
|
483
|
+
cursor?(document: unknown, options: { externals: Record<string, unknown> }):
|
|
484
|
+
AsyncIterator<unknown> & { return(): Promise<unknown> };
|
|
485
|
+
readonly root?: string;
|
|
486
|
+
readonly roots?: readonly string[];
|
|
487
|
+
readonly scope?: unknown;
|
|
488
|
+
readonly relations?: RelationTable;
|
|
350
489
|
}
|
|
351
490
|
|
|
491
|
+
/** The engine's compile registries, under the engine's own option names
|
|
492
|
+
* (QUERY-PEN §8.1): what makes an expressible document executable in
|
|
493
|
+
* memory. Their shapes are the query engine's — wide here, never wrong. */
|
|
352
494
|
export interface LinqOptions {
|
|
353
495
|
/** Enables `ofType`/`cast` (schema operators); e.g.
|
|
354
496
|
* `createTypeTestCompiler()` from `@jarenjs/validate/query`. */
|
|
355
497
|
compileTypeTest?: (schema: unknown, docPath: string) => (value: unknown) => boolean;
|
|
498
|
+
/** Registered `$call` functions, for a hand-written or saved document. */
|
|
499
|
+
functions?: Readonly<Record<string, (...args: any[]) => unknown>>;
|
|
500
|
+
/** Named collations: `orderBy(…, { collation })` is `JQ0010` without one. */
|
|
501
|
+
collations?: Readonly<Record<string, (a: string, b: string) => number>>;
|
|
502
|
+
/** Custom RFC 9535 path function extensions. */
|
|
503
|
+
pathFunctions?: Readonly<Record<string, unknown>>;
|
|
504
|
+
/** Step, depth and sequence bounds — a SAVED document's guard. */
|
|
505
|
+
limits?: Readonly<Record<string, number>>;
|
|
506
|
+
/** An explicit cache-partition key, when the hooks above are rebuilt
|
|
507
|
+
* per call: compiled documents are shared per registry combination. */
|
|
508
|
+
registry?: object;
|
|
356
509
|
}
|
|
357
510
|
|
|
358
511
|
export interface Explanation {
|
|
@@ -364,6 +517,13 @@ export interface Explanation {
|
|
|
364
517
|
readonly functions: readonly string[];
|
|
365
518
|
readonly collations: readonly string[];
|
|
366
519
|
};
|
|
520
|
+
/** The relation hops the chain's callbacks navigated, in capture
|
|
521
|
+
* order; a join's inner sequence's hops precede the join's own. */
|
|
522
|
+
hops: Hop[];
|
|
523
|
+
/** The values `params()` bound, by name — what a provider receives as
|
|
524
|
+
* `externals` when the document runs, so a host handed the chain (a
|
|
525
|
+
* live registration) can carry them without a second spelling. */
|
|
526
|
+
bindings: Record<string, unknown>;
|
|
367
527
|
}
|
|
368
528
|
|
|
369
529
|
/** The deferred, immutable sequence of `T` with declared params `P`. */
|
|
@@ -389,6 +549,10 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
389
549
|
result: (outer: Expr<T>, inner: Expr<U>, p: ParamsExpr<P>) => R,
|
|
390
550
|
): Sequence<Unwrap<R>, P>;
|
|
391
551
|
|
|
552
|
+
/** The matching inner group is bound as an ARRAY value: index it
|
|
553
|
+
* (`g.at(0)`), fan it (`g.all()`), place it in a member (`{ all: g }`),
|
|
554
|
+
* and aggregate over its MEMBERS (`g.count()` is the number of
|
|
555
|
+
* matches, `g.exists()` whether there are any). */
|
|
392
556
|
groupJoin<U, R extends ExprResult>(
|
|
393
557
|
inner: Sequence<U, any>,
|
|
394
558
|
outerKey: (it: Expr<T>, p: ParamsExpr<P>) => ExprResult,
|
|
@@ -406,13 +570,21 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
406
570
|
concat(other: Sequence<T, any> | readonly T[]): Sequence<T, P>;
|
|
407
571
|
defaultIfEmpty(fallback?: T | null): Sequence<T | null, P>;
|
|
408
572
|
|
|
409
|
-
/** Keep items the schema accepts.
|
|
410
|
-
*
|
|
573
|
+
/** Keep items the schema accepts. A schema-pen builder carries its
|
|
574
|
+
* own shape (`Infer<>`); for a hand-written document `S` is
|
|
575
|
+
* caller-asserted (a JSON Schema is not a TypeScript type) and the
|
|
576
|
+
* default is honest `unknown`. */
|
|
577
|
+
ofType<S>(schema: SchemaBuilder<S, any, any>): Sequence<S, P>;
|
|
411
578
|
ofType<S = unknown>(schema: object): Sequence<S, P>;
|
|
579
|
+
cast<S>(schema: SchemaBuilder<S, any, any>): Sequence<S, P>;
|
|
412
580
|
cast<S = unknown>(schema: object): Sequence<S, P>;
|
|
413
581
|
|
|
414
|
-
/** Recorded unsupported:
|
|
415
|
-
|
|
582
|
+
/** Recorded unsupported (§4, §16): the grammar has no positional
|
|
583
|
+
* co-iteration. The parameter is `never`, not just the return type:
|
|
584
|
+
* a rest parameter accepts zero arguments, so `q.zip()` — the one
|
|
585
|
+
* spelling a caller would actually write — type-checked and failed at
|
|
586
|
+
* run time instead. JavaScript callers still get `JL0006`. */
|
|
587
|
+
zip(unsupported: never): never;
|
|
416
588
|
|
|
417
589
|
params<Q extends Record<string, unknown>>(bindings: Q): Sequence<T, P & Q>;
|
|
418
590
|
|
|
@@ -445,7 +617,7 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
445
617
|
any(predicate?: (it: Expr<T>, p: ParamsExpr<P>) => BoolExpr | boolean): boolean;
|
|
446
618
|
all(predicate: (it: Expr<T>, p: ParamsExpr<P>) => BoolExpr | boolean): boolean;
|
|
447
619
|
|
|
448
|
-
/** Cross into the async surface (
|
|
620
|
+
/** Cross into the async surface (QUERY-PEN.md §11): the sync chain
|
|
449
621
|
* becomes the pushed prefix; the element re-types to the callback's
|
|
450
622
|
* RESOLVED type. */
|
|
451
623
|
mapAsync<R>(fn: (item: T, signal: AbortSignal) => R, options: MapAsyncOptions):
|
|
@@ -453,7 +625,9 @@ export class Sequence<T = unknown, P = {}> {
|
|
|
453
625
|
}
|
|
454
626
|
|
|
455
627
|
export function from<T>(source: Iterable<T>, options?: LinqOptions): Sequence<T, {}>;
|
|
456
|
-
|
|
628
|
+
/** A provider: `T` from its `__item` phantom (a typed entity set infers
|
|
629
|
+
* without a cast), or as the caller states it, or `unknown`. */
|
|
630
|
+
export function from<T = unknown>(source: Provider<T>, options?: LinqOptions): Sequence<T, {}>;
|
|
457
631
|
|
|
458
632
|
export function fromDocument<T = unknown>(
|
|
459
633
|
source: Iterable<unknown> | Provider,
|
|
@@ -461,7 +635,7 @@ export function fromDocument<T = unknown>(
|
|
|
461
635
|
options?: LinqOptions,
|
|
462
636
|
): Sequence<T, {}>;
|
|
463
637
|
|
|
464
|
-
/** The runtime code table, synced to
|
|
638
|
+
/** The runtime code table, synced to QUERY-PEN.md §9 by a test. */
|
|
465
639
|
export const LINQ_CODES: Readonly<Record<string, string>>;
|
|
466
640
|
|
|
467
641
|
export class LinqBuildError extends Error {
|
|
@@ -476,7 +650,7 @@ export class LinqRuntimeError extends Error {
|
|
|
476
650
|
readonly docPath: string | undefined;
|
|
477
651
|
}
|
|
478
652
|
|
|
479
|
-
// ————— The asynchronous surface (
|
|
653
|
+
// ————— The asynchronous surface (QUERY-PEN.md §§10–12) —————
|
|
480
654
|
|
|
481
655
|
export interface MapAsyncOptions {
|
|
482
656
|
/** REQUIRED: the in-flight bound (a positive integer, `JL0005`
|
|
@@ -490,6 +664,17 @@ export interface MapAsyncOptions {
|
|
|
490
664
|
|
|
491
665
|
export interface AsyncExplanation {
|
|
492
666
|
barriers: { operator: string, reason: string }[];
|
|
667
|
+
/** What THIS surface does with the item stream: one item at a time, or
|
|
668
|
+
* a buffer at its first local barrier. Over a provider the pushed
|
|
669
|
+
* document's own class — a set residual, an external the database
|
|
670
|
+
* cannot bind — is the provider's `explain(document, { externals:
|
|
671
|
+
* bindings })` to report, and its cursor carries the same answer. */
|
|
672
|
+
streaming: 'row' | 'buffered';
|
|
673
|
+
barrier: { construct: string, reason: string } | null;
|
|
674
|
+
/** The relation hops the chain's callbacks navigated (as `Explanation`). */
|
|
675
|
+
hops: Hop[];
|
|
676
|
+
/** The values `params()` bound, by name (as `Explanation`). */
|
|
677
|
+
bindings: Record<string, unknown>;
|
|
493
678
|
/** Present when the chain is document-representable (no mapAsync). */
|
|
494
679
|
document?: unknown;
|
|
495
680
|
/** Present when a mapAsync splits the chain. */
|
|
@@ -517,6 +702,24 @@ export class AsyncSequence<T = unknown, P = {}> {
|
|
|
517
702
|
groupBy<R extends ExprResult>(key: (it: Expr<T>, p: ParamsExpr<P>) => R):
|
|
518
703
|
AsyncSequence<{ key: Unwrap<R> | null, items: T[] }, P>;
|
|
519
704
|
aggregate<A>(seed: A, step: (acc: Expr<A>, it: Expr<T>, p: ParamsExpr<P>) => ExprResult): AsyncSequence<A, P>;
|
|
705
|
+
|
|
706
|
+
/** Equi-join, pushed WHOLE: only over a provider origin, before any
|
|
707
|
+
* `mapAsync`, with an inner async sequence over the same provider or
|
|
708
|
+
* one sharing its scope (`JL0005` otherwise — a single-pass source
|
|
709
|
+
* cannot be joined; QUERY-PEN §10). */
|
|
710
|
+
join<U, R extends ExprResult>(
|
|
711
|
+
inner: AsyncSequence<U, any>,
|
|
712
|
+
outerKey: (it: Expr<T>, p: ParamsExpr<P>) => ExprResult,
|
|
713
|
+
innerKey: (it: Expr<U>, p: ParamsExpr<P>) => ExprResult,
|
|
714
|
+
result: (outer: Expr<T>, inner: Expr<U>, p: ParamsExpr<P>) => R,
|
|
715
|
+
): AsyncSequence<Unwrap<R>, P>;
|
|
716
|
+
/** Group-join, pushed WHOLE under the same rule as `join`. */
|
|
717
|
+
groupJoin<U, R extends ExprResult>(
|
|
718
|
+
inner: AsyncSequence<U, any>,
|
|
719
|
+
outerKey: (it: Expr<T>, p: ParamsExpr<P>) => ExprResult,
|
|
720
|
+
innerKey: (it: Expr<U>, p: ParamsExpr<P>) => ExprResult,
|
|
721
|
+
result: (outer: Expr<T>, group: ArrayExpr<U> & AggregatableExpr, p: ParamsExpr<P>) => R,
|
|
722
|
+
): AsyncSequence<Unwrap<R>, P>;
|
|
520
723
|
skip(count: number): AsyncSequence<T, P>;
|
|
521
724
|
take(count: number): AsyncSequence<T, P>;
|
|
522
725
|
distinct(): AsyncSequence<T, P>;
|
|
@@ -524,9 +727,12 @@ export class AsyncSequence<T = unknown, P = {}> {
|
|
|
524
727
|
/** Only a CONSTANT array can join an async stream (§10). */
|
|
525
728
|
concat(other: readonly T[]): AsyncSequence<T, P>;
|
|
526
729
|
defaultIfEmpty(fallback?: T | null): AsyncSequence<T | null, P>;
|
|
730
|
+
ofType<S>(schema: SchemaBuilder<S, any, any>): AsyncSequence<S, P>;
|
|
527
731
|
ofType<S = unknown>(schema: object): AsyncSequence<S, P>;
|
|
732
|
+
cast<S>(schema: SchemaBuilder<S, any, any>): AsyncSequence<S, P>;
|
|
528
733
|
cast<S = unknown>(schema: object): AsyncSequence<S, P>;
|
|
529
|
-
|
|
734
|
+
/** Recorded unsupported, as on the synchronous surface. */
|
|
735
|
+
zip(unsupported: never): never;
|
|
530
736
|
params<Q extends Record<string, unknown>>(bindings: Q): AsyncSequence<T, P & Q>;
|
|
531
737
|
|
|
532
738
|
/** The bounded-concurrency boundary (§11): re-types the element to
|
|
@@ -560,11 +766,75 @@ export class AsyncSequence<T = unknown, P = {}> {
|
|
|
560
766
|
all(predicate: (it: Expr<T>, p: ParamsExpr<P>) => BoolExpr | boolean): Promise<boolean>;
|
|
561
767
|
}
|
|
562
768
|
|
|
769
|
+
/** Build an async sequence over a provider (an `execute` duck, asked for
|
|
770
|
+
* before the iterable shapes): the document arrives whole and `execute`
|
|
771
|
+
* may answer a promise; `T` from the `__item` phantom, the caller, or
|
|
772
|
+
* `unknown`. */
|
|
773
|
+
export function fromAsync<T = unknown>(
|
|
774
|
+
source: AsyncProvider<T>,
|
|
775
|
+
options?: LinqOptions,
|
|
776
|
+
): AsyncSequence<T, {}>;
|
|
777
|
+
/** Build an async sequence over an async iterable, a sync iterable, a
|
|
778
|
+
* cursor or a push queue. A STRING is refused (`JL0001`): on this
|
|
779
|
+
* surface a string is a chunk source — feed it through a push queue —
|
|
780
|
+
* never a character stream, which is where the twins deliberately
|
|
781
|
+
* differ from `from('abc')`. */
|
|
563
782
|
export function fromAsync<T>(
|
|
564
783
|
source: AsyncIterable<T> | Iterable<T> | AsyncCursor<T>,
|
|
565
784
|
options?: LinqOptions,
|
|
566
785
|
): AsyncSequence<T, {}>;
|
|
567
786
|
|
|
787
|
+
/** One side of a federation, as `explain()` reports it (§12.1). */
|
|
788
|
+
export interface FederatedSide {
|
|
789
|
+
readonly source: string;
|
|
790
|
+
readonly root: string;
|
|
791
|
+
readonly estimatedRows: number | null;
|
|
792
|
+
/** The join key on this side, as the document spells it. */
|
|
793
|
+
readonly key: string;
|
|
794
|
+
/** The document this side's own source is asked. */
|
|
795
|
+
readonly document: unknown;
|
|
796
|
+
/** Whether this side is pulled row by row, or answered whole. */
|
|
797
|
+
readonly streaming: 'row' | 'buffered';
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** What a federated document will do, without doing any of it (§12.1). */
|
|
801
|
+
export interface FederationPlan {
|
|
802
|
+
readonly strategy: 'hash';
|
|
803
|
+
readonly budget: { readonly maxRows: number; readonly maxBytes: number };
|
|
804
|
+
readonly build: FederatedSide;
|
|
805
|
+
readonly probe: FederatedSide;
|
|
806
|
+
/** The join itself is the engine's, over the two reduced sides. */
|
|
807
|
+
readonly resident: { readonly document: unknown };
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** One named source of a federation: an ordinary provider source whose
|
|
811
|
+
* root is `$.<name>[*]`, sharing one scope with its siblings — which is
|
|
812
|
+
* what admits the join the federation then executes. */
|
|
813
|
+
export interface FederatedSource<T = unknown> extends AsyncProvider<T> {
|
|
814
|
+
explain(document: unknown): FederationPlan;
|
|
815
|
+
readonly root: string;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/** The explicit cross-source boundary (§12.1).
|
|
819
|
+
*
|
|
820
|
+
* A query document reads one input, and an ordinary join across two
|
|
821
|
+
* unrelated sources stays `JL0005`. `federate()` is the one way to opt
|
|
822
|
+
* out of that, by naming the sources and the bounds together: each
|
|
823
|
+
* side's own filters and projection run at its source, the smaller side
|
|
824
|
+
* fills a bounded hash table, the other is probed against it, and the
|
|
825
|
+
* caller's own document decides over the two reduced sets. A side that
|
|
826
|
+
* reaches `maxRows` or `maxBytes` raises `JL2008` at the row that would
|
|
827
|
+
* have broken the bound. */
|
|
828
|
+
export function federate(spec: {
|
|
829
|
+
sources: Record<string, AsyncProvider | { provider: AsyncProvider; estimatedRows?: number }>;
|
|
830
|
+
maxRows: number;
|
|
831
|
+
maxBytes: number;
|
|
832
|
+
strategy?: 'hash';
|
|
833
|
+
}): {
|
|
834
|
+
source<T = unknown>(name: string): FederatedSource<T>;
|
|
835
|
+
readonly names: readonly string[];
|
|
836
|
+
};
|
|
837
|
+
|
|
568
838
|
/** The push→pull adapter for feed/end readers (§12). */
|
|
569
839
|
export function createPushQueue<T = unknown>(options?: { highWaterMark?: number }): {
|
|
570
840
|
feed(value: T): boolean;
|
package/types/jslt.d.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-authored declarations for `@jarenjs/linq/jslt` — the JSLT pen's
|
|
3
|
+
* type contract, kept to the same line as `index.d.ts`: the common path
|
|
4
|
+
* is precisely typed, the exotic path is honestly `unknown`, nothing is
|
|
5
|
+
* ever a WRONG type.
|
|
6
|
+
*
|
|
7
|
+
* A body's VALUE is typed by annotating the callback's first argument
|
|
8
|
+
* (`(v: Expr<Book>) => …`) or by the rule's `schema` match when it is a
|
|
9
|
+
* schema-pen builder; without either it is the honest top. A body's
|
|
10
|
+
* externals are `root` and `path` — always present, no declaration —
|
|
11
|
+
* plus the parameters the body declares by name (`{ externals: ['rate']
|
|
12
|
+
* }`): a declared name types as `UnknownExpr` until the second argument
|
|
13
|
+
* is annotated (`x: Externals<{ rate: number }>`), an undeclared name is
|
|
14
|
+
* a compile error, as with the chain's `params()`.
|
|
15
|
+
*
|
|
16
|
+
* The honest limits: a rule's OUTPUT is the unwrapped shape of what its
|
|
17
|
+
* body returns, with every `apply()` — a dispatch to OTHER rules —
|
|
18
|
+
* `unknown`; a stylesheet's `In`/`Out` are its FIRST rule's (write the
|
|
19
|
+
* root rule first, as Appendix A does), or what the author annotates
|
|
20
|
+
* (`stylesheet<In, Out>(…)`). The built-in rule's rebuilds (`share`/
|
|
21
|
+
* `fresh` around an unmatched container) are not typed at all: a
|
|
22
|
+
* stylesheet whose root is unmatched is `Stylesheet<unknown, unknown>`
|
|
23
|
+
* unless annotated. Every claim here has a runtime twin in
|
|
24
|
+
* `test/linq/jslt-pen.test.js` and a compile-level pin in
|
|
25
|
+
* `test/consumer/linq-jslt.ts`.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { ExprBase, MemberExpr, StringExpr, UnknownExpr, Unwrap } from './index.js';
|
|
29
|
+
import type { BuilderLike, Json, JsonSchema } from './schema.js';
|
|
30
|
+
|
|
31
|
+
/** A mode's built-in-rule disposition (JSLT-FORMAT §5). */
|
|
32
|
+
export type Disposition = 'share' | 'fresh' | 'error';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The externals a body may name: the two the engine binds on every
|
|
36
|
+
* dispatch (§8.2) and the parameters the body declared (§8.1).
|
|
37
|
+
*/
|
|
38
|
+
export type Externals<X = {}, Root = unknown> = {
|
|
39
|
+
/** The input document root (`$root`). */
|
|
40
|
+
readonly root: MemberExpr<Root>;
|
|
41
|
+
/** The matched value's normalized path (`$path`) — `null` for a location-less value. */
|
|
42
|
+
readonly path: StringExpr;
|
|
43
|
+
} & {
|
|
44
|
+
readonly [K in keyof X & string]-?: MemberExpr<X[K]>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Any expression-ish callback result the capture accepts. */
|
|
48
|
+
export type BodyResult = ExprBase<unknown> | object | string | number | boolean | null;
|
|
49
|
+
|
|
50
|
+
/** The value phantom of an expression type; the honest top otherwise. */
|
|
51
|
+
export type ValueOf<V> = V extends ExprBase<infer T> ? (unknown extends T ? unknown : T) : unknown;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A rule body's document — plain JSON (the `$expr` of a rule), carrying
|
|
55
|
+
* the value it was captured over and the shape it produces as
|
|
56
|
+
* phantoms. `body()` writes one; `rule()` reads both phantoms.
|
|
57
|
+
*/
|
|
58
|
+
export type BodyDocument<In = unknown, Out = unknown> = Json & {
|
|
59
|
+
/** Phantoms: declared, never present at runtime. */
|
|
60
|
+
readonly __in: In;
|
|
61
|
+
readonly __out: Out;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export interface BodyOptions<N extends string> {
|
|
65
|
+
/** The stylesheet parameters this body names (§8.1); `root` and `path` need none. */
|
|
66
|
+
readonly externals?: readonly N[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Capture one rule body over the matched value at `$`. Type the value
|
|
71
|
+
* by annotating `v` (`(v: Expr<Book>) => …`); declare parameters by
|
|
72
|
+
* name and type them by annotating `x` (`x: Externals<{ rate: number
|
|
73
|
+
* }>`) — an undeclared name on `x` does not compile.
|
|
74
|
+
*/
|
|
75
|
+
export function body<
|
|
76
|
+
V extends ExprBase<unknown> = UnknownExpr,
|
|
77
|
+
N extends string = never,
|
|
78
|
+
R extends BodyResult = BodyResult,
|
|
79
|
+
Root = unknown,
|
|
80
|
+
X extends Record<N, unknown> = Record<N, unknown>,
|
|
81
|
+
>(
|
|
82
|
+
fn: (value: V, x: Externals<X, Root>) => R,
|
|
83
|
+
options?: BodyOptions<N>,
|
|
84
|
+
): BodyDocument<ValueOf<V>, Unwrap<R>>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* `{ $apply: selector }` / `{ $apply: [selector, mode] }` — the
|
|
88
|
+
* apply-templates operator (§6), inside a `body()` callback. Its result
|
|
89
|
+
* is a dispatch to other rules, so it is the honest top: as an array
|
|
90
|
+
* element (`[apply(…)]`, the `[]` idiom) it unwraps to `unknown[]`; as a
|
|
91
|
+
* bare object member it is refused at build time (`JL0102`).
|
|
92
|
+
*/
|
|
93
|
+
export function apply(selector: ExprBase<unknown> | string | Json, mode?: string): UnknownExpr;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* `{ [name]: operands }` — a registered operator (§13), spelled without
|
|
97
|
+
* judging it; the engine's compiler decides. Works in any capture.
|
|
98
|
+
*/
|
|
99
|
+
export function op(name: `$${string}`, operands?: ExprBase<unknown> | Json | readonly (ExprBase<unknown> | Json)[]): UnknownExpr;
|
|
100
|
+
|
|
101
|
+
/** The `match` member (§3): a JSONPath string, `{ path?, schema? }`, or `null` for the unconditional rule. */
|
|
102
|
+
export type Match =
|
|
103
|
+
| string
|
|
104
|
+
| null
|
|
105
|
+
| undefined
|
|
106
|
+
| { readonly path?: string; readonly schema?: BuilderLike | JsonSchema | boolean };
|
|
107
|
+
|
|
108
|
+
/** The value a match types: a schema-pen builder's `Infer<>`; the honest top otherwise. */
|
|
109
|
+
export type MatchIn<M> = M extends { readonly schema: BuilderLike<infer O, any, any> } ? O : unknown;
|
|
110
|
+
|
|
111
|
+
export interface RuleOptions {
|
|
112
|
+
/** The rule's mode (§7); the unnamed mode `""` by default. */
|
|
113
|
+
readonly mode?: string;
|
|
114
|
+
/** Explicit conflict resolution (§4); the three defaults otherwise. */
|
|
115
|
+
readonly priority?: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The `match` member as emitted. */
|
|
119
|
+
export type MatchDocument =
|
|
120
|
+
| string
|
|
121
|
+
| { readonly path?: string; readonly schema?: JsonSchema | boolean };
|
|
122
|
+
|
|
123
|
+
/** A rule as a plain document — what `stylesheet()` takes, by pen or by hand. */
|
|
124
|
+
export interface RuleDocument {
|
|
125
|
+
readonly mode?: string;
|
|
126
|
+
readonly match?: MatchDocument;
|
|
127
|
+
readonly priority?: number;
|
|
128
|
+
readonly body: Json;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** One template rule (§2.2), carrying its body's phantoms. */
|
|
132
|
+
export interface Rule<In = unknown, Out = unknown> extends RuleDocument {
|
|
133
|
+
readonly __in: In;
|
|
134
|
+
readonly __out: Out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** A callback rule: the value is typed by annotating it (`(v: Expr<Book>) => …`)
|
|
138
|
+
* or by the match's schema builder; the honest top otherwise. */
|
|
139
|
+
export function rule<
|
|
140
|
+
M extends Match,
|
|
141
|
+
R extends BodyResult,
|
|
142
|
+
V extends ExprBase<unknown> = MemberExpr<MatchIn<M>>,
|
|
143
|
+
>(
|
|
144
|
+
match: M,
|
|
145
|
+
fn: (value: V, x: Externals<{}, unknown>) => R,
|
|
146
|
+
options?: RuleOptions,
|
|
147
|
+
): Rule<ValueOf<V>, Unwrap<R>>;
|
|
148
|
+
/** A `body()` document: its phantoms are the rule's (the match's schema types `In` when the body is untyped). */
|
|
149
|
+
export function rule<M extends Match, In, Out>(
|
|
150
|
+
match: M,
|
|
151
|
+
body: BodyDocument<In, Out>,
|
|
152
|
+
options?: RuleOptions,
|
|
153
|
+
): Rule<unknown extends In ? MatchIn<M> : In, Out>;
|
|
154
|
+
/** A query document verbatim: nothing is inferred. */
|
|
155
|
+
export function rule(match: Match, body: Json, options?: RuleOptions): Rule<unknown, unknown>;
|
|
156
|
+
|
|
157
|
+
export interface StylesheetOptions {
|
|
158
|
+
/** The default disposition of every mode (§2.1, §5). */
|
|
159
|
+
readonly unmatched?: Disposition;
|
|
160
|
+
/** Per-mode overrides. */
|
|
161
|
+
readonly modes?: { readonly [mode: string]: { readonly unmatched: Disposition } };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The envelope (§2.1), carrying the root rule's phantoms. */
|
|
165
|
+
export interface Stylesheet<In = unknown, Out = unknown> {
|
|
166
|
+
readonly __in: In;
|
|
167
|
+
readonly __out: Out;
|
|
168
|
+
readonly $jslt: '0.1';
|
|
169
|
+
readonly unmatched?: Disposition;
|
|
170
|
+
readonly modes?: { readonly [mode: string]: { readonly unmatched: Disposition } };
|
|
171
|
+
readonly rules: readonly RuleDocument[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** A rule's `In` phantom; a hand-written rule is `unknown`. */
|
|
175
|
+
export type RuleIn<R> = R extends Rule<infer I, any> ? I : unknown;
|
|
176
|
+
/** A rule's `Out` phantom; a hand-written rule is `unknown`. */
|
|
177
|
+
export type RuleOut<R> = R extends Rule<any, infer O> ? O : unknown;
|
|
178
|
+
|
|
179
|
+
/** The envelope over rule documents; `In`/`Out` are the FIRST rule's. */
|
|
180
|
+
export function stylesheet<const R extends readonly RuleDocument[]>(
|
|
181
|
+
rules: R,
|
|
182
|
+
options?: StylesheetOptions,
|
|
183
|
+
): Stylesheet<RuleIn<R[0]>, RuleOut<R[0]>>;
|
|
184
|
+
/** The envelope with the phantoms as the author states them. */
|
|
185
|
+
export function stylesheet<In, Out>(
|
|
186
|
+
rules: readonly RuleDocument[],
|
|
187
|
+
options?: StylesheetOptions,
|
|
188
|
+
): Stylesheet<In, Out>;
|
|
189
|
+
|
|
190
|
+
/** The input a stylesheet (or rule) was written over. */
|
|
191
|
+
export type Input<S> = S extends { readonly __in: infer I } ? I : unknown;
|
|
192
|
+
/** The shape a stylesheet (or rule) produces. */
|
|
193
|
+
export type Output<S> = S extends { readonly __out: infer O } ? O : unknown;
|