@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.
Files changed (116) hide show
  1. package/dist/analyzer.d.ts.map +1 -1
  2. package/dist/analyzer.js +91 -9
  3. package/dist/cel-environment.d.ts.map +1 -1
  4. package/dist/cel-environment.js +6 -3
  5. package/dist/definition-registry.d.ts +5 -0
  6. package/dist/definition-registry.d.ts.map +1 -1
  7. package/dist/definition-registry.js +9 -0
  8. package/dist/index.d.ts +10 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -2
  11. package/dist/invocation-contract.d.ts +21 -13
  12. package/dist/invocation-contract.d.ts.map +1 -1
  13. package/dist/invocation-contract.js +43 -46
  14. package/dist/loaded-types.d.ts +13 -0
  15. package/dist/loaded-types.d.ts.map +1 -1
  16. package/dist/manifest-loader.d.ts +8 -1
  17. package/dist/manifest-loader.d.ts.map +1 -1
  18. package/dist/manifest-loader.js +33 -6
  19. package/dist/migrations/driver.d.ts +69 -0
  20. package/dist/migrations/driver.d.ts.map +1 -0
  21. package/dist/migrations/driver.js +200 -0
  22. package/dist/migrations/entries/index.d.ts +3 -0
  23. package/dist/migrations/entries/index.d.ts.map +1 -0
  24. package/dist/migrations/entries/index.js +9 -0
  25. package/dist/migrations/entries/normalize-value-types.json +48 -0
  26. package/dist/migrations/entries/ref-slot-scalar-type.json +20 -0
  27. package/dist/migrations/entry-data.d.ts +25 -0
  28. package/dist/migrations/entry-data.d.ts.map +1 -0
  29. package/dist/migrations/entry-data.js +171 -0
  30. package/dist/migrations/index.d.ts +22 -0
  31. package/dist/migrations/index.d.ts.map +1 -0
  32. package/dist/migrations/index.js +19 -0
  33. package/dist/migrations/match.d.ts +162 -0
  34. package/dist/migrations/match.d.ts.map +1 -0
  35. package/dist/migrations/match.js +295 -0
  36. package/dist/migrations/patch.d.ts +68 -0
  37. package/dist/migrations/patch.d.ts.map +1 -0
  38. package/dist/migrations/patch.js +236 -0
  39. package/dist/migrations/provenance.d.ts +25 -0
  40. package/dist/migrations/provenance.d.ts.map +1 -0
  41. package/dist/migrations/provenance.js +118 -0
  42. package/dist/migrations/registry.d.ts +35 -0
  43. package/dist/migrations/registry.d.ts.map +1 -0
  44. package/dist/migrations/registry.js +35 -0
  45. package/dist/migrations/report.d.ts +28 -0
  46. package/dist/migrations/report.d.ts.map +1 -0
  47. package/dist/migrations/report.js +144 -0
  48. package/dist/migrations/types.d.ts +115 -0
  49. package/dist/migrations/types.d.ts.map +1 -0
  50. package/dist/migrations/types.js +24 -0
  51. package/dist/migrations/yaml-edit.d.ts +21 -0
  52. package/dist/migrations/yaml-edit.d.ts.map +1 -0
  53. package/dist/migrations/yaml-edit.js +245 -0
  54. package/dist/parse-loaded-file.d.ts +8 -0
  55. package/dist/parse-loaded-file.d.ts.map +1 -1
  56. package/dist/parse-loaded-file.js +22 -13
  57. package/dist/resolve-schema-type-refs.d.ts +29 -12
  58. package/dist/resolve-schema-type-refs.d.ts.map +1 -1
  59. package/dist/resolve-schema-type-refs.js +89 -16
  60. package/dist/schema-compat.d.ts +35 -9
  61. package/dist/schema-compat.d.ts.map +1 -1
  62. package/dist/schema-compat.js +180 -64
  63. package/dist/schema-region.d.ts +33 -0
  64. package/dist/schema-region.d.ts.map +1 -0
  65. package/dist/schema-region.js +45 -0
  66. package/dist/types.d.ts +17 -0
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/validate-cel-context.d.ts.map +1 -1
  69. package/dist/validate-cel-context.js +29 -3
  70. package/dist/validate-step-inputs.d.ts +8 -1
  71. package/dist/validate-step-inputs.d.ts.map +1 -1
  72. package/dist/validate-step-inputs.js +47 -2
  73. package/dist/validate-value-type-slots.d.ts +43 -0
  74. package/dist/validate-value-type-slots.d.ts.map +1 -0
  75. package/dist/validate-value-type-slots.js +142 -0
  76. package/dist/value-type-keyword.d.ts +60 -0
  77. package/dist/value-type-keyword.d.ts.map +1 -0
  78. package/dist/value-type-keyword.js +119 -0
  79. package/dist/yaml-source-edit.d.ts +56 -0
  80. package/dist/yaml-source-edit.d.ts.map +1 -0
  81. package/dist/yaml-source-edit.js +93 -0
  82. package/package.json +3 -3
  83. package/src/analyzer.ts +99 -6
  84. package/src/cel-environment.ts +6 -3
  85. package/src/definition-registry.ts +10 -0
  86. package/src/index.ts +26 -2
  87. package/src/invocation-contract.ts +43 -48
  88. package/src/loaded-types.ts +13 -0
  89. package/src/manifest-loader.ts +37 -6
  90. package/src/migrations/driver.ts +262 -0
  91. package/src/migrations/entries/index.ts +10 -0
  92. package/src/migrations/entries/normalize-value-types.json +48 -0
  93. package/src/migrations/entries/ref-slot-scalar-type.json +20 -0
  94. package/src/migrations/entry-data.ts +207 -0
  95. package/src/migrations/index.ts +22 -0
  96. package/src/migrations/match.ts +382 -0
  97. package/src/migrations/patch.ts +265 -0
  98. package/src/migrations/provenance.ts +167 -0
  99. package/src/migrations/registry.ts +40 -0
  100. package/src/migrations/report.ts +169 -0
  101. package/src/migrations/types.ts +144 -0
  102. package/src/migrations/yaml-edit.ts +260 -0
  103. package/src/parse-loaded-file.ts +31 -12
  104. package/src/resolve-schema-type-refs.ts +93 -16
  105. package/src/schema-compat.ts +212 -74
  106. package/src/schema-region.ts +46 -0
  107. package/src/types.ts +17 -0
  108. package/src/validate-cel-context.ts +36 -3
  109. package/src/validate-step-inputs.ts +56 -1
  110. package/src/validate-value-type-slots.ts +182 -0
  111. package/src/value-type-keyword.ts +135 -0
  112. package/src/yaml-source-edit.ts +106 -0
  113. package/dist/binary-slot.d.ts +0 -39
  114. package/dist/binary-slot.d.ts.map +0 -1
  115. package/dist/binary-slot.js +0 -58
  116. package/src/binary-slot.ts +0 -71
@@ -0,0 +1,171 @@
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 { DiagnosticSeverity } from "../types.js";
19
+ import { readMigrationMatch } from "./match.js";
20
+ import { MIGRATION_OPS } from "./types.js";
21
+ /** How an entry file names a severity. The LSP integers are a transport detail
22
+ * of one editor; an entry is read by a Rust crate too. */
23
+ const SEVERITIES = {
24
+ error: DiagnosticSeverity.Error,
25
+ warning: DiagnosticSeverity.Warning,
26
+ info: DiagnosticSeverity.Information,
27
+ hint: DiagnosticSeverity.Hint,
28
+ };
29
+ class MigrationEntryError extends Error {
30
+ constructor(file, detail) {
31
+ super(`Invalid migration entry '${file}': ${detail}`);
32
+ this.name = "MigrationEntryError";
33
+ }
34
+ }
35
+ function isPlainObject(value) {
36
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37
+ }
38
+ function requireString(file, node, key) {
39
+ const value = node[key];
40
+ if (typeof value !== "string" || value.length === 0) {
41
+ throw new MigrationEntryError(file, `'${key}' must be a non-empty string`);
42
+ }
43
+ return value;
44
+ }
45
+ const OPERATION_KEYS = {
46
+ "rename-key": ["to"],
47
+ "set-value": ["value", "qualify"],
48
+ "set-tag": ["tag"],
49
+ "insert-item": ["value", "at"],
50
+ "remove-entry": [],
51
+ };
52
+ /** Top-level entry keys. `$comment` is the sanctioned place for author notes —
53
+ * the reader ignores it, but it has to be DECLARED, or "unknown keys are
54
+ * refused" would be true of every level but this one. */
55
+ const ENTRY_KEYS = ["id", "code", "severity", "reason", "rules", "$comment"];
56
+ /**
57
+ * A value a patch may write.
58
+ *
59
+ * Scalars only, because "every operation has a known YAML edit form" is what
60
+ * makes a migration applicable to a FILE, and the file applier renders a value
61
+ * by re-quoting it in the author's own style at the node's own span — which has
62
+ * no meaning for a mapping or a sequence. Without this check the limitation is
63
+ * invisible until a user runs `telo migrate` and is told, permanently, to fix
64
+ * it by hand; with it, the entry's author learns at authoring time. Structured
65
+ * values are a vocabulary extension (a block renderer), not a silent gap.
66
+ */
67
+ function requireScalarValue(file, index, op, value) {
68
+ if (value === null ||
69
+ typeof value === "string" ||
70
+ typeof value === "number" ||
71
+ typeof value === "boolean") {
72
+ return;
73
+ }
74
+ throw new MigrationEntryError(file, `patch[${index}] ('${op}') value must be a scalar (string, number, boolean or null) — ` +
75
+ `a mapping or sequence has no in-place YAML edit form, so \`telo migrate\` could never apply it`);
76
+ }
77
+ function readOperation(file, raw, index) {
78
+ if (!isPlainObject(raw)) {
79
+ throw new MigrationEntryError(file, `patch[${index}] must be a mapping`);
80
+ }
81
+ const op = raw.op;
82
+ if (typeof op !== "string" || !MIGRATION_OPS.includes(op)) {
83
+ throw new MigrationEntryError(file, `patch[${index}].op '${String(op)}' is not one of ${MIGRATION_OPS.join(", ")}`);
84
+ }
85
+ // The vocabulary is closed, so an unknown parameter is a typo — and a typo in
86
+ // a patch is a rewrite that quietly does something other than what it reads
87
+ // as. `set-value`'s two parameters are alternatives, checked below.
88
+ const allowed = new Set([...OPERATION_KEYS[op], "op"]);
89
+ for (const key of Object.keys(raw)) {
90
+ if (!allowed.has(key)) {
91
+ throw new MigrationEntryError(file, `patch[${index}] ('${op}') has no parameter '${key}'`);
92
+ }
93
+ }
94
+ switch (op) {
95
+ case "rename-key":
96
+ return { op, to: requireString(file, raw, "to") };
97
+ case "set-value": {
98
+ const hasValue = Object.hasOwn(raw, "value");
99
+ const hasQualify = Object.hasOwn(raw, "qualify");
100
+ if (hasValue === hasQualify) {
101
+ throw new MigrationEntryError(file, `patch[${index}] ('set-value') takes exactly one of 'value' or 'qualify'`);
102
+ }
103
+ if (hasQualify)
104
+ return { op, qualify: requireString(file, raw, "qualify") };
105
+ requireScalarValue(file, index, op, raw.value);
106
+ return { op, value: raw.value };
107
+ }
108
+ case "set-tag":
109
+ return { op, tag: requireString(file, raw, "tag") };
110
+ case "insert-item": {
111
+ if (!Object.hasOwn(raw, "value")) {
112
+ throw new MigrationEntryError(file, `patch[${index}] ('insert-item') requires 'value'`);
113
+ }
114
+ requireScalarValue(file, index, op, raw.value);
115
+ const at = raw.at;
116
+ if (at !== undefined && (typeof at !== "number" || !Number.isInteger(at) || at < 0)) {
117
+ throw new MigrationEntryError(file, `patch[${index}].at must be a non-negative integer when present`);
118
+ }
119
+ return at === undefined ? { op, value: raw.value } : { op, value: raw.value, at };
120
+ }
121
+ case "remove-entry":
122
+ return { op };
123
+ default:
124
+ throw new MigrationEntryError(file, `patch[${index}].op '${op}' is unhandled`);
125
+ }
126
+ }
127
+ function readRule(file, raw, index) {
128
+ if (!isPlainObject(raw)) {
129
+ throw new MigrationEntryError(file, `rules[${index}] must be a mapping`);
130
+ }
131
+ if (!Array.isArray(raw.patch) || raw.patch.length === 0) {
132
+ throw new MigrationEntryError(file, `rules[${index}].patch must be a non-empty sequence`);
133
+ }
134
+ return {
135
+ match: readMigrationMatch(`Invalid migration entry '${file}': rules[${index}]`, raw.match),
136
+ patch: raw.patch.map((op, i) => readOperation(file, op, i)),
137
+ };
138
+ }
139
+ /**
140
+ * Read one entry file's parsed data into a `MigrationEntry`.
141
+ *
142
+ * `file` names the entry file, so a failure says which one.
143
+ */
144
+ export function parseMigrationEntry(file, data) {
145
+ if (!isPlainObject(data)) {
146
+ throw new MigrationEntryError(file, "an entry must be a mapping");
147
+ }
148
+ // Closed at every level, this one included: a typo'd top-level key would
149
+ // otherwise be silently ignored, which is the one failure a rewrite-on-load
150
+ // design cannot afford once module-shipped entries make this a trust boundary.
151
+ for (const key of Object.keys(data)) {
152
+ if (!ENTRY_KEYS.includes(key)) {
153
+ throw new MigrationEntryError(file, `an entry has no key '${key}'. Known keys: ${ENTRY_KEYS.join(", ")}.`);
154
+ }
155
+ }
156
+ const severityName = requireString(file, data, "severity");
157
+ const severity = SEVERITIES[severityName];
158
+ if (severity === undefined) {
159
+ throw new MigrationEntryError(file, `severity '${severityName}' must be one of ${Object.keys(SEVERITIES).join(", ")}`);
160
+ }
161
+ if (!Array.isArray(data.rules) || data.rules.length === 0) {
162
+ throw new MigrationEntryError(file, "'rules' must be a non-empty sequence");
163
+ }
164
+ return {
165
+ id: requireString(file, data, "id"),
166
+ code: requireString(file, data, "code"),
167
+ severity,
168
+ reason: requireString(file, data, "reason").trim(),
169
+ rules: data.rules.map((rule, i) => readRule(file, rule, i)),
170
+ };
171
+ }
@@ -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
+ export { migrateFileText, migrateManifests, NO_MIGRATIONS } from "./driver.js";
17
+ export type { FileMigrations } from "./driver.js";
18
+ export { parseMigrationEntry } from "./entry-data.js";
19
+ export { remapMigratedPaths } from "./provenance.js";
20
+ export { CORE_MIGRATIONS } from "./registry.js";
21
+ export type { MigrationEntry, MigrationRewrite } from "./types.js";
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;uEAcuE;AAEvE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,19 @@
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
+ export { migrateFileText, migrateManifests, NO_MIGRATIONS } from "./driver.js";
17
+ export { parseMigrationEntry } from "./entry-data.js";
18
+ export { remapMigratedPaths } from "./provenance.js";
19
+ export { CORE_MIGRATIONS } from "./registry.js";
@@ -0,0 +1,162 @@
1
+ /** The selector half of a migration: what a rule MATCHES, as data.
2
+ *
3
+ * A patch addresses a known location; a migration has to find *every*
4
+ * occurrence of a legacy spelling, so selection is the half a plain patch
5
+ * format has none of. It is declarative for the same reason the operations
6
+ * are: an entry travels to a Rust and a Go kernel, and a predicate expressed
7
+ * in one language would mean one artifact is read two ways — invisibly, since
8
+ * a migration that succeeds is silent.
9
+ *
10
+ * **Containment is POSITIVE and required.** A rule states which document kinds
11
+ * it may touch (`inKind`) and which region of those documents it may reach
12
+ * into (`under`); nothing outside is reachable. The alternative — walk
13
+ * everything and subtract — cannot be made sound, because the set to subtract
14
+ * is unbounded: a `Run.Value` value, an `Assert.Equals` expected, any kind
15
+ * whose config carries a user JSON blob can hold something shaped like the
16
+ * node a rule looks for, and forgetting one corrupts a manifest with no
17
+ * diagnostic. A denylist also cannot express the guarantee the module surface
18
+ * is promised to carry — *a dependency can rename its own field and provably
19
+ * nothing else* — which is a statement about what a rule may reach, so it has
20
+ * to be said positively. `notUnder` remains for subtracting inside a region a
21
+ * rule legitimately reaches, which is a narrowing, not the containment itself.
22
+ *
23
+ * **`under` is ANCHORED at the document root**, not a set of key names to look
24
+ * for anywhere on the path. Anchoring is what makes the containment claim
25
+ * true: a `Telo.Definition`'s `resources:` template body carries other kinds'
26
+ * configuration, and any of it may hold a key spelled `schema` over data that
27
+ * merely looks like a schema — so "some segment of the path is `schema`" would
28
+ * reach the very user JSON blob the positive form exists to keep out, and
29
+ * would delete from it silently. Anchored, `under` names top-level document
30
+ * keys and a region is a genuine subtree.
31
+ *
32
+ * **`inSchema` is the one region a kind list cannot name.** An annotation
33
+ * keyword occurs in author-written JSON Schema, and schema fragments are not
34
+ * confined to kind documents: an inline `inputType:` / `outputType:` on ANY kind
35
+ * that declares one, an API route's `request.schema.body`, a `Telo.JsonSchema`'s
36
+ * `schema`. That set of kinds is open — any kind may declare a schema-valued
37
+ * field — and enumerating the standard library's would put resource-kind
38
+ * knowledge into the analyzer, against the topology-driven constraint. So a rule
39
+ * may instead state that it reaches only inside a SCHEMA REGION, named by the
40
+ * KERNEL's own schema-valued keys, which no kind owns.
41
+ *
42
+ * With `inSchema`, and only with it, `inKind` / `under` may be `["*"]` — and
43
+ * only for a rule keyed on a reserved `x-telo-*` annotation. That pairing IS the
44
+ * containment: the region gate bounds where the walk may go, and the reserved-key
45
+ * rule bounds what it may touch, since an `x-telo-*` key is Telo vocabulary
46
+ * wherever it appears and cannot mean something else in someone's config. Both
47
+ * halves are refused at entry-read time, so a module-shipped entry can no more
48
+ * spell `"*"` than it can name another module's kind.
49
+ *
50
+ * The residue is stated rather than claimed away: a manifest that asserts ABOUT
51
+ * a schema — a schema literal under a key spelled `schema` inside an assertion's
52
+ * expected value — is reachable, and would be rewritten into its own synonym.
53
+ * That cannot be closed in a data-only matcher without naming kinds. It is
54
+ * accepted because the sites the wildcards reach are exactly the ones no
55
+ * enumeration covers, and the alternative leaves an author reading a deprecation
56
+ * `telo migrate` refuses to act on.
57
+ *
58
+ * The vocabulary is closed, which is what makes it a trust boundary once
59
+ * module-shipped entries are aggregated beside core ones. An unrecognized key
60
+ * is refused rather than ignored — a selector that silently matches wider than
61
+ * it reads is the one failure this cannot tolerate. */
62
+ import type { MigrationPath } from "./types.js";
63
+ /** The wildcard `inKind` / `under` value. Legal only alongside `inSchema`, and
64
+ * only for a rule keyed on an `x-telo-*` annotation. */
65
+ export declare const MATCH_ANY = "*";
66
+ export interface MigrationMatch {
67
+ /** The mapping key this rule rewrites. */
68
+ readonly key: string;
69
+ /** Document `kind:` values this rule may match in. Required and non-empty:
70
+ * a rule that does not say which documents it touches cannot be reasoned
71
+ * about, and is exactly the rule that reaches into a resource's config. */
72
+ readonly inKind: readonly string[];
73
+ /** The region of the document this rule may reach, named by TOP-LEVEL
74
+ * document keys: the matched node must be AT or BELOW one of them. Required
75
+ * and non-empty, for the same reason as `inKind`.
76
+ *
77
+ * Anchored at the root rather than matched anywhere on the path — see the
78
+ * file header; an unanchored `under` reaches into a nested resource's own
79
+ * configuration and is not containment at all.
80
+ *
81
+ * At-or-below rather than strictly-below so the vocabulary is complete — a
82
+ * rule that rewrites a top-level key names that key, instead of the region
83
+ * being unreachable and the grammar needing a second spelling for the
84
+ * document root. */
85
+ readonly under: readonly string[];
86
+ /** The value must deep-equal this. Mutually exclusive with `valueOneOf`. */
87
+ readonly value?: unknown;
88
+ /** The value must be one of these. Matching against the KNOWN legacy values
89
+ * rather than any value is what leaves an unrecognized one alone for the
90
+ * ordinary validator to report, instead of silently rewriting it. */
91
+ readonly valueOneOf?: readonly unknown[];
92
+ /** A key that must be present in the same mapping. The matched key is often
93
+ * stale only *because* of what sits beside it. */
94
+ readonly withSibling?: string;
95
+ /** Ancestor keys that disqualify a match inside the region `under` allows.
96
+ * The data-bearing JSON Schema keywords (`const`, `default`, `enum`,
97
+ * `examples`) hold values that may look like schemas. */
98
+ readonly notUnder?: readonly string[];
99
+ /** Narrow to a JSON Schema region: the match must be at or below a node
100
+ * reached through one of {@link SCHEMA_REGION_KEYS}.
101
+ *
102
+ * A second bound, never a replacement for `inKind` / `under` — it is what
103
+ * makes their wildcard forms safe, and it narrows an enumerated region just as
104
+ * usefully. See the file header for why an annotation rename needs it. */
105
+ readonly inSchema?: boolean;
106
+ }
107
+ export declare const MATCH_KEYS: readonly ["key", "inKind", "under", "value", "valueOneOf", "withSibling", "notUnder", "inSchema"];
108
+ /** Structural equality over JSON values — what `value` / `valueOneOf` compare
109
+ * with, so a match on `true` never also matches `"true"`.
110
+ *
111
+ * Exported because the PATCHER asks the same question in the other direction:
112
+ * "is the value already what this would write". Both answers have to come from
113
+ * one rule, or a rule could match a spelling the patch then declares current. */
114
+ export declare function deepEquals(a: unknown, b: unknown): boolean;
115
+ /** Read a rule's `match` block, refusing anything the vocabulary does not
116
+ * define. `describe` names the entry and rule so a failure says which. */
117
+ export declare function readMigrationMatch(describe: string, raw: unknown): MigrationMatch;
118
+ /** One candidate site: a mapping entry whose key some rule is interested in. */
119
+ interface MatchSite {
120
+ readonly path: MigrationPath;
121
+ readonly value: unknown;
122
+ readonly parent: Record<string, unknown>;
123
+ }
124
+ /** Every candidate site in one document, keyed by mapping key.
125
+ *
126
+ * Built ONCE per document and shared by every rule that can apply to it,
127
+ * because the walk is the expensive part and it does not depend on the rule —
128
+ * this is on the kernel's boot path for every file in the graph, so a walk per
129
+ * rule would scale the cost of loading any manifest with the size of the
130
+ * migration set.
131
+ *
132
+ * The walk is bounded by the same containment the rules declare: `roots` is
133
+ * the union of the applicable rules' `under`, so a region no rule can reach is
134
+ * never descended into and a document no rule can match is never walked at
135
+ * all. Only keys some rule asked for allocate a path array. */
136
+ export type MatchIndex = ReadonlyMap<string, readonly MatchSite[]>;
137
+ /**
138
+ * Index `document`'s candidate sites for `keys`, descending only into the
139
+ * top-level regions named by `roots`.
140
+ *
141
+ * The caller has already gated on `inKind` — see `selectMatches`, which repeats
142
+ * the check because it holds the individual rule.
143
+ */
144
+ export declare function buildMatchIndex(document: unknown, keys: ReadonlySet<string>, roots: ReadonlySet<string>): MatchIndex;
145
+ /** The rules in `entries` that may match a document of `kind`, with the key and
146
+ * root sets their shared index needs. Empty rules mean the document is not
147
+ * walked at all. */
148
+ export declare function applicableRules<T extends {
149
+ readonly match: MigrationMatch;
150
+ }>(rules: Iterable<T>, kind: unknown): {
151
+ rules: T[];
152
+ keys: Set<string>;
153
+ roots: Set<string>;
154
+ };
155
+ /** The sites in `index` this match selects, in document order. */
156
+ export declare function selectMatches(index: MatchIndex, document: unknown, match: MigrationMatch): MigrationPath[];
157
+ /** Convenience for a single match against a document — builds a one-rule index.
158
+ * The driver uses `applicableRules` + `buildMatchIndex` + `selectMatches` so
159
+ * one walk serves every rule that can reach the document. */
160
+ export declare function findMatches(document: unknown, match: MigrationMatch): MigrationPath[];
161
+ export {};
162
+ //# sourceMappingURL=match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/migrations/match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wDA4DwD;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;yDACyD;AACzD,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;gFAE4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;;;;;;;yBAWqB;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB;;0EAEsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACzC;uDACmD;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;8DAE0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;+EAK2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,UAAU,mGASb,CAAC;AAMX;;;;;kFAKkF;AAClF,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAY1D;AAUD;2EAC2E;AAC3E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,cAAc,CA6EjF;AAED,gFAAgF;AAChF,UAAU,SAAS;IACjB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;gEAWgE;AAChE,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,OAAO,EACjB,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EACzB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB,UAAU,CA6CZ;AAED;;qBAEqB;AACrB,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;CAAE,EAC1E,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAClB,IAAI,EAAE,OAAO,GACZ;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAYvD;AAQD,kEAAkE;AAClE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,cAAc,GACpB,aAAa,EAAE,CA4BjB;AAED;;8DAE8D;AAC9D,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,aAAa,EAAE,CAGrF"}