@telorun/analyzer 0.65.0 → 0.67.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 (142) hide show
  1. package/dist/analyzer.d.ts +1 -1
  2. package/dist/analyzer.d.ts.map +1 -1
  3. package/dist/analyzer.js +194 -65
  4. package/dist/builtins.d.ts.map +1 -1
  5. package/dist/builtins.js +67 -16
  6. package/dist/call-graph.d.ts.map +1 -1
  7. package/dist/call-graph.js +7 -1
  8. package/dist/cel-environment.d.ts +12 -0
  9. package/dist/cel-environment.d.ts.map +1 -1
  10. package/dist/cel-environment.js +35 -19
  11. package/dist/cel-scope.d.ts +8 -0
  12. package/dist/cel-scope.d.ts.map +1 -1
  13. package/dist/cel-scope.js +89 -11
  14. package/dist/definition-registry.d.ts +41 -0
  15. package/dist/definition-registry.d.ts.map +1 -1
  16. package/dist/definition-registry.js +76 -0
  17. package/dist/dependency-graph.d.ts.map +1 -1
  18. package/dist/dependency-graph.js +65 -0
  19. package/dist/eval-paths.d.ts +68 -0
  20. package/dist/eval-paths.d.ts.map +1 -1
  21. package/dist/eval-paths.js +138 -0
  22. package/dist/flatten-for-analyzer.d.ts +37 -1
  23. package/dist/flatten-for-analyzer.d.ts.map +1 -1
  24. package/dist/flatten-for-analyzer.js +104 -5
  25. package/dist/import-resolution-diagnostics.d.ts +1 -1
  26. package/dist/import-resolution-diagnostics.js +1 -1
  27. package/dist/index.d.ts +12 -8
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +8 -6
  30. package/dist/inline-imports.d.ts.map +1 -1
  31. package/dist/inline-imports.js +1 -0
  32. package/dist/manifest-visitor.d.ts +4 -0
  33. package/dist/manifest-visitor.d.ts.map +1 -1
  34. package/dist/manifest-visitor.js +36 -1
  35. package/dist/migrations/entries/index.d.ts.map +1 -1
  36. package/dist/migrations/entries/index.js +2 -0
  37. package/dist/migrations/entries/schema-prepare-bucket.json +27 -0
  38. package/dist/peer-binding.d.ts +232 -0
  39. package/dist/peer-binding.d.ts.map +1 -0
  40. package/dist/peer-binding.js +418 -0
  41. package/dist/precompile.d.ts.map +1 -1
  42. package/dist/precompile.js +8 -0
  43. package/dist/ref-slot.d.ts +13 -0
  44. package/dist/ref-slot.d.ts.map +1 -1
  45. package/dist/ref-slot.js +26 -0
  46. package/dist/reference-field-map.d.ts +28 -0
  47. package/dist/reference-field-map.d.ts.map +1 -1
  48. package/dist/reference-field-map.js +25 -0
  49. package/dist/referrer-rule.d.ts +17 -0
  50. package/dist/referrer-rule.d.ts.map +1 -1
  51. package/dist/referrer-rule.js +4 -1
  52. package/dist/release/index.d.ts +1 -1
  53. package/dist/release/index.js +1 -1
  54. package/dist/resolve-ref-sentinels.d.ts.map +1 -1
  55. package/dist/resolve-ref-sentinels.js +14 -1
  56. package/dist/resource-input.d.ts +75 -0
  57. package/dist/resource-input.d.ts.map +1 -0
  58. package/dist/resource-input.js +90 -0
  59. package/dist/resource-rule.d.ts +40 -27
  60. package/dist/resource-rule.d.ts.map +1 -1
  61. package/dist/resource-rule.js +61 -7
  62. package/dist/rule-condition.d.ts +11 -0
  63. package/dist/rule-condition.d.ts.map +1 -1
  64. package/dist/rule-condition.js +13 -0
  65. package/dist/schema-projection.d.ts +119 -4
  66. package/dist/schema-projection.d.ts.map +1 -1
  67. package/dist/schema-projection.js +213 -18
  68. package/dist/system-kinds.d.ts +7 -2
  69. package/dist/system-kinds.d.ts.map +1 -1
  70. package/dist/system-kinds.js +7 -2
  71. package/dist/telo-version.d.ts +1 -1
  72. package/dist/telo-version.js +1 -1
  73. package/dist/template-body.d.ts +50 -0
  74. package/dist/template-body.d.ts.map +1 -0
  75. package/dist/template-body.js +58 -0
  76. package/dist/validate-cel-context.d.ts +1 -17
  77. package/dist/validate-cel-context.d.ts.map +1 -1
  78. package/dist/validate-cel-context.js +73 -78
  79. package/dist/validate-identifier-names.d.ts.map +1 -1
  80. package/dist/validate-identifier-names.js +17 -2
  81. package/dist/validate-reference-forms.d.ts +10 -0
  82. package/dist/validate-reference-forms.d.ts.map +1 -1
  83. package/dist/validate-reference-forms.js +23 -0
  84. package/dist/validate-references.d.ts +17 -0
  85. package/dist/validate-references.d.ts.map +1 -1
  86. package/dist/validate-references.js +77 -18
  87. package/dist/validate-referrer-rules.d.ts +38 -4
  88. package/dist/validate-referrer-rules.d.ts.map +1 -1
  89. package/dist/validate-referrer-rules.js +139 -31
  90. package/dist/validate-resource-inputs.d.ts +35 -0
  91. package/dist/validate-resource-inputs.d.ts.map +1 -0
  92. package/dist/validate-resource-inputs.js +319 -0
  93. package/dist/validate-resource-rules.d.ts +11 -2
  94. package/dist/validate-resource-rules.d.ts.map +1 -1
  95. package/dist/validate-resource-rules.js +18 -12
  96. package/dist/validate-schema-projection.d.ts.map +1 -1
  97. package/dist/validate-schema-projection.js +33 -2
  98. package/dist/validate-template-dispatch.d.ts +27 -0
  99. package/dist/validate-template-dispatch.d.ts.map +1 -0
  100. package/dist/validate-template-dispatch.js +95 -0
  101. package/dist/with-synthetic-positions.d.ts +1 -1
  102. package/dist/with-synthetic-positions.js +1 -1
  103. package/package.json +3 -3
  104. package/src/analyzer.ts +236 -70
  105. package/src/builtins.ts +69 -16
  106. package/src/call-graph.ts +7 -1
  107. package/src/cel-environment.ts +48 -19
  108. package/src/cel-scope.ts +113 -19
  109. package/src/definition-registry.ts +78 -0
  110. package/src/dependency-graph.ts +66 -0
  111. package/src/eval-paths.ts +152 -0
  112. package/src/flatten-for-analyzer.ts +117 -4
  113. package/src/import-resolution-diagnostics.ts +1 -1
  114. package/src/index.ts +57 -4
  115. package/src/inline-imports.ts +1 -0
  116. package/src/manifest-visitor.ts +41 -1
  117. package/src/migrations/entries/index.ts +2 -0
  118. package/src/migrations/entries/schema-prepare-bucket.json +27 -0
  119. package/src/peer-binding.ts +641 -0
  120. package/src/precompile.ts +8 -0
  121. package/src/ref-slot.ts +36 -0
  122. package/src/reference-field-map.ts +42 -0
  123. package/src/referrer-rule.ts +20 -1
  124. package/src/release/index.ts +1 -1
  125. package/src/resolve-ref-sentinels.ts +12 -1
  126. package/src/resource-input.ts +132 -0
  127. package/src/resource-rule.ts +69 -7
  128. package/src/rule-condition.ts +15 -0
  129. package/src/schema-projection.ts +302 -20
  130. package/src/system-kinds.ts +7 -2
  131. package/src/telo-version.ts +1 -1
  132. package/src/template-body.ts +104 -0
  133. package/src/validate-cel-context.ts +73 -78
  134. package/src/validate-identifier-names.ts +18 -3
  135. package/src/validate-reference-forms.ts +22 -0
  136. package/src/validate-references.ts +85 -16
  137. package/src/validate-referrer-rules.ts +215 -28
  138. package/src/validate-resource-inputs.ts +367 -0
  139. package/src/validate-resource-rules.ts +26 -12
  140. package/src/validate-schema-projection.ts +50 -1
  141. package/src/validate-template-dispatch.ts +99 -0
  142. package/src/with-synthetic-positions.ts +1 -1
@@ -2,7 +2,7 @@
2
2
  * The release model: module identity, fragments, the ledger, the edge graph,
3
3
  * level propagation and version planning.
4
4
  *
5
- * Browser-safe by construction — pure data in, plan out — so the telo editor can
5
+ * Browser-safe by construction — pure data in, plan out — so telo studio can
6
6
  * answer "what does changing this library bump?" from the same model the CLI
7
7
  * releases from. Everything Node-shaped (finding the workspace, running the
8
8
  * controller builder, building payloads, reading git, writing files) is
@@ -2,7 +2,7 @@
2
2
  * The release model: module identity, fragments, the ledger, the edge graph,
3
3
  * level propagation and version planning.
4
4
  *
5
- * Browser-safe by construction — pure data in, plan out — so the telo editor can
5
+ * Browser-safe by construction — pure data in, plan out — so telo studio can
6
6
  * answer "what does changing this library bump?" from the same model the CLI
7
7
  * releases from. Everything Node-shaped (finding the workspace, running the
8
8
  * controller builder, building payloads, reading git, writing files) is
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-ref-sentinels.d.ts","sourceRoot":"","sources":["../src/resolve-ref-sentinels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAGlC;+CAC+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS,CAAC;CAClC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAK5C,kBAAkB,GAAE,gBAAgB,EAAO;AAC3C;;+CAE+C;AAC/C,IAAI,CAAC,EAAE,mBAAmB,GACzB,IAAI,CA8HN"}
1
+ {"version":3,"file":"resolve-ref-sentinels.d.ts","sourceRoot":"","sources":["../src/resolve-ref-sentinels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAGlC;+CAC+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS,CAAC;CAClC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAK5C,kBAAkB,GAAE,gBAAgB,EAAO;AAC3C;;+CAE+C;AAC/C,IAAI,CAAC,EAAE,mBAAmB,GACzB,IAAI,CAyIN"}
@@ -172,7 +172,20 @@ defs) {
172
172
  for (const r of resources) {
173
173
  if (isForeign(r))
174
174
  continue;
175
- if (!r.metadata?.name || !r.kind || SYSTEM_KINDS.has(r.kind))
175
+ if (!r.metadata?.name || !r.kind)
176
+ continue;
177
+ // A `Telo.Import` is import-time metadata, not a resource instance — except
178
+ // for its `resources:` block, which supplies the instances the target
179
+ // library declared it needs. Those are `!ref`s to the importer's OWN
180
+ // resources and resolve exactly like any other reference; nothing else on
181
+ // the document is a reference slot, so only that subtree is walked.
182
+ if (r.kind === "Telo.Import") {
183
+ const supplied = r.resources;
184
+ if (supplied)
185
+ r.resources = walk(supplied);
186
+ continue;
187
+ }
188
+ if (SYSTEM_KINDS.has(r.kind))
176
189
  continue;
177
190
  walk(r);
178
191
  }
@@ -0,0 +1,75 @@
1
+ import type { ResourceManifest } from "@telorun/sdk";
2
+ /**
3
+ * The `resources:` block on a `Telo.Library` — the instances a library requires
4
+ * from whoever imports it, beside the scalar `variables:` / `secrets:` blocks.
5
+ * Instances used to flow up only (`exports.resources`); this is the inward half.
6
+ *
7
+ * THE SINGLE READER of the block, in both halves (the analyzer's passes and the
8
+ * kernel's import controller), on the `ref-slot.ts` precedent: a boundary whose
9
+ * shape two runtimes must agree about is read in one place, so the next shape
10
+ * change is a one-file edit rather than four surfaces recognising a block by
11
+ * pattern-matching it.
12
+ *
13
+ * Browser-safe — no I/O, no Node built-ins.
14
+ */
15
+ /** One declared input. */
16
+ export interface ResourceInput {
17
+ /** The entry key. Inside the library the instance is named exactly as a
18
+ * locally declared resource: `!ref <name>` at a ref slot, `resources.<name>`
19
+ * in CEL. */
20
+ name: string;
21
+ /** The alias-qualified kind constraint, as written in the DECLARING library's
22
+ * own scope (`Sql.Connection`, `Self.Store`, `Telo.LogSink`). There is no
23
+ * `use:` here: the boundary is a dependency edge for init order whatever the
24
+ * library does with the instance, and the flattened application analysis
25
+ * drops the library doc, so an app-level claim about internal call sites is
26
+ * one nothing could check. */
27
+ kind: string;
28
+ description?: string;
29
+ }
30
+ /** Read a module document's `resources:` block. Returns `[]` for an
31
+ * `Telo.Application` (which has no such block), for a library that declares
32
+ * none, and for a malformed entry — the document's own schema validation
33
+ * reports the shape against the precise `resources.<name>` path. */
34
+ export declare function readResourceInputs(moduleDoc: unknown): ResourceInput[];
35
+ /** The values an import supplies for a target library's declared inputs, keyed
36
+ * by entry name. Read off a `Telo.Import` (authored or desugared from an
37
+ * `imports:` entry). Values are `!ref` sentinels before Phase 2.5 and
38
+ * `{kind, name}` after it — this reader does not interpret them. */
39
+ export declare function readSuppliedResources(importDoc: unknown): Record<string, unknown>;
40
+ /** True when `manifest` is a synthesized kind-only declaration standing in for a
41
+ * library's `resources:` entry rather than a resource the author declared. */
42
+ export declare function isInjectedDeclaration(manifest: ResourceManifest | undefined): boolean;
43
+ /**
44
+ * Synthesize a kind-only declaration per `resources:` entry, in the declaring
45
+ * library's own scope.
46
+ *
47
+ * That model has to exist because a library's internals are validated in the
48
+ * library's own pass — the flattened application analysis drops the library doc
49
+ * — so with nothing behind `connection`, `!ref connection` would have nothing to
50
+ * resolve against.
51
+ *
52
+ * Kind-only is enough because it is already what a ref slot gets: a reading
53
+ * types its `status:` half from the kind, closed so a typo below it is
54
+ * `CEL_UNKNOWN_FIELD`, and leaves the flat half open, since no manifest declares
55
+ * what `snapshot()` returned. So `!ref connection` at a ref slot and
56
+ * `resources.connection.<field>` in CEL answer exactly as they do for a locally
57
+ * declared resource.
58
+ *
59
+ * The declaration is a stand-in, never an instantiation: the per-resource
60
+ * validation loop skips it (its kind is routinely an abstract, and its config is
61
+ * the importer's to supply), and the kernel's import controller filters it out
62
+ * of the manifests it registers, binding the borrowed instance under the name
63
+ * instead.
64
+ */
65
+ export declare function injectedDeclarations(moduleDoc: ResourceManifest, ownModule: string | undefined): ResourceManifest[];
66
+ /** How many times a library is instantiated in one application. `isolated` —
67
+ * the default, and what every published module was written against — gives
68
+ * each import declaration its own child scope with its own instances;
69
+ * `shared` makes the library a singleton every import resolves to. */
70
+ export type LibraryLifecycle = "isolated" | "shared";
71
+ /** Read a module document's `lifecycle:`. `Telo.Application` has a field of the
72
+ * same name with a different default and no reader; this is the LIBRARY
73
+ * question only, so it answers `isolated` for anything else. */
74
+ export declare function readLibraryLifecycle(moduleDoc: unknown): LibraryLifecycle;
75
+ //# sourceMappingURL=resource-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-input.d.ts","sourceRoot":"","sources":["../src/resource-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;;;;;;;;GAYG;AAEH,0BAA0B;AAC1B,MAAM,WAAW,aAAa;IAC5B;;kBAEc;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;;;;mCAK+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAOD;;;qEAGqE;AACrE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,OAAO,GAAG,aAAa,EAAE,CAgBtE;AAED;;;qEAGqE;AACrE,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIjF;AAED;+EAC+E;AAC/E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAErF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,gBAAgB,EAAE,CAgBpB;AAED;;;uEAGuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErD;;iEAEiE;AACjE,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,CAIzE"}
@@ -0,0 +1,90 @@
1
+ /** Marker stamped on a synthesized kind-only declaration (see
2
+ * {@link injectedDeclarations}). Read through {@link isInjectedDeclaration} —
3
+ * never by testing the field. */
4
+ const INJECTED = "xTeloInjected";
5
+ /** Read a module document's `resources:` block. Returns `[]` for an
6
+ * `Telo.Application` (which has no such block), for a library that declares
7
+ * none, and for a malformed entry — the document's own schema validation
8
+ * reports the shape against the precise `resources.<name>` path. */
9
+ export function readResourceInputs(moduleDoc) {
10
+ const raw = moduleDoc?.resources;
11
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
12
+ return [];
13
+ const out = [];
14
+ for (const [name, value] of Object.entries(raw)) {
15
+ if (!value || typeof value !== "object" || Array.isArray(value))
16
+ continue;
17
+ const kind = value.kind;
18
+ if (typeof kind !== "string" || kind.length === 0)
19
+ continue;
20
+ const description = value.description;
21
+ out.push({
22
+ name,
23
+ kind,
24
+ ...(typeof description === "string" ? { description } : {}),
25
+ });
26
+ }
27
+ return out;
28
+ }
29
+ /** The values an import supplies for a target library's declared inputs, keyed
30
+ * by entry name. Read off a `Telo.Import` (authored or desugared from an
31
+ * `imports:` entry). Values are `!ref` sentinels before Phase 2.5 and
32
+ * `{kind, name}` after it — this reader does not interpret them. */
33
+ export function readSuppliedResources(importDoc) {
34
+ const raw = importDoc?.resources;
35
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
36
+ return {};
37
+ return raw;
38
+ }
39
+ /** True when `manifest` is a synthesized kind-only declaration standing in for a
40
+ * library's `resources:` entry rather than a resource the author declared. */
41
+ export function isInjectedDeclaration(manifest) {
42
+ return manifest?.metadata?.[INJECTED] === true;
43
+ }
44
+ /**
45
+ * Synthesize a kind-only declaration per `resources:` entry, in the declaring
46
+ * library's own scope.
47
+ *
48
+ * That model has to exist because a library's internals are validated in the
49
+ * library's own pass — the flattened application analysis drops the library doc
50
+ * — so with nothing behind `connection`, `!ref connection` would have nothing to
51
+ * resolve against.
52
+ *
53
+ * Kind-only is enough because it is already what a ref slot gets: a reading
54
+ * types its `status:` half from the kind, closed so a typo below it is
55
+ * `CEL_UNKNOWN_FIELD`, and leaves the flat half open, since no manifest declares
56
+ * what `snapshot()` returned. So `!ref connection` at a ref slot and
57
+ * `resources.connection.<field>` in CEL answer exactly as they do for a locally
58
+ * declared resource.
59
+ *
60
+ * The declaration is a stand-in, never an instantiation: the per-resource
61
+ * validation loop skips it (its kind is routinely an abstract, and its config is
62
+ * the importer's to supply), and the kernel's import controller filters it out
63
+ * of the manifests it registers, binding the borrowed instance under the name
64
+ * instead.
65
+ */
66
+ export function injectedDeclarations(moduleDoc, ownModule) {
67
+ const inputs = readResourceInputs(moduleDoc);
68
+ if (inputs.length === 0)
69
+ return [];
70
+ const meta = moduleDoc.metadata;
71
+ return inputs.map((input) => ({
72
+ kind: input.kind,
73
+ metadata: {
74
+ name: input.name,
75
+ ...(ownModule ? { module: ownModule } : {}),
76
+ source: meta?.source ?? "",
77
+ sourceLine: meta?.sourceLine ?? 0,
78
+ [INJECTED]: true,
79
+ },
80
+ }));
81
+ }
82
+ /** Read a module document's `lifecycle:`. `Telo.Application` has a field of the
83
+ * same name with a different default and no reader; this is the LIBRARY
84
+ * question only, so it answers `isolated` for anything else. */
85
+ export function readLibraryLifecycle(moduleDoc) {
86
+ const doc = moduleDoc;
87
+ if (doc?.kind !== "Telo.Library")
88
+ return "isolated";
89
+ return doc.lifecycle === "shared" ? "shared" : "isolated";
90
+ }
@@ -1,26 +1,3 @@
1
- /**
2
- * The single reader for `x-telo-resource-rules` — a kind declaring, as data,
3
- * relationships between the fields of one resource that JSON Schema cannot
4
- * state: an index naming a column its table does not declare, a foreign key
5
- * whose two sides differ in length, a rename whose source is still declared.
6
- *
7
- * The predicate is CEL over `self` (the whole resource) and `this` (the element
8
- * under test), so correlating two collections is a comprehension closure rather
9
- * than a path language with wildcard bindings to design. `in:` names the
10
- * collection to iterate and IS the diagnostic anchor: iterating what the pointer
11
- * names is what makes a reported path exist by construction.
12
- *
13
- * Vocabulary borrowed from `Telo.JsonSchema.rules` — `condition` true when the
14
- * rule HOLDS, the subject bound as `this`, plus `code` and `message` — because
15
- * two CEL rule vocabularies with opposite polarity is a trap an author falls
16
- * into once per rule. The two mechanisms stay separate: a `Telo.JsonSchema` rule
17
- * runs at dispatch against a value, this one at `telo check` against a manifest.
18
- *
19
- * Lenient by design, the `ref-slot.ts` precedent: anything unreadable here reads
20
- * as absent, and `validate-resource-rules.ts` is the strict half that reports it.
21
- *
22
- * Browser-safe: no Node built-ins.
23
- */
24
1
  export declare const RESOURCE_RULES_ANNOTATION = "x-telo-resource-rules";
25
2
  export type ResourceRuleSeverity = "error" | "warning";
26
3
  export interface ResourceRule {
@@ -45,6 +22,22 @@ export interface RuleSubject {
45
22
  /** Present when the collection is a map: the entry's key. */
46
23
  readonly key?: string;
47
24
  }
25
+ /** The engine of a non-CEL tagged sentinel — a `!ref`, an `!include-*` — or
26
+ * `undefined`. A reference is comparable and never blocks a rule; the other
27
+ * tags hold a value only known once the resource is created, so they do, and
28
+ * the diagnostic has to name the tag rather than claim CEL. */
29
+ export declare function deferredTagOf(value: unknown): string | undefined;
30
+ /**
31
+ * True when a condition was written with the `!cel` tag.
32
+ *
33
+ * The readers stay lenient and take a bare string — a rule still runs either
34
+ * way. What an untagged condition loses is everything outside evaluation: to the
35
+ * editor's colouring, completion and hover it is a plain string, so a rule author
36
+ * writes CEL with no help and gets none of the checks a `!cel` scalar gets.
37
+ * Losing that silently is exactly what a strict half exists to move earlier, so
38
+ * the tag is reported by the strict halves and never enforced by the readers.
39
+ */
40
+ export declare function isTaggedCondition(value: unknown): boolean;
48
41
  /** A precompiled `!cel` node keeps its author-written text on `source`; a plain
49
42
  * string is taken verbatim so a rule reads the same however the loader was
50
43
  * configured (a round-trip view runs with `compile` off). */
@@ -73,15 +66,35 @@ export declare function pointerToPath(pointer: string): string;
73
66
  * declaration defect the strict half reports, and an empty list would hide it.
74
67
  */
75
68
  export declare function resolveRuleSubjects(config: unknown, pointer: string): RuleSubject[] | undefined;
69
+ /** The first leaf a rule cannot compare, and what it is. `what` is a noun
70
+ * phrase the diagnostic quotes verbatim, because "a CEL expression" printed
71
+ * over an `!include-bytes` embed sends its author looking for an expression
72
+ * that is not there. */
73
+ export interface DynamicLeaf {
74
+ readonly path: string;
75
+ readonly what: string;
76
+ }
77
+ /** Classify ONE node, without descending. Exported because a caller that draws
78
+ * its own bound on how far to look (`peer-binding`'s top-level-scalar scan)
79
+ * must classify by the same rule as the recursive walk, or a `!ref` is a
80
+ * reference to one of them and an expression to the other. */
81
+ export declare function dynamicNode(value: unknown, path: string): DynamicLeaf | undefined;
76
82
  /**
77
- * Path of the first CEL leaf inside a value, or `undefined` when every leaf is
78
- * literal. A rule reading an expression would be evaluating a placeholder, so
79
- * the subject is skipped — and the skip is reported, never silent.
83
+ * The first leaf inside a value whose contents are not known until the resource
84
+ * is created, or `undefined` when every leaf is literal. A rule reading one
85
+ * would be comparing against a placeholder, so the subject is skipped — and the
86
+ * skip is reported, never silent.
87
+ *
88
+ * A `!ref` is NOT one of them. It is a tagged sentinel like `!cel`, and testing
89
+ * `__tagged` alone read every reference as an expression: a column whose `type:`
90
+ * holds a `!ref` switched off every rule touching `self.columns` and reported a
91
+ * CEL expression in a manifest containing none. A reference names a declaration
92
+ * — a value a rule compares perfectly well, and the one peer rules are built on.
80
93
  *
81
94
  * Stops at nested inline `{ kind }` declarations for the reason every other walk
82
95
  * does: that CEL belongs to the nested kind, evaluated in its own scope.
83
96
  */
84
- export declare function findDynamicLeaf(value: unknown, base?: string): string | undefined;
97
+ export declare function findDynamicLeaf(value: unknown, base?: string): DynamicLeaf | undefined;
85
98
  /**
86
99
  * The nodes a condition actually READS, resolved against the resource and the
87
100
  * element under test — the input to the dynamic-leaf check.
@@ -1 +1 @@
1
- {"version":3,"file":"resource-rule.d.ts","sourceRoot":"","sources":["../src/resource-rule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,yBAAyB,0BAA0B,CAAC;AAEjE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B;yEACqE;IACrE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;qEACiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,+CAA+C;AAC/C,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAeD;;8DAE8D;AAC9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI9D;AAED;;6CAE6C;AAC7C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAG7D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,EAAE,CA0BjE;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOrE;AAED;;yEAEyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiBvE;AAED;+EAC+E;AAC/E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,WAAW,EAAE,GAAG,SAAS,CAe3B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,SAAS,CAiB7E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE;AACtC;;2BAE2B;AAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,EAAE,CAqBX"}
1
+ {"version":3,"file":"resource-rule.d.ts","sourceRoot":"","sources":["../src/resource-rule.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,yBAAyB,0BAA0B,CAAC;AAEjE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B;yEACqE;IACrE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;qEACiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,+CAA+C;AAC/C,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAyBD;;;gEAGgE;AAChE,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED;;8DAE8D;AAC9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI9D;AAED;;6CAE6C;AAC7C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAG7D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,EAAE,CA0BjE;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOrE;AAED;;yEAEyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiBvE;AAED;+EAC+E;AAC/E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,WAAW,EAAE,GAAG,SAAS,CAe3B;AAED;;;yBAGyB;AACzB,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;+DAG+D;AAC/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAKjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAK,GAAG,WAAW,GAAG,SAAS,CAkBlF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE;AACtC;;2BAE2B;AAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,EAAE,CAqBX"}
@@ -21,6 +21,7 @@
21
21
  *
22
22
  * Browser-safe: no Node built-ins.
23
23
  */
24
+ import { CEL_ENGINE, isRefSentinel, isTaggedSentinel } from "@telorun/templating";
24
25
  export const RESOURCE_RULES_ANNOTATION = "x-telo-resource-rules";
25
26
  function isObject(value) {
26
27
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -29,9 +30,43 @@ function isObject(value) {
29
30
  * Both markers are tested because they are not always both present: a
30
31
  * registered definition's schema reaches the analyzer with `call` and
31
32
  * `__compiled` dropped, keeping only `__tagged` + `source`. Testing one would
32
- * make a rule readable on some paths and invisible on others. */
33
+ * make a rule readable on some paths and invisible on others.
34
+ *
35
+ * A tagged sentinel of ANOTHER engine is not one. `__tagged` marks every tag
36
+ * the loader parses — `!ref` above all — so testing it alone read a reference
37
+ * as an expression: a column whose `type:` holds a `!ref` skipped every rule
38
+ * that touched `self.columns`, and said "the value holds a CEL expression" about
39
+ * a manifest containing none. A reference names a declaration and is a
40
+ * perfectly comparable value; what a rule cannot compare is a value COMPUTED at
41
+ * create time, which is what this predicate exists to find. */
33
42
  function isCelNode(value) {
34
- return isObject(value) && (value.__compiled === true || value.__tagged === true);
43
+ if (!isObject(value))
44
+ return false;
45
+ if (value.__compiled === true)
46
+ return true;
47
+ return value.__tagged === true && value.engine === CEL_ENGINE;
48
+ }
49
+ /** The engine of a non-CEL tagged sentinel — a `!ref`, an `!include-*` — or
50
+ * `undefined`. A reference is comparable and never blocks a rule; the other
51
+ * tags hold a value only known once the resource is created, so they do, and
52
+ * the diagnostic has to name the tag rather than claim CEL. */
53
+ export function deferredTagOf(value) {
54
+ if (!isTaggedSentinel(value) || isRefSentinel(value))
55
+ return undefined;
56
+ return value.engine === CEL_ENGINE ? undefined : value.engine;
57
+ }
58
+ /**
59
+ * True when a condition was written with the `!cel` tag.
60
+ *
61
+ * The readers stay lenient and take a bare string — a rule still runs either
62
+ * way. What an untagged condition loses is everything outside evaluation: to the
63
+ * editor's colouring, completion and hover it is a plain string, so a rule author
64
+ * writes CEL with no help and gets none of the checks a `!cel` scalar gets.
65
+ * Losing that silently is exactly what a strict half exists to move earlier, so
66
+ * the tag is reported by the strict halves and never enforced by the readers.
67
+ */
68
+ export function isTaggedCondition(value) {
69
+ return isCelNode(value);
35
70
  }
36
71
  /** A precompiled `!cel` node keeps its author-written text on `source`; a plain
37
72
  * string is taken verbatim so a rule reads the same however the loader was
@@ -156,18 +191,37 @@ export function resolveRuleSubjects(config, pointer) {
156
191
  }
157
192
  return undefined;
158
193
  }
194
+ /** Classify ONE node, without descending. Exported because a caller that draws
195
+ * its own bound on how far to look (`peer-binding`'s top-level-scalar scan)
196
+ * must classify by the same rule as the recursive walk, or a `!ref` is a
197
+ * reference to one of them and an expression to the other. */
198
+ export function dynamicNode(value, path) {
199
+ const at = path || "(value)";
200
+ if (isCelNode(value))
201
+ return { path: at, what: "a CEL expression" };
202
+ const tag = deferredTagOf(value);
203
+ return tag ? { path: at, what: `an !${tag} embed` } : undefined;
204
+ }
159
205
  /**
160
- * Path of the first CEL leaf inside a value, or `undefined` when every leaf is
161
- * literal. A rule reading an expression would be evaluating a placeholder, so
162
- * the subject is skipped — and the skip is reported, never silent.
206
+ * The first leaf inside a value whose contents are not known until the resource
207
+ * is created, or `undefined` when every leaf is literal. A rule reading one
208
+ * would be comparing against a placeholder, so the subject is skipped — and the
209
+ * skip is reported, never silent.
210
+ *
211
+ * A `!ref` is NOT one of them. It is a tagged sentinel like `!cel`, and testing
212
+ * `__tagged` alone read every reference as an expression: a column whose `type:`
213
+ * holds a `!ref` switched off every rule touching `self.columns` and reported a
214
+ * CEL expression in a manifest containing none. A reference names a declaration
215
+ * — a value a rule compares perfectly well, and the one peer rules are built on.
163
216
  *
164
217
  * Stops at nested inline `{ kind }` declarations for the reason every other walk
165
218
  * does: that CEL belongs to the nested kind, evaluated in its own scope.
166
219
  */
167
220
  export function findDynamicLeaf(value, base = "") {
168
221
  if (isObject(value)) {
169
- if (isCelNode(value))
170
- return base || "(value)";
222
+ const own = dynamicNode(value, base);
223
+ if (own)
224
+ return own;
171
225
  if (typeof value.kind === "string" && base !== "")
172
226
  return undefined;
173
227
  for (const [key, child] of Object.entries(value)) {
@@ -25,6 +25,17 @@ import { buildCelEnvironment } from "@telorun/templating";
25
25
  * as defective rather than truncating coverage silently.
26
26
  */
27
27
  export declare const RULE_BUDGET_MS = 50;
28
+ /**
29
+ * The one message for an untagged `condition:`, shared by every rule family so
30
+ * all of them say the same thing about the same defect.
31
+ *
32
+ * The readers stay lenient and a bare string still runs. What it loses is
33
+ * everything outside evaluation — to the editor's colouring, completion and
34
+ * hover an untagged condition is a plain string, so its author writes CEL with
35
+ * no help and none of the checks a `!cel` scalar gets. Losing that silently is
36
+ * the failure a strict half exists to move earlier.
37
+ */
38
+ export declare const UNTAGGED_CONDITION: string;
28
39
  /** The analyzer's own environment — no host handlers, so every `hostBacked`
29
40
  * entry is a throwing stub. Built once; it is stateless. */
30
41
  export declare function ruleEnv(): ReturnType<typeof buildCelEnvironment>;
@@ -1 +1 @@
1
- {"version":3,"file":"rule-condition.d.ts","sourceRoot":"","sources":["../src/rule-condition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAEL,mBAAmB,EAGpB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAQjC;6DAC6D;AAC7D,wBAAgB,OAAO,IAAI,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAGhE;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAA;CAAE,GAC7F;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAsBvB,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM;AACjB;wEACwE;AACxE,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,YAAY,CAoBd;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB7D"}
1
+ {"version":3,"file":"rule-condition.d.ts","sourceRoot":"","sources":["../src/rule-condition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAEL,mBAAmB,EAGpB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,QAGgD,CAAC;AAQhF;6DAC6D;AAC7D,wBAAgB,OAAO,IAAI,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAGhE;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAA;CAAE,GAC7F;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAsBvB,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM;AACjB;wEACwE;AACxE,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,YAAY,CAoBd;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB7D"}
@@ -25,6 +25,19 @@ import { CEL_FUNCTIONS, buildCelEnvironment, celEngine, extractAccessChains, } f
25
25
  * as defective rather than truncating coverage silently.
26
26
  */
27
27
  export const RULE_BUDGET_MS = 50;
28
+ /**
29
+ * The one message for an untagged `condition:`, shared by every rule family so
30
+ * all of them say the same thing about the same defect.
31
+ *
32
+ * The readers stay lenient and a bare string still runs. What it loses is
33
+ * everything outside evaluation — to the editor's colouring, completion and
34
+ * hover an untagged condition is a plain string, so its author writes CEL with
35
+ * no help and none of the checks a `!cel` scalar gets. Losing that silently is
36
+ * the failure a strict half exists to move earlier.
37
+ */
38
+ export const UNTAGGED_CONDITION = "Write 'condition' with the !cel tag. The reader is lenient and a bare string still " +
39
+ "runs, but untagged the expression is not CEL to the editor's colouring, completion " +
40
+ "or hover, so a rule silently stops being CEL to every surface but this one.";
28
41
  const HOST_BACKED = new Set(CEL_FUNCTIONS.filter((f) => f.hostBacked).map((f) => f.name));
29
42
  const NON_DETERMINISTIC = new Set(CEL_FUNCTIONS.filter((f) => !f.deterministic).map((f) => f.name));
30
43
  let sharedEnv;
@@ -47,6 +47,32 @@ export interface SchemaProjection {
47
47
  readonly nullable?: string;
48
48
  /** Entry field that wraps the mapped node in an array. */
49
49
  readonly array?: string;
50
+ /**
51
+ * How an entry whose keyed field holds a REFERENCE projects.
52
+ *
53
+ * The map is keyed on the field's VALUE, and a reference is not a key, so a
54
+ * `type:` holding one falls through to this path. It is declared as data by the
55
+ * backend, which is what keeps the analyzer from learning that an enum exists:
56
+ * `from` names the field of the target declaration to read, `keyword` the
57
+ * schema keyword its values become, and `base` / `baseFrom` where the node's
58
+ * own type comes from — a literal for an engine whose named type IS its own
59
+ * base, a field of the target for one that declares a storage class.
60
+ *
61
+ * A backend that declares none projects exactly as it did before.
62
+ */
63
+ readonly reference?: ProjectionReference;
64
+ }
65
+ /** The reference path of a projection — see {@link SchemaProjection.reference}. */
66
+ export interface ProjectionReference {
67
+ /** Field of the TARGET declaration whose value the keyword takes. */
68
+ readonly from: string;
69
+ /** The JSON Schema keyword those values become (`enum`). */
70
+ readonly keyword: string;
71
+ /** The node the keyword is added to, written literally. */
72
+ readonly base?: Record<string, unknown>;
73
+ /** Field of the target declaration naming a value in the kind's own
74
+ * `x-telo-schema-map`, whose mapped node is the base. */
75
+ readonly baseFrom?: string;
50
76
  }
51
77
  export type SchemaMap = Readonly<Record<string, Record<string, unknown>>>;
52
78
  /** The projection a kind declares, or undefined. Invalid shapes read as absent;
@@ -59,6 +85,18 @@ export declare function rawSchemaProjection(definition: unknown): unknown;
59
85
  /** True when the annotation was found inside `schema:` rather than on the
60
86
  * document — the misplacement the strict half reports. */
61
87
  export declare function schemaProjectionIsMisplaced(definition: unknown): boolean;
88
+ /**
89
+ * The schema node that CARRIES the value vocabulary — the node itself, or the
90
+ * branch of a union that declares the map.
91
+ *
92
+ * A slot unioning a closed value vocabulary with a reference keeps its map on the
93
+ * value branch, exactly as the ref-slot reader peels the same union for its
94
+ * constraint. Exported because the strict half checks the map against the same
95
+ * branch's `enum`, and two implementations of "which branch is the value one"
96
+ * would eventually disagree — silently, since the failure of missing one is a
97
+ * completeness check that quietly stops running.
98
+ */
99
+ export declare function schemaMapBranch(node: unknown): Record<string, unknown> | undefined;
62
100
  export declare function readSchemaMap(node: unknown): SchemaMap | undefined;
63
101
  /** The consumer-side annotation: a JSON Pointer to this resource's ref slot
64
102
  * whose target declares the projection. */
@@ -83,7 +121,16 @@ export declare function projectionKeyMap(kindSchema: unknown, projection: Schema
83
121
  * enough to double as a validator would move the domain's semantics into the
84
122
  * type layer.
85
123
  */
86
- export declare function projectEntries(manifest: unknown, projection: SchemaProjection, map: SchemaMap): Record<string, unknown> | undefined;
124
+ export declare function projectEntries(manifest: unknown, projection: SchemaProjection, map: SchemaMap,
125
+ /** What a REFERENCE at the keyed field is resolved through, and where a
126
+ * failure to resolve one is reported. A caller with no scope cannot resolve
127
+ * one, so such an entry projects OPEN — present, untyped — rather than
128
+ * vanishing from the row. */
129
+ options?: {
130
+ readonly scope?: ProjectionScope;
131
+ readonly pointer?: string;
132
+ readonly failures?: ProjectionFailure[];
133
+ }): Record<string, unknown> | undefined;
87
134
  /** A reference as the analyzer sees it: the internal `{kind, name, alias?}`
88
135
  * shape `resolveRefSentinels` rewrites `!ref` to. */
89
136
  export interface ProjectionRef {
@@ -91,8 +138,17 @@ export interface ProjectionRef {
91
138
  readonly kind?: string;
92
139
  readonly alias?: string;
93
140
  }
94
- /** The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
95
- * a host whose slot may hold EITHER shape can fall back to this reading. */
141
+ /**
142
+ * The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
143
+ * a host whose slot may hold EITHER shape can fall back to this reading.
144
+ *
145
+ * The unresolved `!ref` SENTINEL is read too. `resolveRefSentinels` normally
146
+ * rewrites one before this pass, but not when the reference names nothing — and
147
+ * that is exactly when a projection failure is reported, so reading only the
148
+ * resolved shape made the diagnostic name the target `<unnamed>`, which is the
149
+ * one fact the author needed from it. A round-trip host (`compile` off) carries
150
+ * the sentinel for every reference, resolved or not.
151
+ */
96
152
  export declare function readProjectionRef(value: unknown): ProjectionRef | undefined;
97
153
  /** What a reference resolved to. `"ambiguous"` is distinct from `undefined`
98
154
  * because the two need different advice: one says disambiguate, the other says
@@ -139,6 +195,16 @@ export interface ProjectionScope {
139
195
  * analysis has.
140
196
  */
141
197
  export declare function manifestListScope(manifests: readonly Record<string, any>[], resolveDefinition: (kind: string) => Record<string, any> | undefined): ProjectionScope;
198
+ /**
199
+ * Why a slot could not be typed from a projection.
200
+ *
201
+ * Each reason is a DIFFERENT repair, which is why the three ways a target can
202
+ * carry no usable projection are kept apart rather than collapsed into
203
+ * `no-projection`: that one message ("declares no 'x-telo-schema-projection'")
204
+ * was printed for a kind that declares one whose key field carries no map, and
205
+ * for a declaration whose entry collection is simply absent — accusing the wrong
206
+ * author of the wrong omission in both.
207
+ */
142
208
  export type ProjectionFailure = {
143
209
  readonly reason: "no-ref";
144
210
  readonly pointer: string;
@@ -150,10 +216,49 @@ export type ProjectionFailure = {
150
216
  readonly reason: "ambiguous";
151
217
  readonly pointer: string;
152
218
  readonly name: string;
153
- } | {
219
+ }
220
+ /** The target's KIND declares no `x-telo-schema-projection` at all. */
221
+ | {
154
222
  readonly reason: "no-projection";
155
223
  readonly pointer: string;
156
224
  readonly kind: string;
225
+ }
226
+ /** It declares one, but the field it keys on carries no `x-telo-schema-map`. */
227
+ | {
228
+ readonly reason: "no-projection-map";
229
+ readonly pointer: string;
230
+ readonly kind: string;
231
+ }
232
+ /** Both are declared and the DECLARATION holds no entry collection to project
233
+ * — an absent `columns:`, or a value that is not a collection. */
234
+ | {
235
+ readonly reason: "no-entries";
236
+ readonly pointer: string;
237
+ readonly kind: string;
238
+ readonly entries: string;
239
+ }
240
+ /** The slot names a resource the module does not DECLARE — a library's
241
+ * `resources:` input, standing in for an instance its importer supplies. A
242
+ * projection is DECLARATION-derived, so it cannot be answered here at all;
243
+ * the stand-in has no entries, and reporting that would tell the library
244
+ * author their block is wrong when it is correct. Resolution moves to the
245
+ * injection site, where the real declaration is. Carried as its own reason
246
+ * rather than as `no-entries` so a consumer can tell "unanswerable here"
247
+ * from "answered, and empty". */
248
+ | {
249
+ readonly reason: "injected";
250
+ readonly pointer: string;
251
+ readonly name: string;
252
+ }
253
+ /** An ENTRY of the projected declaration references a shape that could not be
254
+ * read. Reported rather than dropped: the entry would silently vanish from
255
+ * the projected row, so a consumer naming it would be told the property does
256
+ * not exist. */
257
+ | {
258
+ readonly reason: "entry-reference";
259
+ readonly pointer: string;
260
+ readonly entry: string;
261
+ readonly name: string;
157
262
  };
158
263
  export declare function describeProjectionFailure(failure: ProjectionFailure): string;
159
264
  /**
@@ -166,6 +271,16 @@ export declare function describeProjectionFailure(failure: ProjectionFailure): s
166
271
  * `failures`, because degrading SILENTLY is the failure this whole mechanism
167
272
  * exists to move earlier: the consumer's contract quietly reopens and a
168
273
  * misspelled field passes `telo check` exactly as it did before.
274
+ *
275
+ * **A node that projected NOTHING is returned by IDENTITY**, and that is a
276
+ * correctness property of the caller rather than a micro-optimization:
277
+ * `DefinitionRegistry` memoizes a compiled AJV validator per schema OBJECT,
278
+ * because every resource of a kind is checked against the same one at keystroke
279
+ * time. Rebuilding each node unconditionally — which this did — misses that memo
280
+ * for every resource in the analysis, so AJV recompiled the whole kind schema
281
+ * once per resource: on `apps/hub` that was 197 compiles instead of 54, and 723
282
+ * ms instead of 97. Returning the input where nothing changed restores it for
283
+ * every kind that declares no projection at all, which is nearly all of them.
169
284
  */
170
285
  export declare function resolveSchemaProjections(schema: unknown, manifest: Record<string, any> | undefined, scope: ProjectionScope, failures?: ProjectionFailure[]): unknown;
171
286
  //# sourceMappingURL=schema-projection.d.ts.map