@telorun/analyzer 0.56.1 → 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 +5 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +232 -94
- 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 +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- 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/manifest-visitor.d.ts +4 -0
- package/dist/manifest-visitor.d.ts.map +1 -1
- package/dist/manifest-visitor.js +3 -3
- 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/module-file-claims.d.ts +65 -0
- package/dist/module-file-claims.d.ts.map +1 -0
- package/dist/module-file-claims.js +106 -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 +181 -54
- 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 +51 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +29 -3
- package/dist/validate-include-placement.d.ts +26 -0
- package/dist/validate-include-placement.d.ts.map +1 -0
- package/dist/validate-include-placement.js +67 -0
- 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-throws-coverage.d.ts.map +1 -1
- package/dist/validate-throws-coverage.js +15 -12
- 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 +4 -3
- package/src/analyzer.ts +276 -129
- package/src/cel-environment.ts +6 -3
- package/src/definition-registry.ts +10 -0
- package/src/index.ts +31 -3
- package/src/invocation-contract.ts +43 -48
- package/src/loaded-types.ts +13 -0
- package/src/manifest-loader.ts +37 -6
- package/src/manifest-visitor.ts +11 -3
- 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/module-file-claims.ts +168 -0
- package/src/parse-loaded-file.ts +31 -12
- package/src/resolve-schema-type-refs.ts +93 -16
- package/src/schema-compat.ts +218 -62
- package/src/schema-region.ts +46 -0
- package/src/types.ts +54 -0
- package/src/validate-cel-context.ts +36 -3
- package/src/validate-include-placement.ts +70 -0
- package/src/validate-step-inputs.ts +56 -1
- package/src/validate-throws-coverage.ts +16 -11
- 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
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
|
|
6
|
+
/** Every entry file, in the order the driver applies them. */
|
|
7
|
+
export const MIGRATION_ENTRY_FILES: ReadonlyArray<readonly [file: string, data: unknown]> = [
|
|
8
|
+
["normalize-value-types.json", e0],
|
|
9
|
+
["ref-slot-scalar-type.json", e1],
|
|
10
|
+
];
|
|
@@ -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,207 @@
|
|
|
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
|
+
|
|
19
|
+
import { DiagnosticSeverity } from "../types.js";
|
|
20
|
+
import { readMigrationMatch } from "./match.js";
|
|
21
|
+
import type {
|
|
22
|
+
MigrationEntry,
|
|
23
|
+
MigrationOperation,
|
|
24
|
+
MigrationRule,
|
|
25
|
+
} from "./types.js";
|
|
26
|
+
import { MIGRATION_OPS } from "./types.js";
|
|
27
|
+
|
|
28
|
+
/** How an entry file names a severity. The LSP integers are a transport detail
|
|
29
|
+
* of one editor; an entry is read by a Rust crate too. */
|
|
30
|
+
const SEVERITIES: Record<string, DiagnosticSeverity> = {
|
|
31
|
+
error: DiagnosticSeverity.Error,
|
|
32
|
+
warning: DiagnosticSeverity.Warning,
|
|
33
|
+
info: DiagnosticSeverity.Information,
|
|
34
|
+
hint: DiagnosticSeverity.Hint,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
class MigrationEntryError extends Error {
|
|
38
|
+
constructor(file: string, detail: string) {
|
|
39
|
+
super(`Invalid migration entry '${file}': ${detail}`);
|
|
40
|
+
this.name = "MigrationEntryError";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
45
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function requireString(file: string, node: Record<string, unknown>, key: string): string {
|
|
49
|
+
const value = node[key];
|
|
50
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
51
|
+
throw new MigrationEntryError(file, `'${key}' must be a non-empty string`);
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const OPERATION_KEYS: Record<(typeof MIGRATION_OPS)[number], readonly string[]> = {
|
|
57
|
+
"rename-key": ["to"],
|
|
58
|
+
"set-value": ["value", "qualify"],
|
|
59
|
+
"set-tag": ["tag"],
|
|
60
|
+
"insert-item": ["value", "at"],
|
|
61
|
+
"remove-entry": [],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** Top-level entry keys. `$comment` is the sanctioned place for author notes —
|
|
65
|
+
* the reader ignores it, but it has to be DECLARED, or "unknown keys are
|
|
66
|
+
* refused" would be true of every level but this one. */
|
|
67
|
+
const ENTRY_KEYS = ["id", "code", "severity", "reason", "rules", "$comment"] as const;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A value a patch may write.
|
|
71
|
+
*
|
|
72
|
+
* Scalars only, because "every operation has a known YAML edit form" is what
|
|
73
|
+
* makes a migration applicable to a FILE, and the file applier renders a value
|
|
74
|
+
* by re-quoting it in the author's own style at the node's own span — which has
|
|
75
|
+
* no meaning for a mapping or a sequence. Without this check the limitation is
|
|
76
|
+
* invisible until a user runs `telo migrate` and is told, permanently, to fix
|
|
77
|
+
* it by hand; with it, the entry's author learns at authoring time. Structured
|
|
78
|
+
* values are a vocabulary extension (a block renderer), not a silent gap.
|
|
79
|
+
*/
|
|
80
|
+
function requireScalarValue(file: string, index: number, op: string, value: unknown): void {
|
|
81
|
+
if (
|
|
82
|
+
value === null ||
|
|
83
|
+
typeof value === "string" ||
|
|
84
|
+
typeof value === "number" ||
|
|
85
|
+
typeof value === "boolean"
|
|
86
|
+
) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
throw new MigrationEntryError(
|
|
90
|
+
file,
|
|
91
|
+
`patch[${index}] ('${op}') value must be a scalar (string, number, boolean or null) — ` +
|
|
92
|
+
`a mapping or sequence has no in-place YAML edit form, so \`telo migrate\` could never apply it`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readOperation(file: string, raw: unknown, index: number): MigrationOperation {
|
|
97
|
+
if (!isPlainObject(raw)) {
|
|
98
|
+
throw new MigrationEntryError(file, `patch[${index}] must be a mapping`);
|
|
99
|
+
}
|
|
100
|
+
const op = raw.op;
|
|
101
|
+
if (typeof op !== "string" || !(MIGRATION_OPS as readonly string[]).includes(op)) {
|
|
102
|
+
throw new MigrationEntryError(
|
|
103
|
+
file,
|
|
104
|
+
`patch[${index}].op '${String(op)}' is not one of ${MIGRATION_OPS.join(", ")}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
// The vocabulary is closed, so an unknown parameter is a typo — and a typo in
|
|
108
|
+
// a patch is a rewrite that quietly does something other than what it reads
|
|
109
|
+
// as. `set-value`'s two parameters are alternatives, checked below.
|
|
110
|
+
const allowed = new Set<string>([...OPERATION_KEYS[op as (typeof MIGRATION_OPS)[number]], "op"]);
|
|
111
|
+
for (const key of Object.keys(raw)) {
|
|
112
|
+
if (!allowed.has(key)) {
|
|
113
|
+
throw new MigrationEntryError(file, `patch[${index}] ('${op}') has no parameter '${key}'`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
switch (op) {
|
|
118
|
+
case "rename-key":
|
|
119
|
+
return { op, to: requireString(file, raw, "to") };
|
|
120
|
+
case "set-value": {
|
|
121
|
+
const hasValue = Object.hasOwn(raw, "value");
|
|
122
|
+
const hasQualify = Object.hasOwn(raw, "qualify");
|
|
123
|
+
if (hasValue === hasQualify) {
|
|
124
|
+
throw new MigrationEntryError(
|
|
125
|
+
file,
|
|
126
|
+
`patch[${index}] ('set-value') takes exactly one of 'value' or 'qualify'`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (hasQualify) return { op, qualify: requireString(file, raw, "qualify") };
|
|
130
|
+
requireScalarValue(file, index, op, raw.value);
|
|
131
|
+
return { op, value: raw.value };
|
|
132
|
+
}
|
|
133
|
+
case "set-tag":
|
|
134
|
+
return { op, tag: requireString(file, raw, "tag") };
|
|
135
|
+
case "insert-item": {
|
|
136
|
+
if (!Object.hasOwn(raw, "value")) {
|
|
137
|
+
throw new MigrationEntryError(file, `patch[${index}] ('insert-item') requires 'value'`);
|
|
138
|
+
}
|
|
139
|
+
requireScalarValue(file, index, op, raw.value);
|
|
140
|
+
const at = raw.at;
|
|
141
|
+
if (at !== undefined && (typeof at !== "number" || !Number.isInteger(at) || at < 0)) {
|
|
142
|
+
throw new MigrationEntryError(
|
|
143
|
+
file,
|
|
144
|
+
`patch[${index}].at must be a non-negative integer when present`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return at === undefined ? { op, value: raw.value } : { op, value: raw.value, at };
|
|
148
|
+
}
|
|
149
|
+
case "remove-entry":
|
|
150
|
+
return { op };
|
|
151
|
+
default:
|
|
152
|
+
throw new MigrationEntryError(file, `patch[${index}].op '${op}' is unhandled`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readRule(file: string, raw: unknown, index: number): MigrationRule {
|
|
157
|
+
if (!isPlainObject(raw)) {
|
|
158
|
+
throw new MigrationEntryError(file, `rules[${index}] must be a mapping`);
|
|
159
|
+
}
|
|
160
|
+
if (!Array.isArray(raw.patch) || raw.patch.length === 0) {
|
|
161
|
+
throw new MigrationEntryError(file, `rules[${index}].patch must be a non-empty sequence`);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
match: readMigrationMatch(`Invalid migration entry '${file}': rules[${index}]`, raw.match),
|
|
165
|
+
patch: raw.patch.map((op, i) => readOperation(file, op, i)),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Read one entry file's parsed data into a `MigrationEntry`.
|
|
171
|
+
*
|
|
172
|
+
* `file` names the entry file, so a failure says which one.
|
|
173
|
+
*/
|
|
174
|
+
export function parseMigrationEntry(file: string, data: unknown): MigrationEntry {
|
|
175
|
+
if (!isPlainObject(data)) {
|
|
176
|
+
throw new MigrationEntryError(file, "an entry must be a mapping");
|
|
177
|
+
}
|
|
178
|
+
// Closed at every level, this one included: a typo'd top-level key would
|
|
179
|
+
// otherwise be silently ignored, which is the one failure a rewrite-on-load
|
|
180
|
+
// design cannot afford once module-shipped entries make this a trust boundary.
|
|
181
|
+
for (const key of Object.keys(data)) {
|
|
182
|
+
if (!(ENTRY_KEYS as readonly string[]).includes(key)) {
|
|
183
|
+
throw new MigrationEntryError(
|
|
184
|
+
file,
|
|
185
|
+
`an entry has no key '${key}'. Known keys: ${ENTRY_KEYS.join(", ")}.`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const severityName = requireString(file, data, "severity");
|
|
190
|
+
const severity = SEVERITIES[severityName];
|
|
191
|
+
if (severity === undefined) {
|
|
192
|
+
throw new MigrationEntryError(
|
|
193
|
+
file,
|
|
194
|
+
`severity '${severityName}' must be one of ${Object.keys(SEVERITIES).join(", ")}`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
if (!Array.isArray(data.rules) || data.rules.length === 0) {
|
|
198
|
+
throw new MigrationEntryError(file, "'rules' must be a non-empty sequence");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
id: requireString(file, data, "id"),
|
|
202
|
+
code: requireString(file, data, "code"),
|
|
203
|
+
severity,
|
|
204
|
+
reason: requireString(file, data, "reason").trim(),
|
|
205
|
+
rules: data.rules.map((rule, i) => readRule(file, rule, i)),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The migration mechanism's surface — what a consumer OUTSIDE this directory
|
|
2
|
+
* may hold.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately narrow. The matcher, the patch planner and the two appliers are
|
|
5
|
+
* one mechanism with one entry point per job (`migrateManifests` for the
|
|
6
|
+
* loader, `migrateFileText` for `telo migrate`, `remapMigratedPaths` for a
|
|
7
|
+
* diagnostic consumer); re-exporting their internals would make the plan/apply
|
|
8
|
+
* split, the effect vocabulary and the text-edit shape semver-bound API with
|
|
9
|
+
* no caller, and freeze the one part most likely to change as the operation
|
|
10
|
+
* vocabulary grows.
|
|
11
|
+
*
|
|
12
|
+
* The entry-set trio (`CORE_MIGRATIONS`, `parseMigrationEntry`,
|
|
13
|
+
* `MigrationEntry`) is here because `LoaderInitOptions.migrations` is a
|
|
14
|
+
* composition-root seam: a host aggregating module-shipped entries beside the
|
|
15
|
+
* core ones needs to read one and to name the set it is extending. */
|
|
16
|
+
|
|
17
|
+
export { migrateFileText, migrateManifests, NO_MIGRATIONS } from "./driver.js";
|
|
18
|
+
export type { FileMigrations } from "./driver.js";
|
|
19
|
+
export { parseMigrationEntry } from "./entry-data.js";
|
|
20
|
+
export { remapMigratedPaths } from "./provenance.js";
|
|
21
|
+
export { CORE_MIGRATIONS } from "./registry.js";
|
|
22
|
+
export type { MigrationEntry, MigrationRewrite } from "./types.js";
|