@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.
- package/ARCHITECTURE.md +217 -0
- package/README.md +559 -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 +771 -383
- 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 +437 -36
- 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 +231 -26
- 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/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;
|
|
@@ -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>;
|