@telorun/analyzer 0.57.0 → 0.58.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/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +91 -9
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +6 -3
- package/dist/definition-registry.d.ts +5 -0
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +9 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/invocation-contract.d.ts +21 -13
- package/dist/invocation-contract.d.ts.map +1 -1
- package/dist/invocation-contract.js +43 -46
- package/dist/loaded-types.d.ts +13 -0
- package/dist/loaded-types.d.ts.map +1 -1
- package/dist/manifest-loader.d.ts +8 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +33 -6
- package/dist/migrations/driver.d.ts +69 -0
- package/dist/migrations/driver.d.ts.map +1 -0
- package/dist/migrations/driver.js +200 -0
- package/dist/migrations/entries/index.d.ts +3 -0
- package/dist/migrations/entries/index.d.ts.map +1 -0
- package/dist/migrations/entries/index.js +9 -0
- package/dist/migrations/entries/normalize-value-types.json +48 -0
- package/dist/migrations/entries/ref-slot-scalar-type.json +20 -0
- package/dist/migrations/entry-data.d.ts +25 -0
- package/dist/migrations/entry-data.d.ts.map +1 -0
- package/dist/migrations/entry-data.js +171 -0
- package/dist/migrations/index.d.ts +22 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +19 -0
- package/dist/migrations/match.d.ts +162 -0
- package/dist/migrations/match.d.ts.map +1 -0
- package/dist/migrations/match.js +295 -0
- package/dist/migrations/patch.d.ts +68 -0
- package/dist/migrations/patch.d.ts.map +1 -0
- package/dist/migrations/patch.js +236 -0
- package/dist/migrations/provenance.d.ts +25 -0
- package/dist/migrations/provenance.d.ts.map +1 -0
- package/dist/migrations/provenance.js +118 -0
- package/dist/migrations/registry.d.ts +35 -0
- package/dist/migrations/registry.d.ts.map +1 -0
- package/dist/migrations/registry.js +35 -0
- package/dist/migrations/report.d.ts +28 -0
- package/dist/migrations/report.d.ts.map +1 -0
- package/dist/migrations/report.js +144 -0
- package/dist/migrations/types.d.ts +115 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations/types.js +24 -0
- package/dist/migrations/yaml-edit.d.ts +21 -0
- package/dist/migrations/yaml-edit.d.ts.map +1 -0
- package/dist/migrations/yaml-edit.js +245 -0
- package/dist/parse-loaded-file.d.ts +8 -0
- package/dist/parse-loaded-file.d.ts.map +1 -1
- package/dist/parse-loaded-file.js +22 -13
- package/dist/resolve-schema-type-refs.d.ts +29 -12
- package/dist/resolve-schema-type-refs.d.ts.map +1 -1
- package/dist/resolve-schema-type-refs.js +89 -16
- package/dist/schema-compat.d.ts +35 -9
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +180 -64
- package/dist/schema-region.d.ts +33 -0
- package/dist/schema-region.d.ts.map +1 -0
- package/dist/schema-region.js +45 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +29 -3
- package/dist/validate-step-inputs.d.ts +8 -1
- package/dist/validate-step-inputs.d.ts.map +1 -1
- package/dist/validate-step-inputs.js +47 -2
- package/dist/validate-value-type-slots.d.ts +43 -0
- package/dist/validate-value-type-slots.d.ts.map +1 -0
- package/dist/validate-value-type-slots.js +142 -0
- package/dist/value-type-keyword.d.ts +60 -0
- package/dist/value-type-keyword.d.ts.map +1 -0
- package/dist/value-type-keyword.js +119 -0
- package/dist/yaml-source-edit.d.ts +56 -0
- package/dist/yaml-source-edit.d.ts.map +1 -0
- package/dist/yaml-source-edit.js +93 -0
- package/package.json +3 -3
- package/src/analyzer.ts +99 -6
- package/src/cel-environment.ts +6 -3
- package/src/definition-registry.ts +10 -0
- package/src/index.ts +26 -2
- package/src/invocation-contract.ts +43 -48
- package/src/loaded-types.ts +13 -0
- package/src/manifest-loader.ts +37 -6
- package/src/migrations/driver.ts +262 -0
- package/src/migrations/entries/index.ts +10 -0
- package/src/migrations/entries/normalize-value-types.json +48 -0
- package/src/migrations/entries/ref-slot-scalar-type.json +20 -0
- package/src/migrations/entry-data.ts +207 -0
- package/src/migrations/index.ts +22 -0
- package/src/migrations/match.ts +382 -0
- package/src/migrations/patch.ts +265 -0
- package/src/migrations/provenance.ts +167 -0
- package/src/migrations/registry.ts +40 -0
- package/src/migrations/report.ts +169 -0
- package/src/migrations/types.ts +144 -0
- package/src/migrations/yaml-edit.ts +260 -0
- package/src/parse-loaded-file.ts +31 -12
- package/src/resolve-schema-type-refs.ts +93 -16
- package/src/schema-compat.ts +212 -74
- package/src/schema-region.ts +46 -0
- package/src/types.ts +17 -0
- package/src/validate-cel-context.ts +36 -3
- package/src/validate-step-inputs.ts +56 -1
- package/src/validate-value-type-slots.ts +182 -0
- package/src/value-type-keyword.ts +135 -0
- package/src/yaml-source-edit.ts +106 -0
- package/dist/binary-slot.d.ts +0 -39
- package/dist/binary-slot.d.ts.map +0 -1
- package/dist/binary-slot.js +0 -58
- package/src/binary-slot.ts +0 -71
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loaded-types.d.ts","sourceRoot":"","sources":["../src/loaded-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;4EAI4E;AAC5E,MAAM,WAAW,UAAU;IACzB;+DAC2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf;gEAC4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;+EAE2E;IAC3E,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;+EAC2E;IAC3E,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,SAAS,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,WAAW,EAAE,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"loaded-types.d.ts","sourceRoot":"","sources":["../src/loaded-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;4EAI4E;AAC5E,MAAM,WAAW,UAAU;IACzB;+DAC2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf;gEAC4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb;;+EAE2E;IAC3E,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB;+EAC2E;IAC3E,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,SAAS,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B;;;;2DAIuD;IACvD,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;mCACmC;AACnC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB;oEACgE;IAChE,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;;;;sCAKsC;AACtC,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB;;;kEAG8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB;6EACyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;kDAE8C;IAC9C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;0BAC0B;AAC1B,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB;;wBAEoB;IACpB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC;;;;2BAIuB;IACvB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAClD;;;iFAG6E;IAC7E,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;;sDAIkD;IAClD,oBAAoB,EAAE,kBAAkB,EAAE,CAAC;IAC3C;;;iDAG6C;IAC7C,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;IACzC;;;8EAG0E;IAC1E,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IACvC;yCACqC;IACrC,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B;8BAC0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;gFAE4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;CACd"}
|
|
@@ -15,6 +15,7 @@ export declare class Loader {
|
|
|
15
15
|
private readonly urlToSource;
|
|
16
16
|
protected sources: ManifestSource[];
|
|
17
17
|
private readonly celEnv;
|
|
18
|
+
private readonly migrations?;
|
|
18
19
|
/** Sources are resolved in order — the first whose `supports(url)` matches
|
|
19
20
|
* wins. The caller (composition root) decides which concrete sources exist
|
|
20
21
|
* and supplies them; `defaultSources()` bundles the browser-safe built-ins
|
|
@@ -50,7 +51,13 @@ export declare class Loader {
|
|
|
50
51
|
* caller opted in. Desugaring lives here, not in the pure `parseLoadedFile`,
|
|
51
52
|
* so round-trip consumers (the editor) keep a raw manifest/AST/position
|
|
52
53
|
* triple they can pair by index; only resolved consumers that pass
|
|
53
|
-
* `desugarImports` see synthetic Telo.Import manifests.
|
|
54
|
+
* `desugarImports` see synthetic Telo.Import manifests.
|
|
55
|
+
*
|
|
56
|
+
* The migration phase runs inside `parseLoadedFile`, i.e. before desugaring —
|
|
57
|
+
* a rule must only ever match author-written nodes, and the position is what
|
|
58
|
+
* makes that structural rather than a convention. Nothing needs the later
|
|
59
|
+
* position: the `imports:` map is read straight off the module manifest and is
|
|
60
|
+
* equally available before desugaring. */
|
|
54
61
|
private parseAndMaybeDesugar;
|
|
55
62
|
/** Raw text of any already-cached variant for `source`, so a cache miss on
|
|
56
63
|
* one (compile, desugar) variant reparses without a second source read. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AA2DpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D;;;;;8BAK0B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAA4B;IAExD;;;;mBAIe;gBACH,OAAO,GAAE,cAAc,EAAO,EAAE,OAAO,GAAE,iBAAsB;IAM3E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrD;;;;2CAIuC;IACvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;;;gFAS4E;IAC5E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAYzB;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAyCvE;;;;;;;;;;6CAUyC;IACzC,OAAO,CAAC,oBAAoB;IAe5B;gFAC4E;IAC5E,OAAO,CAAC,cAAc;IAQtB;;wEAEoE;IAC9D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsB3E;;;qCAGiC;IAC3B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAkI9E;;;;;;;;sBAQkB;IAClB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAOlE,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,mCAAmC;IAc3C,OAAO,CAAC,2BAA2B;YAkCrB,eAAe;IAmB7B;;;0CAGsC;IAChC,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CA0B5D"}
|
package/dist/manifest-loader.js
CHANGED
|
@@ -24,18 +24,34 @@ function collectParseDiagnostics(modules) {
|
|
|
24
24
|
}
|
|
25
25
|
return diagnostics;
|
|
26
26
|
}
|
|
27
|
+
/** Rewrite always, report locally. Every file in the graph was migrated, but a
|
|
28
|
+
* published dependency's manifest is not the consumer's to fix and its author
|
|
29
|
+
* is the only person who can — so only the entry module's own files (owner +
|
|
30
|
+
* its `include:` partials) report. Same rule `X_TELO_REF_UNRESOLVED` follows. */
|
|
31
|
+
function collectMigrationDiagnostics(entry) {
|
|
32
|
+
const diagnostics = [];
|
|
33
|
+
for (const file of [entry.owner, ...entry.partials]) {
|
|
34
|
+
diagnostics.push(...file.migrations.diagnostics);
|
|
35
|
+
}
|
|
36
|
+
return diagnostics;
|
|
37
|
+
}
|
|
27
38
|
const SYSTEM_KINDS = new Set([
|
|
28
39
|
"Telo.Application",
|
|
29
40
|
"Telo.Library",
|
|
30
41
|
"Telo.Import",
|
|
31
42
|
"Telo.Definition",
|
|
32
43
|
]);
|
|
33
|
-
/** File cache variant tags: compile (c/r) × desugarImports (d/n)
|
|
34
|
-
* and a raw load of the same file are distinct entries so
|
|
35
|
-
* wrong manifest tree
|
|
36
|
-
|
|
44
|
+
/** File cache variant tags: compile (c/r) × desugarImports (d/n) × migrate
|
|
45
|
+
* (m/x). A desugared and a raw load of the same file are distinct entries so
|
|
46
|
+
* neither sees the wrong manifest tree, and the migration axis is there for
|
|
47
|
+
* the same reason — the editor's round-trip view and `telo migrate` must see
|
|
48
|
+
* the author's spelling, everything else the current one. */
|
|
49
|
+
const CACHE_VARIANTS = [
|
|
50
|
+
"rnx", "rdx", "cnx", "cdx",
|
|
51
|
+
"rnm", "rdm", "cnm", "cdm",
|
|
52
|
+
];
|
|
37
53
|
function variantKey(options) {
|
|
38
|
-
return `${options?.compile ? "c" : "r"}${options?.desugarImports ? "d" : "n"}`;
|
|
54
|
+
return `${options?.compile ? "c" : "r"}${options?.desugarImports ? "d" : "n"}${options?.migrate ? "m" : "x"}`;
|
|
39
55
|
}
|
|
40
56
|
export class Loader {
|
|
41
57
|
/** LoadedFile cache keyed by `${compile ? "compiled" : "raw"}:${source}`.
|
|
@@ -52,6 +68,7 @@ export class Loader {
|
|
|
52
68
|
urlToSource = new Map();
|
|
53
69
|
sources;
|
|
54
70
|
celEnv;
|
|
71
|
+
migrations;
|
|
55
72
|
/** Sources are resolved in order — the first whose `supports(url)` matches
|
|
56
73
|
* wins. The caller (composition root) decides which concrete sources exist
|
|
57
74
|
* and supplies them; `defaultSources()` bundles the browser-safe built-ins
|
|
@@ -60,6 +77,7 @@ export class Loader {
|
|
|
60
77
|
constructor(sources = [], options = {}) {
|
|
61
78
|
this.sources = [...sources];
|
|
62
79
|
this.celEnv = buildCelEnvironment(options.celHandlers);
|
|
80
|
+
this.migrations = options.migrations;
|
|
63
81
|
}
|
|
64
82
|
register(source) {
|
|
65
83
|
this.sources.unshift(source);
|
|
@@ -157,11 +175,19 @@ export class Loader {
|
|
|
157
175
|
* caller opted in. Desugaring lives here, not in the pure `parseLoadedFile`,
|
|
158
176
|
* so round-trip consumers (the editor) keep a raw manifest/AST/position
|
|
159
177
|
* triple they can pair by index; only resolved consumers that pass
|
|
160
|
-
* `desugarImports` see synthetic Telo.Import manifests.
|
|
178
|
+
* `desugarImports` see synthetic Telo.Import manifests.
|
|
179
|
+
*
|
|
180
|
+
* The migration phase runs inside `parseLoadedFile`, i.e. before desugaring —
|
|
181
|
+
* a rule must only ever match author-written nodes, and the position is what
|
|
182
|
+
* makes that structural rather than a convention. Nothing needs the later
|
|
183
|
+
* position: the `imports:` map is read straight off the module manifest and is
|
|
184
|
+
* equally available before desugaring. */
|
|
161
185
|
parseAndMaybeDesugar(source, requestedUrl, text, options) {
|
|
162
186
|
const loaded = parseLoadedFile(source, requestedUrl, text, {
|
|
163
187
|
compile: options?.compile,
|
|
164
188
|
celEnv: this.celEnv,
|
|
189
|
+
migrate: options?.migrate,
|
|
190
|
+
migrations: this.migrations,
|
|
165
191
|
});
|
|
166
192
|
return options?.desugarImports ? desugarLoadedFile(loaded) : loaded;
|
|
167
193
|
}
|
|
@@ -318,6 +344,7 @@ export class Loader {
|
|
|
318
344
|
modules,
|
|
319
345
|
importEdges,
|
|
320
346
|
overrides,
|
|
347
|
+
migrationDiagnostics: collectMigrationDiagnostics(entry),
|
|
321
348
|
versionDiagnostics: diagnostics,
|
|
322
349
|
parseDiagnostics: collectParseDiagnostics(modules),
|
|
323
350
|
errors,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** The migration driver — one pass, one set of guarantees.
|
|
2
|
+
*
|
|
3
|
+
* **Composition is the driver's guarantee, not each entry's proof
|
|
4
|
+
* obligation.** The match set is frozen against the pre-migration tree, so no
|
|
5
|
+
* rule can match a node another rule produced; rules within an entry apply in
|
|
6
|
+
* order at each match; entries never see one another's output. That matters
|
|
7
|
+
* once core and module entries are aggregated from different parties, where
|
|
8
|
+
* "it happens to work" is not determinism. Idempotency then follows from the
|
|
9
|
+
* driver rather than from every author getting it right: a rule matches only
|
|
10
|
+
* the legacy spelling, and re-running finds nothing.
|
|
11
|
+
*
|
|
12
|
+
* How a rewrite is REPORTED — the provenance record and the diagnostic — is
|
|
13
|
+
* `report.ts`. Nothing there can change what a migration does, and nothing
|
|
14
|
+
* here decides how it reads. */
|
|
15
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
16
|
+
import type { Document } from "yaml";
|
|
17
|
+
import type { MigrationEntry, MigrationRewrite } from "./types.js";
|
|
18
|
+
import type { AnalysisDiagnostic } from "../types.js";
|
|
19
|
+
/** What one file's migration produced. */
|
|
20
|
+
export interface FileMigrations {
|
|
21
|
+
/** One record per applied rewrite, in application order. Empty when the file
|
|
22
|
+
* carried no legacy spelling — the overwhelmingly common case. */
|
|
23
|
+
readonly rewrites: readonly MigrationRewrite[];
|
|
24
|
+
/** One diagnostic per rewrite. Whether they are SURFACED is the graph's
|
|
25
|
+
* decision: a migration rewrites always, because the runtime must read
|
|
26
|
+
* artifacts published years ago, but reports only for the entry's own
|
|
27
|
+
* modules, because a published dependency is not the consumer's to fix. */
|
|
28
|
+
readonly diagnostics: readonly AnalysisDiagnostic[];
|
|
29
|
+
}
|
|
30
|
+
export declare const NO_MIGRATIONS: FileMigrations;
|
|
31
|
+
/**
|
|
32
|
+
* Run `entries` over one file's parsed documents, mutating `manifests` in
|
|
33
|
+
* place. Returns the provenance records and their diagnostics.
|
|
34
|
+
*
|
|
35
|
+
* The manifests are the loader's own projection of the file, never the
|
|
36
|
+
* author's text — nothing here can reach disk.
|
|
37
|
+
*/
|
|
38
|
+
export declare function migrateManifests(args: {
|
|
39
|
+
source: string;
|
|
40
|
+
manifests: Array<ResourceManifest | null>;
|
|
41
|
+
entries?: readonly MigrationEntry[];
|
|
42
|
+
}): FileMigrations;
|
|
43
|
+
/**
|
|
44
|
+
* Migrate the author's YAML instead of the loader's tree — the operation
|
|
45
|
+
* `telo migrate` is the reference application of.
|
|
46
|
+
*
|
|
47
|
+
* `manifests` must be the RAW (un-migrated) projection of `documents`, since
|
|
48
|
+
* the matchers select legacy spellings. Returns `null` when nothing matched, so
|
|
49
|
+
* a caller can leave an untouched file untouched rather than rewriting
|
|
50
|
+
* identical bytes.
|
|
51
|
+
*
|
|
52
|
+
* `unwritable` names every rewrite the tree accepted but the TEXT could not
|
|
53
|
+
* express. The two appliers can disagree only in this direction, and the
|
|
54
|
+
* disagreement has to be reported: the diagnostic that sent the author here
|
|
55
|
+
* says "run `telo migrate`", so a location this silently skipped would keep
|
|
56
|
+
* warning with no way to act on it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function migrateFileText(args: {
|
|
59
|
+
source: string;
|
|
60
|
+
text: string;
|
|
61
|
+
documents: readonly Document[];
|
|
62
|
+
manifests: ReadonlyArray<ResourceManifest | null>;
|
|
63
|
+
entries?: readonly MigrationEntry[];
|
|
64
|
+
}): {
|
|
65
|
+
text: string;
|
|
66
|
+
rewrites: MigrationRewrite[];
|
|
67
|
+
unwritable: MigrationRewrite[];
|
|
68
|
+
} | null;
|
|
69
|
+
//# sourceMappingURL=driver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/migrations/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;iCAaiC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAWrC,OAAO,KAAK,EACV,cAAc,EAGd,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B;uEACmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C;;;gFAG4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrD;AAED,eAAO,MAAM,aAAa,EAAE,cAAkD,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACrC,GAAG,cAAc,CAYjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC/B,SAAS,EAAE,aAAa,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACrC,GAAG;IACF,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC,GAAG,IAAI,CA8CP"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** The migration driver — one pass, one set of guarantees.
|
|
2
|
+
*
|
|
3
|
+
* **Composition is the driver's guarantee, not each entry's proof
|
|
4
|
+
* obligation.** The match set is frozen against the pre-migration tree, so no
|
|
5
|
+
* rule can match a node another rule produced; rules within an entry apply in
|
|
6
|
+
* order at each match; entries never see one another's output. That matters
|
|
7
|
+
* once core and module entries are aggregated from different parties, where
|
|
8
|
+
* "it happens to work" is not determinism. Idempotency then follows from the
|
|
9
|
+
* driver rather than from every author getting it right: a rule matches only
|
|
10
|
+
* the legacy spelling, and re-running finds nothing.
|
|
11
|
+
*
|
|
12
|
+
* How a rewrite is REPORTED — the provenance record and the diagnostic — is
|
|
13
|
+
* `report.ts`. Nothing there can change what a migration does, and nothing
|
|
14
|
+
* here decides how it reads. */
|
|
15
|
+
import { applyEffectsToTree, formatMigrationPath, planPatch, } from "./patch.js";
|
|
16
|
+
import { applyTextEdits, planTextEdits } from "./yaml-edit.js";
|
|
17
|
+
import { applicableRules, buildMatchIndex, selectMatches } from "./match.js";
|
|
18
|
+
import { CORE_MIGRATIONS } from "./registry.js";
|
|
19
|
+
import { toDiagnostic, toRewrite } from "./report.js";
|
|
20
|
+
export const NO_MIGRATIONS = { rewrites: [], diagnostics: [] };
|
|
21
|
+
/**
|
|
22
|
+
* Run `entries` over one file's parsed documents, mutating `manifests` in
|
|
23
|
+
* place. Returns the provenance records and their diagnostics.
|
|
24
|
+
*
|
|
25
|
+
* The manifests are the loader's own projection of the file, never the
|
|
26
|
+
* author's text — nothing here can reach disk.
|
|
27
|
+
*/
|
|
28
|
+
export function migrateManifests(args) {
|
|
29
|
+
const entries = args.entries ?? CORE_MIGRATIONS;
|
|
30
|
+
if (entries.length === 0)
|
|
31
|
+
return NO_MIGRATIONS;
|
|
32
|
+
const applied = applyAll(args.manifests, entries);
|
|
33
|
+
if (applied.length === 0)
|
|
34
|
+
return NO_MIGRATIONS;
|
|
35
|
+
const rewrites = applied.map((a) => toRewrite(a));
|
|
36
|
+
const diagnostics = applied.map((a, i) => toDiagnostic(a, rewrites[i], args.source, args.manifests));
|
|
37
|
+
return { rewrites, diagnostics };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Migrate the author's YAML instead of the loader's tree — the operation
|
|
41
|
+
* `telo migrate` is the reference application of.
|
|
42
|
+
*
|
|
43
|
+
* `manifests` must be the RAW (un-migrated) projection of `documents`, since
|
|
44
|
+
* the matchers select legacy spellings. Returns `null` when nothing matched, so
|
|
45
|
+
* a caller can leave an untouched file untouched rather than rewriting
|
|
46
|
+
* identical bytes.
|
|
47
|
+
*
|
|
48
|
+
* `unwritable` names every rewrite the tree accepted but the TEXT could not
|
|
49
|
+
* express. The two appliers can disagree only in this direction, and the
|
|
50
|
+
* disagreement has to be reported: the diagnostic that sent the author here
|
|
51
|
+
* says "run `telo migrate`", so a location this silently skipped would keep
|
|
52
|
+
* warning with no way to act on it.
|
|
53
|
+
*/
|
|
54
|
+
export function migrateFileText(args) {
|
|
55
|
+
const entries = args.entries ?? CORE_MIGRATIONS;
|
|
56
|
+
if (entries.length === 0)
|
|
57
|
+
return null;
|
|
58
|
+
// Planned against a copy: the tree walk is what decides which patches apply
|
|
59
|
+
// (and in what order they refuse), so the YAML side must ask exactly the same
|
|
60
|
+
// question of exactly the same state rather than re-deriving it.
|
|
61
|
+
const scratch = args.manifests.map((m) => (m ? structuredClone(m) : null));
|
|
62
|
+
const applied = applyAll(scratch, entries);
|
|
63
|
+
if (applied.length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
const edits = [];
|
|
66
|
+
const kept = [];
|
|
67
|
+
const skipped = [];
|
|
68
|
+
for (const patch of applied) {
|
|
69
|
+
const doc = args.documents[patch.documentIndex];
|
|
70
|
+
if (!doc) {
|
|
71
|
+
skipped.push(patch);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const planned = planTextEdits(doc, args.text, patch.plan.effects);
|
|
75
|
+
// A patch the tree accepted but the text cannot express (a block scalar
|
|
76
|
+
// span, a flow-style entry) leaves that node alone rather than half-written.
|
|
77
|
+
if (!planned) {
|
|
78
|
+
skipped.push(patch);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
// Two patches whose spans overlap cannot both be spliced — the second would
|
|
82
|
+
// write into bytes the first replaced. The tree side has no equivalent,
|
|
83
|
+
// because an object write is idempotent where a splice is positional, so
|
|
84
|
+
// this is the one place the file applier is stricter: the later patch is
|
|
85
|
+
// dropped and its node stays as the author wrote it.
|
|
86
|
+
if (planned.some((edit) => edits.some((existing) => overlaps(edit, existing)))) {
|
|
87
|
+
skipped.push(patch);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
edits.push(...planned);
|
|
91
|
+
kept.push(patch);
|
|
92
|
+
}
|
|
93
|
+
if (edits.length === 0 && skipped.length === 0)
|
|
94
|
+
return null;
|
|
95
|
+
return {
|
|
96
|
+
text: edits.length > 0 ? applyTextEdits(args.text, edits) : args.text,
|
|
97
|
+
rewrites: kept.map(toRewrite),
|
|
98
|
+
unwritable: skipped.map(toRewrite),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Whether two splices contend for the same bytes. A pure insertion (an empty
|
|
102
|
+
* span) collides only with a span that strictly contains its point. */
|
|
103
|
+
function overlaps(a, b) {
|
|
104
|
+
if (a.start === a.end)
|
|
105
|
+
return b.start < a.start && a.start < b.end;
|
|
106
|
+
if (b.start === b.end)
|
|
107
|
+
return a.start < b.start && b.start < a.end;
|
|
108
|
+
return a.start < b.end && b.start < a.end;
|
|
109
|
+
}
|
|
110
|
+
/** One pass: every match collected against the frozen pre-migration tree, then
|
|
111
|
+
* applied in entry order. */
|
|
112
|
+
function applyAll(manifests, entries) {
|
|
113
|
+
const candidates = [];
|
|
114
|
+
// One index per DOCUMENT, shared by every rule that can reach it — the walk
|
|
115
|
+
// is the expensive part and does not depend on the rule. This runs on the
|
|
116
|
+
// kernel's boot path for every file in the graph, so a walk per rule would
|
|
117
|
+
// scale the cost of loading any manifest with the size of the migration set.
|
|
118
|
+
//
|
|
119
|
+
// The `inKind` gate and the `under` regions are both known BEFORE the walk,
|
|
120
|
+
// so they bound it rather than filtering its output: a document no rule
|
|
121
|
+
// targets is never walked, and a region no rule names is never descended
|
|
122
|
+
// into. `type:` alone occurs a couple of hundred times in a standard-library
|
|
123
|
+
// manifest, so indexing sites that cannot be selected is the whole cost.
|
|
124
|
+
const flatRules = entries.flatMap((entry) => entry.rules.map((rule) => ({ entry, ops: rule.patch, match: rule.match })));
|
|
125
|
+
for (let documentIndex = 0; documentIndex < manifests.length; documentIndex++) {
|
|
126
|
+
const manifest = manifests[documentIndex];
|
|
127
|
+
if (!manifest)
|
|
128
|
+
continue;
|
|
129
|
+
const { rules, keys, roots } = applicableRules(flatRules, manifest.kind);
|
|
130
|
+
if (rules.length === 0)
|
|
131
|
+
continue;
|
|
132
|
+
const index = buildMatchIndex(manifest, keys, roots);
|
|
133
|
+
for (const { entry, ops, match } of rules) {
|
|
134
|
+
for (const path of selectMatches(index, manifest, match)) {
|
|
135
|
+
candidates.push({ entry, ops, documentIndex, path });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Arrays whose LENGTH an already-applied patch changed, as
|
|
140
|
+
// `<documentIndex>:<dotted path>`. A frozen match names a sequence element by
|
|
141
|
+
// INDEX, and an index is not an identity: once a sibling patch inserted or
|
|
142
|
+
// removed an item, the same index names a different element — including one
|
|
143
|
+
// another rule just produced, which is exactly what the frozen match set
|
|
144
|
+
// exists to prevent. A key-based path needs no such record, because a rename
|
|
145
|
+
// or removal makes the stale path resolve to nothing and `planPatch` refuses
|
|
146
|
+
// it on its own.
|
|
147
|
+
const shiftedArrays = new Set();
|
|
148
|
+
const applied = [];
|
|
149
|
+
for (const candidate of candidates) {
|
|
150
|
+
const manifest = manifests[candidate.documentIndex];
|
|
151
|
+
if (!manifest)
|
|
152
|
+
continue;
|
|
153
|
+
if (indexIsStale(candidate.documentIndex, candidate.path, shiftedArrays))
|
|
154
|
+
continue;
|
|
155
|
+
const result = planPatch(manifest, candidate.path, candidate.ops);
|
|
156
|
+
// Refusal is not an error: the node stays as the author wrote it and the
|
|
157
|
+
// ordinary validator reports it with an accurate message.
|
|
158
|
+
if (!result.ok)
|
|
159
|
+
continue;
|
|
160
|
+
applyEffectsToTree(manifest, result.plan.effects);
|
|
161
|
+
for (const array of resizedArrays(candidate.documentIndex, result.plan.effects)) {
|
|
162
|
+
shiftedArrays.add(array);
|
|
163
|
+
}
|
|
164
|
+
applied.push({
|
|
165
|
+
entry: candidate.entry,
|
|
166
|
+
documentIndex: candidate.documentIndex,
|
|
167
|
+
matched: candidate.path,
|
|
168
|
+
plan: result.plan,
|
|
169
|
+
ops: candidate.ops,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return applied;
|
|
173
|
+
}
|
|
174
|
+
/** Arrays this patch resized, keyed for `shiftedArrays`. */
|
|
175
|
+
function resizedArrays(documentIndex, effects) {
|
|
176
|
+
const out = [];
|
|
177
|
+
for (const effect of effects) {
|
|
178
|
+
if (effect.kind === "insert-item") {
|
|
179
|
+
out.push(`${documentIndex}:${formatMigrationPath(effect.path)}`);
|
|
180
|
+
}
|
|
181
|
+
else if (effect.kind === "remove-entry" &&
|
|
182
|
+
typeof effect.path[effect.path.length - 1] === "number") {
|
|
183
|
+
out.push(`${documentIndex}:${formatMigrationPath(effect.path.slice(0, -1))}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
/** Whether any index along `path` steps into an array a prior patch resized. */
|
|
189
|
+
function indexIsStale(documentIndex, path, shiftedArrays) {
|
|
190
|
+
if (shiftedArrays.size === 0)
|
|
191
|
+
return false;
|
|
192
|
+
for (let i = 0; i < path.length; i++) {
|
|
193
|
+
if (typeof path[i] !== "number")
|
|
194
|
+
continue;
|
|
195
|
+
if (shiftedArrays.has(`${documentIndex}:${formatMigrationPath(path.slice(0, i))}`)) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/migrations/entries/index.ts"],"names":[],"mappings":"AAKA,8DAA8D;AAC9D,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAGvF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// GENERATED by scripts/copy-migration-entries.mjs — do not edit, and do not commit.
|
|
2
|
+
// Source: analyzer/migrations/*.json (lexically ordered).
|
|
3
|
+
import e0 from "./normalize-value-types.json" with { type: "json" };
|
|
4
|
+
import e1 from "./ref-slot-scalar-type.json" with { type: "json" };
|
|
5
|
+
/** Every entry file, in the order the driver applies them. */
|
|
6
|
+
export const MIGRATION_ENTRY_FILES = [
|
|
7
|
+
["normalize-value-types.json", e0],
|
|
8
|
+
["ref-slot-scalar-type.json", e1],
|
|
9
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Three keywords answered one question — what is the value at this slot, beyond what JSON Schema's `type` can say — and each answered it differently. `inSchema` with the wildcard `inKind` / `under` is what makes this reachable at all: these annotations occur in author-written schema fragments inside ORDINARY resource documents (an inline `inputType:` on any kind that declares one, a route's `request.schema.body`), and that set of kinds is open, so enumerating it would be both incomplete and a violation of the topology-driven constraint. The wildcards are earned by the pairing the reader enforces: a schema region bounds where the walk may go, and an `x-telo-*` key — Telo vocabulary wherever it appears — bounds what it may touch. Each rule sets the value BEFORE renaming the key, so both match the frozen pre-migration tree; a rename first would leave the second rule looking for a node that no longer exists. `notUnder` keeps all three out of the data-bearing keywords, where a value may merely look like a schema.",
|
|
3
|
+
"id": "normalize-value-types",
|
|
4
|
+
"code": "X_TELO_TYPE_DEPRECATED",
|
|
5
|
+
"severity": "warning",
|
|
6
|
+
"reason": "The three value-type annotations were unified into one `x-telo-type`, whose names are `Telo.`-qualified like every other kernel-owned name. `x-telo-binary` and `x-telo-stream` said what a value IS just as `x-telo-type` did, differing only in posture toward the JSON Schema layer — so a fourth kind of value meant a fourth keyword and edits across four packages.",
|
|
7
|
+
"rules": [
|
|
8
|
+
{
|
|
9
|
+
"match": {
|
|
10
|
+
"key": "x-telo-stream",
|
|
11
|
+
"inKind": ["*"],
|
|
12
|
+
"under": ["*"],
|
|
13
|
+
"inSchema": true,
|
|
14
|
+
"value": true,
|
|
15
|
+
"notUnder": ["const", "default", "enum", "examples"]
|
|
16
|
+
},
|
|
17
|
+
"patch": [
|
|
18
|
+
{ "op": "set-value", "value": "Telo.Stream" },
|
|
19
|
+
{ "op": "rename-key", "to": "x-telo-type" }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"match": {
|
|
24
|
+
"key": "x-telo-binary",
|
|
25
|
+
"inKind": ["*"],
|
|
26
|
+
"under": ["*"],
|
|
27
|
+
"inSchema": true,
|
|
28
|
+
"value": true,
|
|
29
|
+
"notUnder": ["const", "default", "enum", "examples"]
|
|
30
|
+
},
|
|
31
|
+
"patch": [
|
|
32
|
+
{ "op": "set-value", "value": "Telo.Bytes" },
|
|
33
|
+
{ "op": "rename-key", "to": "x-telo-type" }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"match": {
|
|
38
|
+
"key": "x-telo-type",
|
|
39
|
+
"inKind": ["*"],
|
|
40
|
+
"under": ["*"],
|
|
41
|
+
"inSchema": true,
|
|
42
|
+
"valueOneOf": ["TcpPort", "UdpPort"],
|
|
43
|
+
"notUnder": ["const", "default", "enum", "examples"]
|
|
44
|
+
},
|
|
45
|
+
"patch": [{ "op": "set-value", "qualify": "Telo." }]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "See README.md in this directory. `inKind` + `under` are the positive containment: `under` names TOP-LEVEL keys, so this rule can reach nothing but the schema blocks of a kind document — never the `resources:` template body, whose nested config may hold a key spelled `schema` over data. `withSibling` is what makes it precise — a scalar `type:` is stale only because an `x-telo-ref` sits beside it. `notUnder` then narrows within that region, keeping the rule out of the data-bearing keywords where a value may merely look like a schema.",
|
|
3
|
+
"id": "ref-slot-scalar-type",
|
|
4
|
+
"code": "X_TELO_REF_SCALAR_TYPE",
|
|
5
|
+
"severity": "warning",
|
|
6
|
+
"reason": "A reference is written with the `!ref` tag and resolves to an object, so a reference slot no longer carries the scalar `type:` that plain-string references were pinned to. Leaving it in place makes the slot reject the very value it is declared to accept, and every schema reader has had to know to ignore it.",
|
|
7
|
+
"rules": [
|
|
8
|
+
{
|
|
9
|
+
"match": {
|
|
10
|
+
"key": "type",
|
|
11
|
+
"inKind": ["Telo.Definition", "Telo.Abstract"],
|
|
12
|
+
"under": ["schema", "status", "inputType", "outputType"],
|
|
13
|
+
"valueOneOf": ["string", "number", "integer", "boolean"],
|
|
14
|
+
"withSibling": "x-telo-ref",
|
|
15
|
+
"notUnder": ["const", "default", "enum", "examples"]
|
|
16
|
+
},
|
|
17
|
+
"patch": [{ "op": "remove-entry" }]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The on-disk shape of a migration entry, and its reader.
|
|
2
|
+
*
|
|
3
|
+
* An entry is DATA: one file per entry under `analyzer/migrations/`, consumed
|
|
4
|
+
* as one lexically ordered set. That is what makes adding a migration one file
|
|
5
|
+
* and retiring one deleting it, what ships the set in the published package,
|
|
6
|
+
* and what lets the Rust crate embed the identical files at build time rather
|
|
7
|
+
* than reimplementing each rewrite.
|
|
8
|
+
*
|
|
9
|
+
* **An entry contains no code.** Both halves — what a rule matches and what it
|
|
10
|
+
* patches — are data, so one file is read identically by every kernel. A
|
|
11
|
+
* predicate expressed in one language would mean one artifact is read two
|
|
12
|
+
* ways, invisibly, since a migration that succeeds is silent.
|
|
13
|
+
*
|
|
14
|
+
* Reading is STRICT. A malformed entry is an authoring mistake, and the
|
|
15
|
+
* alternative to throwing is a migration that silently does not run — the one
|
|
16
|
+
* failure mode a rewrite-on-load design cannot afford, because a migration
|
|
17
|
+
* that succeeds and one that never fires look identical. */
|
|
18
|
+
import type { MigrationEntry } from "./types.js";
|
|
19
|
+
/**
|
|
20
|
+
* Read one entry file's parsed data into a `MigrationEntry`.
|
|
21
|
+
*
|
|
22
|
+
* `file` names the entry file, so a failure says which one.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseMigrationEntry(file: string, data: unknown): MigrationEntry;
|
|
25
|
+
//# sourceMappingURL=entry-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-data.d.ts","sourceRoot":"","sources":["../../src/migrations/entry-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;6DAgB6D;AAI7D,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,YAAY,CAAC;AAgJpB;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,cAAc,CAiC/E"}
|