@jarenjs/linq 0.49.2 → 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.
Files changed (77) hide show
  1. package/ARCHITECTURE.md +217 -0
  2. package/README.md +559 -17
  3. package/docs/APP-PEN.md +1143 -0
  4. package/docs/CONTRACT-PEN.md +1217 -0
  5. package/docs/DB-CLIENT.md +814 -0
  6. package/docs/FLOW-PEN.md +1026 -0
  7. package/docs/FORMS-PEN.md +940 -0
  8. package/docs/JSLT-PEN.md +955 -0
  9. package/docs/LINQ-FORMAT.md +771 -383
  10. package/docs/MIGRATION-PEN.md +781 -0
  11. package/docs/MODEL-PEN.md +1083 -0
  12. package/docs/QUERY-PEN.md +1636 -0
  13. package/docs/SCHEMA-PEN.md +1218 -0
  14. package/package.json +57 -4
  15. package/src/app/action.js +255 -0
  16. package/src/app/capture.js +63 -0
  17. package/src/app/define.js +260 -0
  18. package/src/app/index.js +20 -0
  19. package/src/app/patch.js +277 -0
  20. package/src/app/sub.js +106 -0
  21. package/src/async.js +329 -75
  22. package/src/capture-root.js +82 -0
  23. package/src/concurrency.js +9 -4
  24. package/src/contract/define.js +269 -0
  25. package/src/contract/http.js +247 -0
  26. package/src/contract/index.js +23 -0
  27. package/src/contract/operation.js +342 -0
  28. package/src/db/handle.js +86 -0
  29. package/src/db/include.js +316 -0
  30. package/src/db/index.js +19 -0
  31. package/src/db/live.js +43 -0
  32. package/src/db/membership.js +37 -0
  33. package/src/db/open.js +82 -0
  34. package/src/document.js +143 -13
  35. package/src/effect.js +65 -0
  36. package/src/errors.js +69 -6
  37. package/src/expression.js +437 -36
  38. package/src/flow/capture.js +33 -0
  39. package/src/flow/dag.js +302 -0
  40. package/src/flow/fsm.js +328 -0
  41. package/src/flow/index.js +22 -0
  42. package/src/forms/index.js +43 -0
  43. package/src/forms/rules.js +170 -0
  44. package/src/forms/submit.js +177 -0
  45. package/src/index.js +4 -2
  46. package/src/jslt/body.js +226 -0
  47. package/src/jslt/index.js +18 -0
  48. package/src/jslt/rules.js +207 -0
  49. package/src/json-boundary.js +90 -0
  50. package/src/migration/define.js +323 -0
  51. package/src/migration/index.js +15 -0
  52. package/src/migration/steps.js +248 -0
  53. package/src/model/collection.js +171 -0
  54. package/src/model/define.js +125 -0
  55. package/src/model/entity.js +307 -0
  56. package/src/model/index.js +47 -0
  57. package/src/model/relation.js +85 -0
  58. package/src/provider.js +137 -20
  59. package/src/schema/brand.js +31 -0
  60. package/src/schema/builders.js +526 -0
  61. package/src/schema/check.js +29 -0
  62. package/src/schema/emit.js +394 -0
  63. package/src/schema/factories.js +239 -0
  64. package/src/schema/index.js +37 -0
  65. package/src/schema-of.js +24 -0
  66. package/src/sequence.js +233 -103
  67. package/src/sources.js +10 -3
  68. package/types/app.d.ts +293 -0
  69. package/types/contract.d.ts +371 -0
  70. package/types/db.d.ts +188 -0
  71. package/types/flow.d.ts +285 -0
  72. package/types/forms.d.ts +253 -0
  73. package/types/index.d.ts +231 -26
  74. package/types/jslt.d.ts +193 -0
  75. package/types/migration.d.ts +201 -0
  76. package/types/model.d.ts +493 -0
  77. package/types/schema.d.ts +494 -0
@@ -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;
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Hand-authored declarations for `@jarenjs/linq/migration` — the
3
+ * migration pen's type contract, on the line every pen keeps: the common
4
+ * path precisely typed, the exotic path honestly `unknown`, nothing ever
5
+ * a WRONG type.
6
+ *
7
+ * A transform is typed from the two model documents' phantoms. `from`
8
+ * gives the OLD shape of the named table (`InferMeta<typeof
9
+ * previous>[name]['doc']`), the value the callback is captured over;
10
+ * `to` gives the NEW shape, which the callback's result must SPELL — a
11
+ * member forgotten, mistyped or not in the new shape is a compile error,
12
+ * and the honest top (`UnknownExpr`, a `get()`) is admitted wherever a
13
+ * precise value is, because the runtime validator is the judge there.
14
+ * From a JSON snapshot the old shape is `unknown` and the value the
15
+ * honest top; the author annotates it (`(u: Expr<User>) => …`) from the
16
+ * declaration `jaren-db snapshot --types` writes, or keeps the previous
17
+ * model module beside the current one so the phantom is there. An
18
+ * assertion's row is typed by the members the two shapes SHARE (a
19
+ * precondition sees old rows, a postcondition new ones; what both agree
20
+ * on is what neither lies about), annotated when one shape is meant.
21
+ * Every claim here has a runtime twin in
22
+ * `test/linq/migration-pen.test.js` and a compile-level pin in
23
+ * `test/consumer/linq-migration.ts`.
24
+ */
25
+
26
+ import type { BoolExpr, DateTime, ExprBase, MemberExpr, UnknownExpr } from './index.js';
27
+ import type { Json } from './schema.js';
28
+ import type { ModelDocument, InferMeta } from './model.js';
29
+ import type { Externals, RuleDocument, RuleOut, ValueOf } from './jslt.js';
30
+
31
+ // ————— the document —————
32
+
33
+ export interface DdlStep { readonly kind: 'ddl'; readonly sql: string; readonly note?: string }
34
+ export interface SqlStep { readonly kind: 'sql'; readonly sql: string; readonly note?: string }
35
+ export interface JsltStep {
36
+ readonly kind: 'jslt';
37
+ readonly collection: string;
38
+ readonly stylesheet: readonly RuleDocument[];
39
+ /** A planner placeholder; the runner refuses it (`JD0021`). The pen never sets or clears it. */
40
+ readonly draft?: boolean;
41
+ readonly note?: string;
42
+ }
43
+ export interface QueryStep {
44
+ readonly kind: 'query';
45
+ readonly collection: string;
46
+ readonly assert: Json;
47
+ readonly expect?: 'empty' | 'ebv';
48
+ readonly note?: string;
49
+ }
50
+ export interface DeriveColumn {
51
+ readonly name: string;
52
+ readonly derive: 'geohash' | 'bbox' | 'vector';
53
+ readonly precision?: number;
54
+ readonly dims?: number;
55
+ readonly component?: 'w' | 's' | 'e' | 'n';
56
+ readonly segments: readonly object[];
57
+ }
58
+ export interface DeriveStep {
59
+ readonly kind: 'derive';
60
+ readonly collection: string;
61
+ readonly columns: readonly DeriveColumn[];
62
+ readonly note?: string;
63
+ }
64
+ export interface RebuildStep {
65
+ readonly kind: 'rebuild';
66
+ readonly table: string;
67
+ readonly create: readonly string[];
68
+ readonly copy: string;
69
+ readonly indexes: readonly string[];
70
+ readonly note?: string;
71
+ }
72
+ export type MigrationStep = DdlStep | SqlStep | JsltStep | QueryStep | DeriveStep | RebuildStep;
73
+
74
+ /** The `$migration` 0.1 document (MIGRATION-FORMAT §2). */
75
+ export interface MigrationDocument {
76
+ readonly $migration: '0.1';
77
+ readonly id: string;
78
+ readonly from: string;
79
+ readonly to: string;
80
+ readonly note?: string;
81
+ readonly steps: readonly MigrationStep[];
82
+ }
83
+
84
+ // ————— what a model document types —————
85
+
86
+ /** The entities and collections a model document declares; any string
87
+ * for a document the type cannot read (a JSON snapshot). */
88
+ export type DeclaredNames<M> = M extends ModelDocument<infer E, infer C>
89
+ ? (unknown extends E ? string : keyof E & string) | (unknown extends C ? string : keyof C & string)
90
+ : string;
91
+
92
+ /** The document shape of table `N` in a model — an entity's `doc`; the
93
+ * honest `unknown` for a collection, a JSON snapshot, or a name the
94
+ * model does not type. */
95
+ export type DocOf<M, N extends string> =
96
+ [InferMeta<M>] extends [never] ? unknown
97
+ : N extends keyof InferMeta<M>
98
+ ? (InferMeta<M>[N] extends { doc: infer D } ? D : unknown)
99
+ : unknown;
100
+
101
+ /** How a value of `T` is spelled in a transform's result: the value
102
+ * itself, an expression yielding it, the honest top, or — for an object
103
+ * — a literal spelling each member the same way; `unknown` admits
104
+ * anything. */
105
+ export type Spell<T> = SpellPresent<Exclude<T, undefined>> | (undefined extends T ? undefined : never);
106
+ type SpellPresent<T> =
107
+ unknown extends T ? unknown :
108
+ [T] extends [DateTime] ? string | ExprBase<string> | UnknownExpr :
109
+ [T] extends [readonly (infer E)[]] ? T | ExprBase<T> | UnknownExpr | readonly Spell<E>[] :
110
+ [T] extends [object] ? T | ExprBase<T> | UnknownExpr | { readonly [K in keyof T]: Spell<T[K]> } :
111
+ T | ExprBase<T> | UnknownExpr;
112
+
113
+ /** A rules array's verdict: a typed first rule must produce the new
114
+ * shape; a hand-written rule (`unknown` out) is the honest top. */
115
+ export type RulesFor<R extends readonly RuleDocument[], New> =
116
+ unknown extends RuleOut<R[0]> ? unknown : RuleOut<R[0]> extends New ? unknown : never;
117
+
118
+ /** Any stylesheet envelope: the JSLT pen's (phantoms and all) or a hand-written one. */
119
+ export type StylesheetLike = { readonly $jslt: '0.1'; readonly rules: readonly RuleDocument[] };
120
+
121
+ /** A stylesheet's verdict: a typed one must produce the new shape; an
122
+ * untyped or hand-written one is the honest top. */
123
+ export type SheetFor<S, New> = S extends { readonly __out: infer O }
124
+ ? (unknown extends O ? unknown : O extends New ? unknown : never)
125
+ : unknown;
126
+
127
+ export interface AssertOptions {
128
+ /** `'empty'` (the default, absent from the document): no row may
129
+ * satisfy the predicate — it names the violation; `'ebv'`: the matching
130
+ * rows are the witness. */
131
+ readonly expect?: 'empty' | 'ebv';
132
+ }
133
+
134
+ // ————— the builder —————
135
+
136
+ /** The migration under construction: immutable, every method a new
137
+ * builder; `.document`/`toJSON()` the deep-frozen document. */
138
+ export class Migration<From = unknown, To = unknown> {
139
+ private constructor();
140
+ /** One rendered DDL statement (`ddl`). */
141
+ ddl(sql: string, note?: string): Migration<From, To>;
142
+ /** One data statement spelled directly (`sql`, MIGRATION-FORMAT §9.4). */
143
+ sql(sql: string, note?: string): Migration<From, To>;
144
+ /** A `jslt` step: one root rule captured over the OLD row (`root`/`path`
145
+ * as externals), whose result spells the NEW row. Over a planned
146
+ * document it replaces the draft for `name`; otherwise it is appended
147
+ * for a table the target declares. */
148
+ transform<N extends DeclaredNames<To>, V extends ExprBase<unknown> = MemberExpr<DocOf<From, N>>>(
149
+ name: N,
150
+ rule: (row: V, x: Externals<{}, ValueOf<V>>) => Spell<DocOf<To, N>>,
151
+ ): Migration<From, To>;
152
+ /** A `stylesheet(…)` document — its output must be the new row when it
153
+ * is typed; a hand-written envelope is the honest top. A disposition or
154
+ * a mode table cannot ride in a `jslt` step (`JL0102`). */
155
+ transform<N extends DeclaredNames<To>, S extends StylesheetLike>(
156
+ name: N,
157
+ stylesheet: S & SheetFor<S, DocOf<To, N>>,
158
+ ): Migration<From, To>;
159
+ /** A rules array: a typed first rule must produce the new row; a
160
+ * hand-written rule is the honest top. */
161
+ transform<N extends DeclaredNames<To>, const R extends readonly RuleDocument[]>(
162
+ name: N,
163
+ rules: R & RulesFor<R, DocOf<To, N>>,
164
+ ): Migration<From, To>;
165
+ /** A `query` step over `name`'s rows: the predicate names the VIOLATION
166
+ * (`expect: 'empty'`, the default) or the witness (`'ebv'`); the row is
167
+ * the members the two shapes share, annotated when one shape is meant. */
168
+ assert<N extends DeclaredNames<To>, V extends ExprBase<unknown> = MemberExpr<DocOf<From, N> | DocOf<To, N>>>(
169
+ name: N,
170
+ predicate: (row: V) => BoolExpr | boolean,
171
+ options?: AssertOptions,
172
+ ): Migration<From, To>;
173
+ /** A query document over the rows, verbatim. */
174
+ assert<N extends DeclaredNames<To>>(name: N, query: Json, options?: AssertOptions): Migration<From, To>;
175
+ /** A backfill of stored derived columns (`derive`, §2.1). */
176
+ derive<N extends DeclaredNames<To>>(name: N, columns: readonly DeriveColumn[]): Migration<From, To>;
177
+ /** Any planner-emitted step, verbatim — the escape that keeps `rebuild` authorable. */
178
+ step(raw: MigrationStep): Migration<From, To>;
179
+ readonly document: MigrationDocument;
180
+ toJSON(): MigrationDocument;
181
+ }
182
+
183
+ export interface MigrationSpec<From, To> {
184
+ readonly id: string;
185
+ /** The model the store is at: a model document (the pen's or a JSON snapshot). */
186
+ readonly from: From;
187
+ /** The model the code carries. */
188
+ readonly to: To;
189
+ readonly note?: string;
190
+ }
191
+
192
+ /** A migration between two model documents; `from`/`to` are their shape hashes. */
193
+ export function defineMigration<From extends object, To extends object>(spec: MigrationSpec<From, To>): Migration<From, To>;
194
+
195
+ /** A planner's document, taken up so a typed `transform` replaces the
196
+ * draft it left; `from`/`to` type the transforms and are checked against
197
+ * the document's hashes (`JL0102` when they are not the planned models). */
198
+ export function fromPlanned<From = unknown, To = unknown>(
199
+ document: MigrationDocument | Json,
200
+ options?: { readonly from?: From; readonly to?: To },
201
+ ): Migration<From, To>;