@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.
- package/dist/analyzer.d.ts +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +194 -65
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +67 -16
- package/dist/call-graph.d.ts.map +1 -1
- package/dist/call-graph.js +7 -1
- package/dist/cel-environment.d.ts +12 -0
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +35 -19
- package/dist/cel-scope.d.ts +8 -0
- package/dist/cel-scope.d.ts.map +1 -1
- package/dist/cel-scope.js +89 -11
- package/dist/definition-registry.d.ts +41 -0
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +76 -0
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +65 -0
- package/dist/eval-paths.d.ts +68 -0
- package/dist/eval-paths.d.ts.map +1 -1
- package/dist/eval-paths.js +138 -0
- package/dist/flatten-for-analyzer.d.ts +37 -1
- package/dist/flatten-for-analyzer.d.ts.map +1 -1
- package/dist/flatten-for-analyzer.js +104 -5
- package/dist/import-resolution-diagnostics.d.ts +1 -1
- package/dist/import-resolution-diagnostics.js +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/inline-imports.d.ts.map +1 -1
- package/dist/inline-imports.js +1 -0
- package/dist/manifest-visitor.d.ts +4 -0
- package/dist/manifest-visitor.d.ts.map +1 -1
- package/dist/manifest-visitor.js +36 -1
- package/dist/migrations/entries/index.d.ts.map +1 -1
- package/dist/migrations/entries/index.js +2 -0
- package/dist/migrations/entries/schema-prepare-bucket.json +27 -0
- package/dist/peer-binding.d.ts +232 -0
- package/dist/peer-binding.d.ts.map +1 -0
- package/dist/peer-binding.js +418 -0
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +8 -0
- package/dist/ref-slot.d.ts +13 -0
- package/dist/ref-slot.d.ts.map +1 -1
- package/dist/ref-slot.js +26 -0
- package/dist/reference-field-map.d.ts +28 -0
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +25 -0
- package/dist/referrer-rule.d.ts +17 -0
- package/dist/referrer-rule.d.ts.map +1 -1
- package/dist/referrer-rule.js +4 -1
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.js +1 -1
- package/dist/resolve-ref-sentinels.d.ts.map +1 -1
- package/dist/resolve-ref-sentinels.js +14 -1
- package/dist/resource-input.d.ts +75 -0
- package/dist/resource-input.d.ts.map +1 -0
- package/dist/resource-input.js +90 -0
- package/dist/resource-rule.d.ts +40 -27
- package/dist/resource-rule.d.ts.map +1 -1
- package/dist/resource-rule.js +61 -7
- package/dist/rule-condition.d.ts +11 -0
- package/dist/rule-condition.d.ts.map +1 -1
- package/dist/rule-condition.js +13 -0
- package/dist/schema-projection.d.ts +119 -4
- package/dist/schema-projection.d.ts.map +1 -1
- package/dist/schema-projection.js +213 -18
- package/dist/system-kinds.d.ts +7 -2
- package/dist/system-kinds.d.ts.map +1 -1
- package/dist/system-kinds.js +7 -2
- package/dist/telo-version.d.ts +1 -1
- package/dist/telo-version.js +1 -1
- package/dist/template-body.d.ts +50 -0
- package/dist/template-body.d.ts.map +1 -0
- package/dist/template-body.js +58 -0
- package/dist/validate-cel-context.d.ts +1 -17
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +73 -78
- package/dist/validate-identifier-names.d.ts.map +1 -1
- package/dist/validate-identifier-names.js +17 -2
- package/dist/validate-reference-forms.d.ts +10 -0
- package/dist/validate-reference-forms.d.ts.map +1 -1
- package/dist/validate-reference-forms.js +23 -0
- package/dist/validate-references.d.ts +17 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +77 -18
- package/dist/validate-referrer-rules.d.ts +38 -4
- package/dist/validate-referrer-rules.d.ts.map +1 -1
- package/dist/validate-referrer-rules.js +139 -31
- package/dist/validate-resource-inputs.d.ts +35 -0
- package/dist/validate-resource-inputs.d.ts.map +1 -0
- package/dist/validate-resource-inputs.js +319 -0
- package/dist/validate-resource-rules.d.ts +11 -2
- package/dist/validate-resource-rules.d.ts.map +1 -1
- package/dist/validate-resource-rules.js +18 -12
- package/dist/validate-schema-projection.d.ts.map +1 -1
- package/dist/validate-schema-projection.js +33 -2
- package/dist/validate-template-dispatch.d.ts +27 -0
- package/dist/validate-template-dispatch.d.ts.map +1 -0
- package/dist/validate-template-dispatch.js +95 -0
- package/dist/with-synthetic-positions.d.ts +1 -1
- package/dist/with-synthetic-positions.js +1 -1
- package/package.json +3 -3
- package/src/analyzer.ts +236 -70
- package/src/builtins.ts +69 -16
- package/src/call-graph.ts +7 -1
- package/src/cel-environment.ts +48 -19
- package/src/cel-scope.ts +113 -19
- package/src/definition-registry.ts +78 -0
- package/src/dependency-graph.ts +66 -0
- package/src/eval-paths.ts +152 -0
- package/src/flatten-for-analyzer.ts +117 -4
- package/src/import-resolution-diagnostics.ts +1 -1
- package/src/index.ts +57 -4
- package/src/inline-imports.ts +1 -0
- package/src/manifest-visitor.ts +41 -1
- package/src/migrations/entries/index.ts +2 -0
- package/src/migrations/entries/schema-prepare-bucket.json +27 -0
- package/src/peer-binding.ts +641 -0
- package/src/precompile.ts +8 -0
- package/src/ref-slot.ts +36 -0
- package/src/reference-field-map.ts +42 -0
- package/src/referrer-rule.ts +20 -1
- package/src/release/index.ts +1 -1
- package/src/resolve-ref-sentinels.ts +12 -1
- package/src/resource-input.ts +132 -0
- package/src/resource-rule.ts +69 -7
- package/src/rule-condition.ts +15 -0
- package/src/schema-projection.ts +302 -20
- package/src/system-kinds.ts +7 -2
- package/src/telo-version.ts +1 -1
- package/src/template-body.ts +104 -0
- package/src/validate-cel-context.ts +73 -78
- package/src/validate-identifier-names.ts +18 -3
- package/src/validate-reference-forms.ts +22 -0
- package/src/validate-references.ts +85 -16
- package/src/validate-referrer-rules.ts +215 -28
- package/src/validate-resource-inputs.ts +367 -0
- package/src/validate-resource-rules.ts +26 -12
- package/src/validate-schema-projection.ts +50 -1
- package/src/validate-template-dispatch.ts +99 -0
- package/src/with-synthetic-positions.ts +1 -1
package/dist/release/index.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/dist/release/index.js
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
|
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
|
+
}
|
package/dist/resource-rule.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* the subject is skipped — and the
|
|
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):
|
|
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":"
|
|
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"}
|
package/dist/resource-rule.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* the subject is skipped — and the
|
|
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
|
-
|
|
170
|
-
|
|
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)) {
|
package/dist/rule-condition.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/rule-condition.js
CHANGED
|
@@ -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
|
|
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
|
-
/**
|
|
95
|
-
*
|
|
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
|