@telorun/analyzer 0.57.0 → 0.59.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 +11 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -2
  11. package/dist/invocation-contract.d.ts +58 -13
  12. package/dist/invocation-contract.d.ts.map +1 -1
  13. package/dist/invocation-contract.js +123 -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 +53 -9
  61. package/dist/schema-compat.d.ts.map +1 -1
  62. package/dist/schema-compat.js +245 -66
  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 +97 -15
  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 +34 -3
  87. package/src/invocation-contract.ts +134 -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 +277 -75
  106. package/src/schema-region.ts +46 -0
  107. package/src/types.ts +17 -0
  108. package/src/validate-cel-context.ts +117 -15
  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,106 @@
1
+ /** Writing a value back into YAML SOURCE — the two primitives every in-place
2
+ * repair is built from, and the one place their rules are stated.
3
+ *
4
+ * A repair is a byte splice over the author's own text, never
5
+ * `Document.toString()`: re-serializing re-folds block scalars, drops
6
+ * quote-style hints and reflows long strings, so a one-key edit would arrive
7
+ * as a whole-file diff. That leaves two questions every writer has to answer
8
+ * identically — how a value is re-quoted to occupy a span, and how overlapping
9
+ * spans are spliced — and three surfaces asking them: the editors' quick fix
10
+ * (`@telorun/ide-support`), `telo migrate` (the migration driver), and
11
+ * `telo upgrade`'s pin rewrite.
12
+ *
13
+ * They live HERE, in the lowest package of the three, because they are pure
14
+ * string work with no Node dependency and because a copy per surface is a
15
+ * silent-divergence risk of exactly the kind Telo's cross-cutting primitives
16
+ * rule exists to prevent: two writers editing the same file from two copies of
17
+ * a subtle quoting rule will eventually quote one value two ways, and nothing
18
+ * would catch it. Same precedent as `ref-slot.ts` / `binary-slot.ts` — a rule
19
+ * several surfaces must agree on gets one reader. */
20
+
21
+ /** Characters that make a plain (unquoted) YAML scalar reparse as something
22
+ * else. `-` and `?` are indicators only when followed by a space, so they are
23
+ * handled by the leading-token check rather than listed here. */
24
+ const PLAIN_UNSAFE_LEAD = new Set([
25
+ "&", "*", "!", "|", ">", "%", "@", "`", "#", "'", '"', "{", "[", "}", "]", ",",
26
+ ]);
27
+
28
+ /** Whether `value` can be written as a plain scalar without changing meaning. */
29
+ export function isPlainSafe(value: string): boolean {
30
+ if (value === "" || value.trim() !== value) return false;
31
+ if (PLAIN_UNSAFE_LEAD.has(value[0]!)) return false;
32
+ // `-`/`?`/`:` lead only matter when a space follows — `-x` is a scalar,
33
+ // `- x` is a sequence entry.
34
+ if (/^[-?:]\s/.test(value)) return false;
35
+ // A colon-space anywhere opens a mapping; a space-hash opens a comment.
36
+ if (value.includes(": ") || value.includes(" #")) return false;
37
+ if (value.endsWith(":")) return false;
38
+ return !/[\n\r]/.test(value);
39
+ }
40
+
41
+ /** Quote style of the source text a repair is replacing. */
42
+ export type QuoteStyle = "double" | "single" | "plain";
43
+
44
+ export function quoteStyleOf(source: string): QuoteStyle {
45
+ if (source.length >= 2 && source.startsWith('"') && source.endsWith('"')) return "double";
46
+ if (source.length >= 2 && source.startsWith("'") && source.endsWith("'")) return "single";
47
+ return "plain";
48
+ }
49
+
50
+ /** Render `replacement` so it occupies `originalSource`'s span as the same
51
+ * scalar the author would have written by hand, or `undefined` when the span
52
+ * cannot be rewritten safely.
53
+ *
54
+ * A plain original is kept plain when it can be — rewriting `Run.Sequenc` to
55
+ * `"Run.Sequence"` would be a correct but noisy diff on a kind name — and
56
+ * promoted to double quotes when the new value would not survive unquoted.
57
+ *
58
+ * **A multi-line span is refused.** A block scalar's span covers its `|`/`>-`
59
+ * indicator AND its trailing newline, so writing a single-line scalar over it
60
+ * deletes the line break that ended the mapping entry and glues the next key
61
+ * onto the value — the document stops parsing. Re-emitting a block scalar
62
+ * correctly needs the node's indentation, which no consumer of this function
63
+ * has. A multi-line REPLACEMENT is refused for the mirror reason: its
64
+ * continuation lines would land at column 0, which is not a legal mapping
65
+ * value. A quick fix promises a repair that can be applied without review, so
66
+ * the only honest answer for these is no repair. */
67
+ export function renderFixReplacement(
68
+ originalSource: string,
69
+ replacement: string,
70
+ ): string | undefined {
71
+ if (/[\n\r]/.test(originalSource) || /[\n\r]/.test(replacement)) return undefined;
72
+ const style = quoteStyleOf(originalSource);
73
+
74
+ if (style === "single") {
75
+ // A single-quoted YAML scalar escapes only the quote, by doubling it. CEL
76
+ // string literals use single quotes constantly, so this is the common case
77
+ // for an expression written in a single-quoted scalar.
78
+ return `'${replacement.replaceAll("'", "''")}'`;
79
+ }
80
+ if (style === "double" || !isPlainSafe(replacement)) {
81
+ return `"${replacement.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
82
+ }
83
+ return replacement;
84
+ }
85
+
86
+ /** A splice over a source file: replace `[start, end)` with `newText`. An empty
87
+ * span is a pure insertion. */
88
+ export interface TextEdit {
89
+ /** Byte offset of the first replaced character (inclusive). */
90
+ readonly start: number;
91
+ /** Byte offset one past the last replaced character. */
92
+ readonly end: number;
93
+ readonly newText: string;
94
+ }
95
+
96
+ /** Splice `edits` into `text`, right to left so earlier offsets stay valid.
97
+ * Callers are responsible for edits not overlapping; two splices contending
98
+ * for the same bytes cannot both be honoured, and this does not arbitrate. */
99
+ export function applyTextEdits(text: string, edits: readonly TextEdit[]): string {
100
+ if (edits.length === 0) return text;
101
+ let out = text;
102
+ for (const edit of [...edits].sort((a, b) => b.start - a.start)) {
103
+ out = out.slice(0, edit.start) + edit.newText + out.slice(edit.end);
104
+ }
105
+ return out;
106
+ }
@@ -1,39 +0,0 @@
1
- import type { KeywordDefinition } from "ajv";
2
- /**
3
- * `x-telo-binary: true` — the annotation that gives a byte buffer a declared
4
- * identity, and the single accessor every surface reads it through (the
5
- * `ref-slot.ts` precedent).
6
- *
7
- * WHY AN ANNOTATION RATHER THAN A TYPE. Bytes are not expressible in JSON
8
- * Schema's type vocabulary. `type: object` is the closest fit and it is a lie
9
- * that costs a real check: every arbitrary object satisfies it, so a mistyped
10
- * `content: { foo: bar }` reaches the controller instead of failing statically.
11
- * `type: binary` would fix the honesty and break everything else — AJV refuses
12
- * to compile an unknown type at all (`type must be JSONType or JSONType[]`),
13
- * with no option to allow it, so the schema would produce no validator; and a
14
- * published `telo.yaml` would stop being JSON Schema, which is what the hub, the
15
- * editor and any third-party reader consume. An `x-` keyword is ignored by
16
- * unaware tooling BY SPECIFICATION, so it degrades to "unconstrained" rather
17
- * than to "cannot validate this kind at all".
18
- *
19
- * ONE RULE, BOTH HALVES. The keyword is implemented as AJV CODEGEN rather than a
20
- * `validate` function so it inlines into standalone-compiled validators — the
21
- * kernel compiles and caches those, and a function-valued keyword would not
22
- * survive serialization. The analyzer registers the identical keyword, and
23
- * `celPlaceholderForSchema` hands a real `Uint8Array` to a CEL leaf at a binary
24
- * slot, so static and runtime agree by construction instead of by two rules kept
25
- * in step. What falls out is the check the annotation exists for: **bytes can
26
- * never be manifest-authored** — no YAML literal is a `Uint8Array` — so a literal
27
- * at a binary slot is rejected while a value arriving by reference passes.
28
- */
29
- export declare const X_TELO_BINARY = "x-telo-binary";
30
- /** True when this schema node declares its value to be raw bytes. */
31
- export declare function isBinarySlot(schema: unknown): boolean;
32
- /**
33
- * The AJV keyword. `Uint8Array` is the structural test rather than any richer
34
- * notion of "binary": it is what every byte producer in the runtime hands over
35
- * (`Buffer` extends it, so Node buffers pass), and it is the one check that means
36
- * the same thing in a browser-side analyzer and in the kernel.
37
- */
38
- export declare function binaryKeyword(): KeywordDefinition;
39
- //# sourceMappingURL=binary-slot.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"binary-slot.d.ts","sourceRoot":"","sources":["../src/binary-slot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,KAAK,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAMrD;AAWD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,iBAAiB,CAajD"}
@@ -1,58 +0,0 @@
1
- import * as AjvNS from "ajv";
2
- /**
3
- * `x-telo-binary: true` — the annotation that gives a byte buffer a declared
4
- * identity, and the single accessor every surface reads it through (the
5
- * `ref-slot.ts` precedent).
6
- *
7
- * WHY AN ANNOTATION RATHER THAN A TYPE. Bytes are not expressible in JSON
8
- * Schema's type vocabulary. `type: object` is the closest fit and it is a lie
9
- * that costs a real check: every arbitrary object satisfies it, so a mistyped
10
- * `content: { foo: bar }` reaches the controller instead of failing statically.
11
- * `type: binary` would fix the honesty and break everything else — AJV refuses
12
- * to compile an unknown type at all (`type must be JSONType or JSONType[]`),
13
- * with no option to allow it, so the schema would produce no validator; and a
14
- * published `telo.yaml` would stop being JSON Schema, which is what the hub, the
15
- * editor and any third-party reader consume. An `x-` keyword is ignored by
16
- * unaware tooling BY SPECIFICATION, so it degrades to "unconstrained" rather
17
- * than to "cannot validate this kind at all".
18
- *
19
- * ONE RULE, BOTH HALVES. The keyword is implemented as AJV CODEGEN rather than a
20
- * `validate` function so it inlines into standalone-compiled validators — the
21
- * kernel compiles and caches those, and a function-valued keyword would not
22
- * survive serialization. The analyzer registers the identical keyword, and
23
- * `celPlaceholderForSchema` hands a real `Uint8Array` to a CEL leaf at a binary
24
- * slot, so static and runtime agree by construction instead of by two rules kept
25
- * in step. What falls out is the check the annotation exists for: **bytes can
26
- * never be manifest-authored** — no YAML literal is a `Uint8Array` — so a literal
27
- * at a binary slot is rejected while a value arriving by reference passes.
28
- */
29
- export const X_TELO_BINARY = "x-telo-binary";
30
- /** True when this schema node declares its value to be raw bytes. */
31
- export function isBinarySlot(schema) {
32
- return (typeof schema === "object" &&
33
- schema !== null &&
34
- schema[X_TELO_BINARY] === true);
35
- }
36
- const ajvExports = AjvNS.default ?? AjvNS;
37
- const codegen = ajvExports._ ?? AjvNS._;
38
- /**
39
- * The AJV keyword. `Uint8Array` is the structural test rather than any richer
40
- * notion of "binary": it is what every byte producer in the runtime hands over
41
- * (`Buffer` extends it, so Node buffers pass), and it is the one check that means
42
- * the same thing in a browser-side analyzer and in the kernel.
43
- */
44
- export function binaryKeyword() {
45
- return {
46
- keyword: X_TELO_BINARY,
47
- schemaType: "boolean",
48
- code(cxt) {
49
- // `x-telo-binary: false` states nothing, so it constrains nothing.
50
- if (cxt.schema !== true)
51
- return;
52
- cxt.pass(codegen `${cxt.data} instanceof Uint8Array`);
53
- },
54
- error: {
55
- message: "must be raw bytes (a Uint8Array) — bytes cannot be written inline in a manifest",
56
- },
57
- };
58
- }
@@ -1,71 +0,0 @@
1
- import * as AjvNS from "ajv";
2
- import type { KeywordDefinition } from "ajv";
3
-
4
- /**
5
- * `x-telo-binary: true` — the annotation that gives a byte buffer a declared
6
- * identity, and the single accessor every surface reads it through (the
7
- * `ref-slot.ts` precedent).
8
- *
9
- * WHY AN ANNOTATION RATHER THAN A TYPE. Bytes are not expressible in JSON
10
- * Schema's type vocabulary. `type: object` is the closest fit and it is a lie
11
- * that costs a real check: every arbitrary object satisfies it, so a mistyped
12
- * `content: { foo: bar }` reaches the controller instead of failing statically.
13
- * `type: binary` would fix the honesty and break everything else — AJV refuses
14
- * to compile an unknown type at all (`type must be JSONType or JSONType[]`),
15
- * with no option to allow it, so the schema would produce no validator; and a
16
- * published `telo.yaml` would stop being JSON Schema, which is what the hub, the
17
- * editor and any third-party reader consume. An `x-` keyword is ignored by
18
- * unaware tooling BY SPECIFICATION, so it degrades to "unconstrained" rather
19
- * than to "cannot validate this kind at all".
20
- *
21
- * ONE RULE, BOTH HALVES. The keyword is implemented as AJV CODEGEN rather than a
22
- * `validate` function so it inlines into standalone-compiled validators — the
23
- * kernel compiles and caches those, and a function-valued keyword would not
24
- * survive serialization. The analyzer registers the identical keyword, and
25
- * `celPlaceholderForSchema` hands a real `Uint8Array` to a CEL leaf at a binary
26
- * slot, so static and runtime agree by construction instead of by two rules kept
27
- * in step. What falls out is the check the annotation exists for: **bytes can
28
- * never be manifest-authored** — no YAML literal is a `Uint8Array` — so a literal
29
- * at a binary slot is rejected while a value arriving by reference passes.
30
- */
31
-
32
- export const X_TELO_BINARY = "x-telo-binary";
33
-
34
- /** True when this schema node declares its value to be raw bytes. */
35
- export function isBinarySlot(schema: unknown): boolean {
36
- return (
37
- typeof schema === "object" &&
38
- schema !== null &&
39
- (schema as Record<string, unknown>)[X_TELO_BINARY] === true
40
- );
41
- }
42
-
43
- // AJV's codegen template tag. The package is consumed in both ESM and CJS interop
44
- // shapes, so the named export may sit on the namespace or behind `.default` —
45
- // the same fallback `schema-compat` uses to reach the constructor. Skipping it
46
- // would leave `codegen` undefined under a loader that does not detect AJV's named
47
- // CJS re-exports, and the keyword would throw on its first compile.
48
- type CodegenTag = (s: TemplateStringsArray, ...a: unknown[]) => unknown;
49
- const ajvExports = (AjvNS as any).default ?? AjvNS;
50
- const codegen: CodegenTag = ajvExports._ ?? (AjvNS as any)._;
51
-
52
- /**
53
- * The AJV keyword. `Uint8Array` is the structural test rather than any richer
54
- * notion of "binary": it is what every byte producer in the runtime hands over
55
- * (`Buffer` extends it, so Node buffers pass), and it is the one check that means
56
- * the same thing in a browser-side analyzer and in the kernel.
57
- */
58
- export function binaryKeyword(): KeywordDefinition {
59
- return {
60
- keyword: X_TELO_BINARY,
61
- schemaType: "boolean",
62
- code(cxt: any) {
63
- // `x-telo-binary: false` states nothing, so it constrains nothing.
64
- if (cxt.schema !== true) return;
65
- cxt.pass(codegen`${cxt.data} instanceof Uint8Array`);
66
- },
67
- error: {
68
- message: "must be raw bytes (a Uint8Array) — bytes cannot be written inline in a manifest",
69
- },
70
- } as KeywordDefinition;
71
- }