@telorun/analyzer 0.55.0 → 0.56.1
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/README.md +2 -2
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +9 -4
- package/dist/binary-slot.d.ts +39 -0
- package/dist/binary-slot.d.ts.map +1 -0
- package/dist/binary-slot.js +58 -0
- package/dist/flatten-for-analyzer.d.ts +20 -1
- package/dist/flatten-for-analyzer.d.ts.map +1 -1
- package/dist/flatten-for-analyzer.js +48 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/kernel-globals.d.ts +25 -11
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +54 -24
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +11 -0
- package/package.json +2 -2
- package/src/analyzer.ts +8 -4
- package/src/binary-slot.ts +71 -0
- package/src/flatten-for-analyzer.ts +61 -3
- package/src/index.ts +1 -0
- package/src/kernel-globals.ts +74 -25
- package/src/schema-compat.ts +10 -0
package/README.md
CHANGED
|
@@ -23,9 +23,9 @@ Built to be language-agnostic and infinitely extensible.
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
# Reconcile your manifest into a running backend
|
|
26
|
-
$ telo ./examples/
|
|
26
|
+
$ telo ./examples/todo-app
|
|
27
27
|
|
|
28
|
-
{"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:
|
|
28
|
+
{"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8077"}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Why use Telo?
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EACL,aAAa,EAEb,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,0BAA0B,CAAC;AAsBlC,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAiBvF,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAqF/F;;;;;uDAKuD;AACvD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAClC,aAAa,GAAG;IAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAA;CAAE,CAO9F;AA6FD;0CAC0C;AAC1C,eAAO,MAAM,eAAe,GAAI,OAAO;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,KAAG,OACT,CAAC;AAErD;8EAC8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAuBD;2DAC2D;AAC3D,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EACvC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CASjC;AAED;4EAC4E;AAC5E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAmBvG;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EAAE,EAChB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EAC/C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAC3D,IAAI,CAiDN;AA8mBD,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,eAAe,CAAC,EAAE,eAAe,GAChC,kBAAkB,EAAE;
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
|
package/dist/analyzer.js
CHANGED
|
@@ -7,7 +7,7 @@ import { effectiveAuthorSchema } from "./extends-resolution.js";
|
|
|
7
7
|
import { PERMISSIVE_CONTRACT, resolveContract, } from "./invocation-contract.js";
|
|
8
8
|
import { buildCallGraph } from "./call-graph.js";
|
|
9
9
|
import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
|
|
10
|
-
import {
|
|
10
|
+
import { buildKernelGlobalsIndex, KERNEL_GLOBAL_NAMES, mergeKernelGlobalsIntoContext, } from "./kernel-globals.js";
|
|
11
11
|
import { buildObservedStateIndex, buildObservedStateResourcesSchema, collectRunReachableNames, observedStateRead, validateObservedStateDeclarations, } from "./validate-observed-state.js";
|
|
12
12
|
import { computeSuggestKind } from "./kind-suggest.js";
|
|
13
13
|
import { visitManifest } from "./manifest-visitor.js";
|
|
@@ -1361,7 +1361,7 @@ export class StaticAnalyzer {
|
|
|
1361
1361
|
: new Set();
|
|
1362
1362
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
1363
1363
|
// recognises variables, secrets, resources, env automatically
|
|
1364
|
-
const kernelGlobals =
|
|
1364
|
+
const kernelGlobals = buildKernelGlobalsIndex(allManifests, observedState);
|
|
1365
1365
|
// Fallback context for CEL in a slot with no `x-telo-context` annotation:
|
|
1366
1366
|
// everything stays open except the typed `.status` nodes, so unknown-field
|
|
1367
1367
|
// checking reaches observed state everywhere without newly rejecting any
|
|
@@ -1377,7 +1377,8 @@ export class StaticAnalyzer {
|
|
|
1377
1377
|
: null;
|
|
1378
1378
|
// The module doc (Application/Library) carries the Application-only `ports`
|
|
1379
1379
|
// namespace; threaded into per-resource CEL typing so `${{ ports.X }}`
|
|
1380
|
-
// resolves its nominal brand cross-doc.
|
|
1380
|
+
// resolves its nominal brand cross-doc. A flattened set holds exactly one —
|
|
1381
|
+
// the entry's; see `buildKernelGlobalsSchema`.
|
|
1381
1382
|
const moduleManifest = allManifests.find((mm) => mm.kind === "Telo.Application") ??
|
|
1382
1383
|
allManifests.find((mm) => mm.kind === "Telo.Library");
|
|
1383
1384
|
// Validate each non-definition, non-system resource
|
|
@@ -1849,7 +1850,11 @@ export class StaticAnalyzer {
|
|
|
1849
1850
|
aliases,
|
|
1850
1851
|
allManifests: allManifests,
|
|
1851
1852
|
});
|
|
1852
|
-
effectiveContext = mergeKernelGlobalsIntoContext(withBindingNames(resolvedContext, m),
|
|
1853
|
+
effectiveContext = mergeKernelGlobalsIntoContext(withBindingNames(resolvedContext, m),
|
|
1854
|
+
// Typed in the module that DECLARED this resource — for a manifest
|
|
1855
|
+
// forwarded from an imported library, that is its `moduleGlobals`
|
|
1856
|
+
// stamp, not the consuming application's block.
|
|
1857
|
+
kernelGlobals.forResource(m));
|
|
1853
1858
|
}
|
|
1854
1859
|
else if (observedStateContext) {
|
|
1855
1860
|
// No `x-telo-context` matched, so nothing was chain-validated here
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { KeywordDefinition } from "ajv";
|
|
2
|
+
/**
|
|
3
|
+
* `x-telo-binary: true` — the annotation that gives a byte buffer a declared
|
|
4
|
+
* identity, and the single accessor every surface reads it through (the
|
|
5
|
+
* `ref-slot.ts` precedent).
|
|
6
|
+
*
|
|
7
|
+
* WHY AN ANNOTATION RATHER THAN A TYPE. Bytes are not expressible in JSON
|
|
8
|
+
* Schema's type vocabulary. `type: object` is the closest fit and it is a lie
|
|
9
|
+
* that costs a real check: every arbitrary object satisfies it, so a mistyped
|
|
10
|
+
* `content: { foo: bar }` reaches the controller instead of failing statically.
|
|
11
|
+
* `type: binary` would fix the honesty and break everything else — AJV refuses
|
|
12
|
+
* to compile an unknown type at all (`type must be JSONType or JSONType[]`),
|
|
13
|
+
* with no option to allow it, so the schema would produce no validator; and a
|
|
14
|
+
* published `telo.yaml` would stop being JSON Schema, which is what the hub, the
|
|
15
|
+
* editor and any third-party reader consume. An `x-` keyword is ignored by
|
|
16
|
+
* unaware tooling BY SPECIFICATION, so it degrades to "unconstrained" rather
|
|
17
|
+
* than to "cannot validate this kind at all".
|
|
18
|
+
*
|
|
19
|
+
* ONE RULE, BOTH HALVES. The keyword is implemented as AJV CODEGEN rather than a
|
|
20
|
+
* `validate` function so it inlines into standalone-compiled validators — the
|
|
21
|
+
* kernel compiles and caches those, and a function-valued keyword would not
|
|
22
|
+
* survive serialization. The analyzer registers the identical keyword, and
|
|
23
|
+
* `celPlaceholderForSchema` hands a real `Uint8Array` to a CEL leaf at a binary
|
|
24
|
+
* slot, so static and runtime agree by construction instead of by two rules kept
|
|
25
|
+
* in step. What falls out is the check the annotation exists for: **bytes can
|
|
26
|
+
* never be manifest-authored** — no YAML literal is a `Uint8Array` — so a literal
|
|
27
|
+
* at a binary slot is rejected while a value arriving by reference passes.
|
|
28
|
+
*/
|
|
29
|
+
export declare const X_TELO_BINARY = "x-telo-binary";
|
|
30
|
+
/** True when this schema node declares its value to be raw bytes. */
|
|
31
|
+
export declare function isBinarySlot(schema: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The AJV keyword. `Uint8Array` is the structural test rather than any richer
|
|
34
|
+
* notion of "binary": it is what every byte producer in the runtime hands over
|
|
35
|
+
* (`Buffer` extends it, so Node buffers pass), and it is the one check that means
|
|
36
|
+
* the same thing in a browser-side analyzer and in the kernel.
|
|
37
|
+
*/
|
|
38
|
+
export declare function binaryKeyword(): KeywordDefinition;
|
|
39
|
+
//# sourceMappingURL=binary-slot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-slot.d.ts","sourceRoot":"","sources":["../src/binary-slot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,KAAK,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAMrD;AAWD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,iBAAiB,CAajD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as AjvNS from "ajv";
|
|
2
|
+
/**
|
|
3
|
+
* `x-telo-binary: true` — the annotation that gives a byte buffer a declared
|
|
4
|
+
* identity, and the single accessor every surface reads it through (the
|
|
5
|
+
* `ref-slot.ts` precedent).
|
|
6
|
+
*
|
|
7
|
+
* WHY AN ANNOTATION RATHER THAN A TYPE. Bytes are not expressible in JSON
|
|
8
|
+
* Schema's type vocabulary. `type: object` is the closest fit and it is a lie
|
|
9
|
+
* that costs a real check: every arbitrary object satisfies it, so a mistyped
|
|
10
|
+
* `content: { foo: bar }` reaches the controller instead of failing statically.
|
|
11
|
+
* `type: binary` would fix the honesty and break everything else — AJV refuses
|
|
12
|
+
* to compile an unknown type at all (`type must be JSONType or JSONType[]`),
|
|
13
|
+
* with no option to allow it, so the schema would produce no validator; and a
|
|
14
|
+
* published `telo.yaml` would stop being JSON Schema, which is what the hub, the
|
|
15
|
+
* editor and any third-party reader consume. An `x-` keyword is ignored by
|
|
16
|
+
* unaware tooling BY SPECIFICATION, so it degrades to "unconstrained" rather
|
|
17
|
+
* than to "cannot validate this kind at all".
|
|
18
|
+
*
|
|
19
|
+
* ONE RULE, BOTH HALVES. The keyword is implemented as AJV CODEGEN rather than a
|
|
20
|
+
* `validate` function so it inlines into standalone-compiled validators — the
|
|
21
|
+
* kernel compiles and caches those, and a function-valued keyword would not
|
|
22
|
+
* survive serialization. The analyzer registers the identical keyword, and
|
|
23
|
+
* `celPlaceholderForSchema` hands a real `Uint8Array` to a CEL leaf at a binary
|
|
24
|
+
* slot, so static and runtime agree by construction instead of by two rules kept
|
|
25
|
+
* in step. What falls out is the check the annotation exists for: **bytes can
|
|
26
|
+
* never be manifest-authored** — no YAML literal is a `Uint8Array` — so a literal
|
|
27
|
+
* at a binary slot is rejected while a value arriving by reference passes.
|
|
28
|
+
*/
|
|
29
|
+
export const X_TELO_BINARY = "x-telo-binary";
|
|
30
|
+
/** True when this schema node declares its value to be raw bytes. */
|
|
31
|
+
export function isBinarySlot(schema) {
|
|
32
|
+
return (typeof schema === "object" &&
|
|
33
|
+
schema !== null &&
|
|
34
|
+
schema[X_TELO_BINARY] === true);
|
|
35
|
+
}
|
|
36
|
+
const ajvExports = AjvNS.default ?? AjvNS;
|
|
37
|
+
const codegen = ajvExports._ ?? AjvNS._;
|
|
38
|
+
/**
|
|
39
|
+
* The AJV keyword. `Uint8Array` is the structural test rather than any richer
|
|
40
|
+
* notion of "binary": it is what every byte producer in the runtime hands over
|
|
41
|
+
* (`Buffer` extends it, so Node buffers pass), and it is the one check that means
|
|
42
|
+
* the same thing in a browser-side analyzer and in the kernel.
|
|
43
|
+
*/
|
|
44
|
+
export function binaryKeyword() {
|
|
45
|
+
return {
|
|
46
|
+
keyword: X_TELO_BINARY,
|
|
47
|
+
schemaType: "boolean",
|
|
48
|
+
code(cxt) {
|
|
49
|
+
// `x-telo-binary: false` states nothing, so it constrains nothing.
|
|
50
|
+
if (cxt.schema !== true)
|
|
51
|
+
return;
|
|
52
|
+
cxt.pass(codegen `${cxt.data} instanceof Uint8Array`);
|
|
53
|
+
},
|
|
54
|
+
error: {
|
|
55
|
+
message: "must be raw bytes (a Uint8Array) — bytes cannot be written inline in a manifest",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -33,8 +33,27 @@ export declare function parseExportEntry(entry: string): ParsedExportEntry;
|
|
|
33
33
|
* `byModuleName` and `!ref Alias.name` resolves, while `validate-references` /
|
|
34
34
|
* the per-resource validation loop never re-walk or re-validate it against the
|
|
35
35
|
* consumer's scope. A consumer that instead emits every module doc as a peer
|
|
36
|
-
* local manifest silently breaks both.
|
|
36
|
+
* local manifest silently breaks both.
|
|
37
|
+
*
|
|
38
|
+
* Dropping the module doc costs the consumer's analysis the one thing a
|
|
39
|
+
* forwarded manifest's CEL still needs: the `variables` / `secrets` / `ports`
|
|
40
|
+
* contract its expressions are evaluated against. That contract is carried
|
|
41
|
+
* across as {@link ModuleGlobals} on each forwarded manifest instead, so the
|
|
42
|
+
* consumer can check those reads in the DECLARING module's scope rather than
|
|
43
|
+
* either skipping them (a diagnostic performed nowhere) or judging them by its
|
|
44
|
+
* own block (a hard error the library author cannot act on). Stamped here
|
|
45
|
+
* because this is the only point where a manifest and its module doc are both
|
|
46
|
+
* in hand — by the time `analyze()` sees the flat list, the doc is gone. */
|
|
37
47
|
export declare function selectModuleManifestsForAnalysis(moduleManifests: ResourceManifest[], isRoot: boolean): ResourceManifest[];
|
|
48
|
+
/** The declaring module's config contract, carried on a forwarded manifest so
|
|
49
|
+
* the consumer's analysis can type its CEL globals in the right scope. Only the
|
|
50
|
+
* namespaces whose typing is scope-dependent: `resources` is deliberately
|
|
51
|
+
* absent, see {@link readModuleGlobals}. */
|
|
52
|
+
export interface ModuleGlobals {
|
|
53
|
+
variables?: Record<string, unknown>;
|
|
54
|
+
secrets?: Record<string, unknown>;
|
|
55
|
+
ports?: Record<string, unknown>;
|
|
56
|
+
}
|
|
38
57
|
/** Produce the flat manifest list `analyze()` consumes today.
|
|
39
58
|
*
|
|
40
59
|
* Combines the entry module's manifests with `Telo.Definition`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flatten-for-analyzer.d.ts","sourceRoot":"","sources":["../src/flatten-for-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;yEAEyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;4CAI4C;AAC5C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAGjE;AAED
|
|
1
|
+
{"version":3,"file":"flatten-for-analyzer.d.ts","sourceRoot":"","sources":["../src/flatten-for-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,7 +27,17 @@ export function parseExportEntry(entry) {
|
|
|
27
27
|
* `byModuleName` and `!ref Alias.name` resolves, while `validate-references` /
|
|
28
28
|
* the per-resource validation loop never re-walk or re-validate it against the
|
|
29
29
|
* consumer's scope. A consumer that instead emits every module doc as a peer
|
|
30
|
-
* local manifest silently breaks both.
|
|
30
|
+
* local manifest silently breaks both.
|
|
31
|
+
*
|
|
32
|
+
* Dropping the module doc costs the consumer's analysis the one thing a
|
|
33
|
+
* forwarded manifest's CEL still needs: the `variables` / `secrets` / `ports`
|
|
34
|
+
* contract its expressions are evaluated against. That contract is carried
|
|
35
|
+
* across as {@link ModuleGlobals} on each forwarded manifest instead, so the
|
|
36
|
+
* consumer can check those reads in the DECLARING module's scope rather than
|
|
37
|
+
* either skipping them (a diagnostic performed nowhere) or judging them by its
|
|
38
|
+
* own block (a hard error the library author cannot act on). Stamped here
|
|
39
|
+
* because this is the only point where a manifest and its module doc are both
|
|
40
|
+
* in hand — by the time `analyze()` sees the flat list, the doc is gone. */
|
|
31
41
|
export function selectModuleManifestsForAnalysis(moduleManifests, isRoot) {
|
|
32
42
|
if (isRoot)
|
|
33
43
|
return moduleManifests;
|
|
@@ -41,22 +51,57 @@ export function selectModuleManifestsForAnalysis(moduleManifests, isRoot) {
|
|
|
41
51
|
continue;
|
|
42
52
|
exportedResources.add(parseExportEntry(entry).name);
|
|
43
53
|
}
|
|
54
|
+
const moduleGlobals = readModuleGlobals(libDoc);
|
|
44
55
|
const out = [];
|
|
45
56
|
for (const m of moduleManifests) {
|
|
46
57
|
if (m.kind === "Telo.Definition" || m.kind === "Telo.Abstract" || m.kind === "Telo.Import") {
|
|
47
|
-
out.push(m);
|
|
58
|
+
out.push(withModuleGlobals(m, moduleGlobals));
|
|
48
59
|
}
|
|
49
60
|
else if (!isModuleKind(m.kind) &&
|
|
50
61
|
typeof m.metadata?.name === "string" &&
|
|
51
62
|
exportedResources.has(m.metadata.name)) {
|
|
52
63
|
out.push({
|
|
53
64
|
...m,
|
|
54
|
-
metadata: {
|
|
65
|
+
metadata: {
|
|
66
|
+
...m.metadata,
|
|
67
|
+
forwardedExport: true,
|
|
68
|
+
...(moduleGlobals ? { moduleGlobals } : {}),
|
|
69
|
+
},
|
|
55
70
|
});
|
|
56
71
|
}
|
|
57
72
|
}
|
|
58
73
|
return out;
|
|
59
74
|
}
|
|
75
|
+
/** Read the schema-map blocks off a module doc, or `undefined` when it declares
|
|
76
|
+
* none (the globals then stay permissive, exactly as an absent doc does).
|
|
77
|
+
*
|
|
78
|
+
* `resources` is NOT carried. The flat list holds only this module's EXPORTED
|
|
79
|
+
* instances, so a name list built from it would omit every internal one — and a
|
|
80
|
+
* `with:`-scoped resource is declared inline rather than as a doc, so it would
|
|
81
|
+
* be missing too. Both would read as "no such resource" in a consumer's pass:
|
|
82
|
+
* a false positive worse than the unchecked read it replaced. The consumer
|
|
83
|
+
* leaves a forwarded manifest's `resources` node open and the library's own
|
|
84
|
+
* analysis, which sees every name, does that check. */
|
|
85
|
+
function readModuleGlobals(libDoc) {
|
|
86
|
+
const doc = libDoc;
|
|
87
|
+
if (!doc)
|
|
88
|
+
return undefined;
|
|
89
|
+
const globals = {};
|
|
90
|
+
for (const key of ["variables", "secrets", "ports"]) {
|
|
91
|
+
const block = doc[key];
|
|
92
|
+
if (block && typeof block === "object" && !Array.isArray(block))
|
|
93
|
+
globals[key] = block;
|
|
94
|
+
}
|
|
95
|
+
return Object.keys(globals).length > 0 ? globals : undefined;
|
|
96
|
+
}
|
|
97
|
+
function withModuleGlobals(m, moduleGlobals) {
|
|
98
|
+
if (!moduleGlobals)
|
|
99
|
+
return m;
|
|
100
|
+
return {
|
|
101
|
+
...m,
|
|
102
|
+
metadata: { ...m.metadata, moduleGlobals },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
60
105
|
/** Produce the flat manifest list `analyze()` consumes today.
|
|
61
106
|
*
|
|
62
107
|
* Combines the entry module's manifests with `Telo.Definition`,
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export { buildReferenceFieldMap, isRefEntry, isScopeEntry } from "./reference-fi
|
|
|
23
23
|
export type { ReferenceFieldMap, RefFieldEntry } from "./reference-field-map.js";
|
|
24
24
|
export { hasDeclaredUse, isRefSlot, isRefUse, possibleUses, readRefSlot, REF_USES, refSlotAnnotation, rewriteRefSlotKinds, transfersControl, } from "./ref-slot.js";
|
|
25
25
|
export type { RefSlot, RefUse, RefUseCases } from "./ref-slot.js";
|
|
26
|
+
export { binaryKeyword, isBinarySlot, X_TELO_BINARY } from "./binary-slot.js";
|
|
26
27
|
export { hasProvidesZone, hasRequiresZone, readProvidesZone, readRequiresZone, rewriteRequiresZoneKind, } from "./zone-slot.js";
|
|
27
28
|
export type { ProvidesZoneSlot, RequiresZoneSlot } from "./zone-slot.js";
|
|
28
29
|
export { deriveLibraryExportRequirements, projectZoneRequirements, runZoneAnalysis, zoneDocumentsSignature, } from "./resolve-zone-requirements.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { hasIntermediateWildcard, parseRedactionPath, RedactionPathError, } from
|
|
|
12
12
|
export { buildCallGraph, projectToPairs, resourceId } from "./call-graph.js";
|
|
13
13
|
export { buildReferenceFieldMap, isRefEntry, isScopeEntry } from "./reference-field-map.js";
|
|
14
14
|
export { hasDeclaredUse, isRefSlot, isRefUse, possibleUses, readRefSlot, REF_USES, refSlotAnnotation, rewriteRefSlotKinds, transfersControl, } from "./ref-slot.js";
|
|
15
|
+
export { binaryKeyword, isBinarySlot, X_TELO_BINARY } from "./binary-slot.js";
|
|
15
16
|
export { hasProvidesZone, hasRequiresZone, readProvidesZone, readRequiresZone, rewriteRequiresZoneKind, } from "./zone-slot.js";
|
|
16
17
|
export { deriveLibraryExportRequirements, projectZoneRequirements, runZoneAnalysis, zoneDocumentsSignature, } from "./resolve-zone-requirements.js";
|
|
17
18
|
export { validateZoneSlotDeclarations } from "./validate-zone-slots.js";
|
package/dist/kernel-globals.d.ts
CHANGED
|
@@ -11,20 +11,34 @@ import type { ResourceManifest } from "@telorun/sdk";
|
|
|
11
11
|
* and referencing `variables.X` / `secrets.X`.
|
|
12
12
|
*/
|
|
13
13
|
export declare const KERNEL_GLOBAL_NAMES: readonly ["variables", "secrets", "resources", "ports"];
|
|
14
|
+
/** Kernel globals as ONE resource's declaring module sees them. */
|
|
15
|
+
export interface KernelGlobalsIndex {
|
|
16
|
+
forResource(m: ResourceManifest): Record<string, any>;
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
|
-
* Build
|
|
16
|
-
*
|
|
17
|
-
* chain-access validation recognises kernel globals without module authors
|
|
19
|
+
* Build the typed JSON Schema describing the kernel globals available to each
|
|
20
|
+
* resource in a manifest set. Used to merge into `x-telo-context` schemas so
|
|
21
|
+
* that chain-access validation recognises kernel globals without module authors
|
|
18
22
|
* having to re-declare them.
|
|
19
23
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* `variables` / `secrets` / `ports` are typed **per declaring module**, because
|
|
25
|
+
* that is the contract the resource's CEL is evaluated against at runtime. An
|
|
26
|
+
* application analysis is flattened — `selectModuleManifestsForAnalysis` drops
|
|
27
|
+
* an imported library's module doc and carries its config blocks across as
|
|
28
|
+
* `metadata.moduleGlobals` instead — so a resource forwarded from a library is
|
|
29
|
+
* typed from that stamp, and everything else from the entry module's own doc
|
|
30
|
+
* (the only module doc left in the set).
|
|
31
|
+
*
|
|
32
|
+
* Typing every resource from the entry doc is what made a library's
|
|
33
|
+
* `variables.x` a hard error the library author could not act on, and — in the
|
|
34
|
+
* other direction — let a library read a variable it never declared whenever the
|
|
35
|
+
* app happened to declare that name.
|
|
36
|
+
*
|
|
37
|
+
* `resources` is NOT per module: it enumerates every non-system resource name in
|
|
38
|
+
* the set, and stays OPEN for a forwarded manifest (see `readModuleGlobals` for
|
|
39
|
+
* why a name list cannot be carried across the boundary honestly).
|
|
26
40
|
*/
|
|
27
|
-
export declare function
|
|
41
|
+
export declare function buildKernelGlobalsIndex(manifests: ResourceManifest[],
|
|
28
42
|
/** Every resource a CEL read can name, including scope-declared ones (see
|
|
29
43
|
* `buildObservedStateIndex`). Kinds that declare a `status:` get a typed,
|
|
30
44
|
* closed `status` node; every other resource node stays open, so no flat read
|
|
@@ -32,7 +46,7 @@ export declare function buildKernelGlobalsSchema(manifests: ResourceManifest[],
|
|
|
32
46
|
resources?: ReadonlyMap<string, {
|
|
33
47
|
kind: string;
|
|
34
48
|
status?: Record<string, any>;
|
|
35
|
-
}>):
|
|
49
|
+
}>): KernelGlobalsIndex;
|
|
36
50
|
/**
|
|
37
51
|
* Merge kernel globals into an `x-telo-context` schema so chain-access
|
|
38
52
|
* validation recognises `variables`, `secrets`, `resources`, `ports`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,yDAA0D,CAAC;AAS3F
|
|
1
|
+
{"version":3,"file":"kernel-globals.d.ts","sourceRoot":"","sources":["../src/kernel-globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,yDAA0D,CAAC;AAS3F,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,gBAAgB,EAAE;AAC7B;;;2CAG2C;AAC3C,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,GAC9E,kBAAkB,CA0BpB;AAmGD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrB"}
|
package/dist/kernel-globals.js
CHANGED
|
@@ -19,26 +19,68 @@ const SYSTEM_KINDS = new Set([
|
|
|
19
19
|
"Telo.Abstract",
|
|
20
20
|
]);
|
|
21
21
|
/**
|
|
22
|
-
* Build
|
|
23
|
-
*
|
|
24
|
-
* chain-access validation recognises kernel globals without module authors
|
|
22
|
+
* Build the typed JSON Schema describing the kernel globals available to each
|
|
23
|
+
* resource in a manifest set. Used to merge into `x-telo-context` schemas so
|
|
24
|
+
* that chain-access validation recognises kernel globals without module authors
|
|
25
25
|
* having to re-declare them.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
27
|
+
* `variables` / `secrets` / `ports` are typed **per declaring module**, because
|
|
28
|
+
* that is the contract the resource's CEL is evaluated against at runtime. An
|
|
29
|
+
* application analysis is flattened — `selectModuleManifestsForAnalysis` drops
|
|
30
|
+
* an imported library's module doc and carries its config blocks across as
|
|
31
|
+
* `metadata.moduleGlobals` instead — so a resource forwarded from a library is
|
|
32
|
+
* typed from that stamp, and everything else from the entry module's own doc
|
|
33
|
+
* (the only module doc left in the set).
|
|
34
|
+
*
|
|
35
|
+
* Typing every resource from the entry doc is what made a library's
|
|
36
|
+
* `variables.x` a hard error the library author could not act on, and — in the
|
|
37
|
+
* other direction — let a library read a variable it never declared whenever the
|
|
38
|
+
* app happened to declare that name.
|
|
39
|
+
*
|
|
40
|
+
* `resources` is NOT per module: it enumerates every non-system resource name in
|
|
41
|
+
* the set, and stays OPEN for a forwarded manifest (see `readModuleGlobals` for
|
|
42
|
+
* why a name list cannot be carried across the boundary honestly).
|
|
33
43
|
*/
|
|
34
|
-
export function
|
|
44
|
+
export function buildKernelGlobalsIndex(manifests,
|
|
35
45
|
/** Every resource a CEL read can name, including scope-declared ones (see
|
|
36
46
|
* `buildObservedStateIndex`). Kinds that declare a `status:` get a typed,
|
|
37
47
|
* closed `status` node; every other resource node stays open, so no flat read
|
|
38
48
|
* that passes today can start failing. */
|
|
39
49
|
resources) {
|
|
40
|
-
const
|
|
50
|
+
const entryDoc = manifests.find((m) => m.kind === "Telo.Application") ??
|
|
41
51
|
manifests.find((m) => m.kind === "Telo.Library");
|
|
52
|
+
const entrySchema = globalsSchema(entryDoc, buildResourcesSchema(manifests, resources));
|
|
53
|
+
const openResources = { type: "object", additionalProperties: true };
|
|
54
|
+
const byModule = new Map();
|
|
55
|
+
return {
|
|
56
|
+
forResource(m) {
|
|
57
|
+
const meta = m.metadata;
|
|
58
|
+
const stamped = meta?.moduleGlobals;
|
|
59
|
+
if (!stamped)
|
|
60
|
+
return entrySchema;
|
|
61
|
+
const key = meta?.module ?? "";
|
|
62
|
+
const cached = byModule.get(key);
|
|
63
|
+
if (cached)
|
|
64
|
+
return cached;
|
|
65
|
+
const schema = globalsSchema(stamped, openResources);
|
|
66
|
+
byModule.set(key, schema);
|
|
67
|
+
return schema;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function globalsSchema(doc, resourcesSchema) {
|
|
72
|
+
return {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
variables: buildSchemaMapSchema(doc?.variables),
|
|
76
|
+
secrets: buildSchemaMapSchema(doc?.secrets),
|
|
77
|
+
resources: resourcesSchema,
|
|
78
|
+
ports: buildPortsSchema(doc?.ports),
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Every non-system resource name in the set, plus the scope-declared ones. */
|
|
83
|
+
function buildResourcesSchema(manifests, resources) {
|
|
42
84
|
const resourceProps = {};
|
|
43
85
|
for (const m of manifests) {
|
|
44
86
|
const name = m.metadata?.name;
|
|
@@ -67,19 +109,7 @@ resources) {
|
|
|
67
109
|
continue;
|
|
68
110
|
applyObservedStateNode(resourceProps, key, entry.status);
|
|
69
111
|
}
|
|
70
|
-
return {
|
|
71
|
-
type: "object",
|
|
72
|
-
properties: {
|
|
73
|
-
variables: buildSchemaMapSchema(moduleManifest?.variables),
|
|
74
|
-
secrets: buildSchemaMapSchema(moduleManifest?.secrets),
|
|
75
|
-
resources: {
|
|
76
|
-
type: "object",
|
|
77
|
-
properties: resourceProps,
|
|
78
|
-
additionalProperties: false,
|
|
79
|
-
},
|
|
80
|
-
ports: buildPortsSchema(moduleManifest?.ports),
|
|
81
|
-
},
|
|
82
|
-
};
|
|
112
|
+
return { type: "object", properties: resourceProps, additionalProperties: false };
|
|
83
113
|
}
|
|
84
114
|
/** Build the closed `ports` chain-access schema: each declared port is an
|
|
85
115
|
* integer, so `ports.<name>` resolves and `ports.typo` (or member access past
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-compat.d.ts","sourceRoot":"","sources":["../src/schema-compat.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema-compat.d.ts","sourceRoot":"","sources":["../src/schema-compat.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,GAAG,KAA0C,CAAC;AAEpD;;;;;;;mCAOmC;AACnC,wBAAgB,SAAS,IAAI,YAAY,CAAC,OAAO,GAAG,CAAC,CAWpD;AAKD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;oEAEoE;AACpE,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC1B,mBAAmB,CAIrB;AAiDD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAelD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGxE;AAuBD,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;CACd;AAaD,0GAA0G;AAC1G,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,WAAW,EAAE,CA2B/F;AAED;qFACqF;AACrF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ7E;AAED;;;;6DAI6D;AAC7D,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAsBjC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGnD,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGzF;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,CAyBnF;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAiChG;AAkED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CA0C/E;AAqBD,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAOtG;AAyDD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAwBlF;AAED;iGACiG;AACjG,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;AAChC;;;;;;;;6DAQ6D;AAC7D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EACrC,IAAI,SAAK,GACR,OAAO,CAgDT"}
|
package/dist/schema-compat.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AjvModule from "ajv";
|
|
2
2
|
import addFormats from "ajv-formats";
|
|
3
3
|
import { isRefSentinel, isTaggedSentinel, ManifestRootSchema, normalizeRefSlots } from "@telorun/templating";
|
|
4
|
+
import { binaryKeyword, isBinarySlot } from "./binary-slot.js";
|
|
4
5
|
const Ajv = AjvModule.default ?? AjvModule;
|
|
5
6
|
/** Creates a configured AJV instance (allErrors, strict: false, with formats).
|
|
6
7
|
* Also registers the kernel manifest root schema under `telo://manifest` so
|
|
@@ -15,6 +16,10 @@ export function createAjv() {
|
|
|
15
16
|
addFormats.default
|
|
16
17
|
? addFormats.default(instance)
|
|
17
18
|
: addFormats(instance);
|
|
19
|
+
// Bytes have no JSON Schema type, so the annotation carries the check. Registered
|
|
20
|
+
// here and in the kernel's validators from one definition, so a literal at a byte
|
|
21
|
+
// slot is rejected statically and at dispatch by the identical rule.
|
|
22
|
+
instance.addKeyword(binaryKeyword());
|
|
18
23
|
instance.addSchema(ManifestRootSchema);
|
|
19
24
|
return instance;
|
|
20
25
|
}
|
|
@@ -352,6 +357,12 @@ function foldedConstraints(schema) {
|
|
|
352
357
|
}
|
|
353
358
|
export function celPlaceholderForSchema(rawSchema) {
|
|
354
359
|
const schema = foldedConstraints(rawSchema);
|
|
360
|
+
// A byte slot's placeholder must BE bytes: the same keyword validates statically
|
|
361
|
+
// and at dispatch, so a CEL leaf standing in for a runtime buffer has to satisfy
|
|
362
|
+
// it. This is what keeps the rule single — a literal is rejected because no YAML
|
|
363
|
+
// literal is a Uint8Array, while a value arriving by reference passes.
|
|
364
|
+
if (isBinarySlot(schema))
|
|
365
|
+
return new Uint8Array();
|
|
355
366
|
if (schema.default !== undefined)
|
|
356
367
|
return schema.default;
|
|
357
368
|
// An enum-constrained field needs a placeholder drawn from the enum: the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/analyzer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.56.1",
|
|
4
4
|
"description": "Telo Analyzer - Static manifest validator for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@types/node": "^20.0.0",
|
|
49
49
|
"typescript": "^5.0.0",
|
|
50
50
|
"vitest": "^2.1.8",
|
|
51
|
-
"@telorun/sdk": "0.
|
|
51
|
+
"@telorun/sdk": "0.70.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@telorun/sdk": "*"
|
package/src/analyzer.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import { buildCallGraph } from "./call-graph.js";
|
|
26
26
|
import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
|
|
27
27
|
import {
|
|
28
|
-
|
|
28
|
+
buildKernelGlobalsIndex,
|
|
29
29
|
KERNEL_GLOBAL_NAMES,
|
|
30
30
|
mergeKernelGlobalsIntoContext,
|
|
31
31
|
} from "./kernel-globals.js";
|
|
@@ -1613,7 +1613,7 @@ export class StaticAnalyzer {
|
|
|
1613
1613
|
|
|
1614
1614
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
1615
1615
|
// recognises variables, secrets, resources, env automatically
|
|
1616
|
-
const kernelGlobals =
|
|
1616
|
+
const kernelGlobals = buildKernelGlobalsIndex(allManifests, observedState);
|
|
1617
1617
|
|
|
1618
1618
|
// Fallback context for CEL in a slot with no `x-telo-context` annotation:
|
|
1619
1619
|
// everything stays open except the typed `.status` nodes, so unknown-field
|
|
@@ -1632,7 +1632,8 @@ export class StaticAnalyzer {
|
|
|
1632
1632
|
|
|
1633
1633
|
// The module doc (Application/Library) carries the Application-only `ports`
|
|
1634
1634
|
// namespace; threaded into per-resource CEL typing so `${{ ports.X }}`
|
|
1635
|
-
// resolves its nominal brand cross-doc.
|
|
1635
|
+
// resolves its nominal brand cross-doc. A flattened set holds exactly one —
|
|
1636
|
+
// the entry's; see `buildKernelGlobalsSchema`.
|
|
1636
1637
|
const moduleManifest =
|
|
1637
1638
|
allManifests.find((mm) => mm.kind === "Telo.Application") ??
|
|
1638
1639
|
allManifests.find((mm) => mm.kind === "Telo.Library");
|
|
@@ -2217,7 +2218,10 @@ export class StaticAnalyzer {
|
|
|
2217
2218
|
});
|
|
2218
2219
|
effectiveContext = mergeKernelGlobalsIntoContext(
|
|
2219
2220
|
withBindingNames(resolvedContext, m as Record<string, any>),
|
|
2220
|
-
|
|
2221
|
+
// Typed in the module that DECLARED this resource — for a manifest
|
|
2222
|
+
// forwarded from an imported library, that is its `moduleGlobals`
|
|
2223
|
+
// stamp, not the consuming application's block.
|
|
2224
|
+
kernelGlobals.forResource(m),
|
|
2221
2225
|
);
|
|
2222
2226
|
} else if (observedStateContext) {
|
|
2223
2227
|
// No `x-telo-context` matched, so nothing was chain-validated here
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as AjvNS from "ajv";
|
|
2
|
+
import type { KeywordDefinition } from "ajv";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `x-telo-binary: true` — the annotation that gives a byte buffer a declared
|
|
6
|
+
* identity, and the single accessor every surface reads it through (the
|
|
7
|
+
* `ref-slot.ts` precedent).
|
|
8
|
+
*
|
|
9
|
+
* WHY AN ANNOTATION RATHER THAN A TYPE. Bytes are not expressible in JSON
|
|
10
|
+
* Schema's type vocabulary. `type: object` is the closest fit and it is a lie
|
|
11
|
+
* that costs a real check: every arbitrary object satisfies it, so a mistyped
|
|
12
|
+
* `content: { foo: bar }` reaches the controller instead of failing statically.
|
|
13
|
+
* `type: binary` would fix the honesty and break everything else — AJV refuses
|
|
14
|
+
* to compile an unknown type at all (`type must be JSONType or JSONType[]`),
|
|
15
|
+
* with no option to allow it, so the schema would produce no validator; and a
|
|
16
|
+
* published `telo.yaml` would stop being JSON Schema, which is what the hub, the
|
|
17
|
+
* editor and any third-party reader consume. An `x-` keyword is ignored by
|
|
18
|
+
* unaware tooling BY SPECIFICATION, so it degrades to "unconstrained" rather
|
|
19
|
+
* than to "cannot validate this kind at all".
|
|
20
|
+
*
|
|
21
|
+
* ONE RULE, BOTH HALVES. The keyword is implemented as AJV CODEGEN rather than a
|
|
22
|
+
* `validate` function so it inlines into standalone-compiled validators — the
|
|
23
|
+
* kernel compiles and caches those, and a function-valued keyword would not
|
|
24
|
+
* survive serialization. The analyzer registers the identical keyword, and
|
|
25
|
+
* `celPlaceholderForSchema` hands a real `Uint8Array` to a CEL leaf at a binary
|
|
26
|
+
* slot, so static and runtime agree by construction instead of by two rules kept
|
|
27
|
+
* in step. What falls out is the check the annotation exists for: **bytes can
|
|
28
|
+
* never be manifest-authored** — no YAML literal is a `Uint8Array` — so a literal
|
|
29
|
+
* at a binary slot is rejected while a value arriving by reference passes.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export const X_TELO_BINARY = "x-telo-binary";
|
|
33
|
+
|
|
34
|
+
/** True when this schema node declares its value to be raw bytes. */
|
|
35
|
+
export function isBinarySlot(schema: unknown): boolean {
|
|
36
|
+
return (
|
|
37
|
+
typeof schema === "object" &&
|
|
38
|
+
schema !== null &&
|
|
39
|
+
(schema as Record<string, unknown>)[X_TELO_BINARY] === true
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// AJV's codegen template tag. The package is consumed in both ESM and CJS interop
|
|
44
|
+
// shapes, so the named export may sit on the namespace or behind `.default` —
|
|
45
|
+
// the same fallback `schema-compat` uses to reach the constructor. Skipping it
|
|
46
|
+
// would leave `codegen` undefined under a loader that does not detect AJV's named
|
|
47
|
+
// CJS re-exports, and the keyword would throw on its first compile.
|
|
48
|
+
type CodegenTag = (s: TemplateStringsArray, ...a: unknown[]) => unknown;
|
|
49
|
+
const ajvExports = (AjvNS as any).default ?? AjvNS;
|
|
50
|
+
const codegen: CodegenTag = ajvExports._ ?? (AjvNS as any)._;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The AJV keyword. `Uint8Array` is the structural test rather than any richer
|
|
54
|
+
* notion of "binary": it is what every byte producer in the runtime hands over
|
|
55
|
+
* (`Buffer` extends it, so Node buffers pass), and it is the one check that means
|
|
56
|
+
* the same thing in a browser-side analyzer and in the kernel.
|
|
57
|
+
*/
|
|
58
|
+
export function binaryKeyword(): KeywordDefinition {
|
|
59
|
+
return {
|
|
60
|
+
keyword: X_TELO_BINARY,
|
|
61
|
+
schemaType: "boolean",
|
|
62
|
+
code(cxt: any) {
|
|
63
|
+
// `x-telo-binary: false` states nothing, so it constrains nothing.
|
|
64
|
+
if (cxt.schema !== true) return;
|
|
65
|
+
cxt.pass(codegen`${cxt.data} instanceof Uint8Array`);
|
|
66
|
+
},
|
|
67
|
+
error: {
|
|
68
|
+
message: "must be raw bytes (a Uint8Array) — bytes cannot be written inline in a manifest",
|
|
69
|
+
},
|
|
70
|
+
} as KeywordDefinition;
|
|
71
|
+
}
|
|
@@ -41,7 +41,17 @@ export function parseExportEntry(entry: string): ParsedExportEntry {
|
|
|
41
41
|
* `byModuleName` and `!ref Alias.name` resolves, while `validate-references` /
|
|
42
42
|
* the per-resource validation loop never re-walk or re-validate it against the
|
|
43
43
|
* consumer's scope. A consumer that instead emits every module doc as a peer
|
|
44
|
-
* local manifest silently breaks both.
|
|
44
|
+
* local manifest silently breaks both.
|
|
45
|
+
*
|
|
46
|
+
* Dropping the module doc costs the consumer's analysis the one thing a
|
|
47
|
+
* forwarded manifest's CEL still needs: the `variables` / `secrets` / `ports`
|
|
48
|
+
* contract its expressions are evaluated against. That contract is carried
|
|
49
|
+
* across as {@link ModuleGlobals} on each forwarded manifest instead, so the
|
|
50
|
+
* consumer can check those reads in the DECLARING module's scope rather than
|
|
51
|
+
* either skipping them (a diagnostic performed nowhere) or judging them by its
|
|
52
|
+
* own block (a hard error the library author cannot act on). Stamped here
|
|
53
|
+
* because this is the only point where a manifest and its module doc are both
|
|
54
|
+
* in hand — by the time `analyze()` sees the flat list, the doc is gone. */
|
|
45
55
|
export function selectModuleManifestsForAnalysis(
|
|
46
56
|
moduleManifests: ResourceManifest[],
|
|
47
57
|
isRoot: boolean,
|
|
@@ -58,10 +68,12 @@ export function selectModuleManifestsForAnalysis(
|
|
|
58
68
|
exportedResources.add(parseExportEntry(entry).name);
|
|
59
69
|
}
|
|
60
70
|
|
|
71
|
+
const moduleGlobals = readModuleGlobals(libDoc);
|
|
72
|
+
|
|
61
73
|
const out: ResourceManifest[] = [];
|
|
62
74
|
for (const m of moduleManifests) {
|
|
63
75
|
if (m.kind === "Telo.Definition" || m.kind === "Telo.Abstract" || m.kind === "Telo.Import") {
|
|
64
|
-
out.push(m);
|
|
76
|
+
out.push(withModuleGlobals(m, moduleGlobals));
|
|
65
77
|
} else if (
|
|
66
78
|
!isModuleKind(m.kind) &&
|
|
67
79
|
typeof m.metadata?.name === "string" &&
|
|
@@ -69,13 +81,59 @@ export function selectModuleManifestsForAnalysis(
|
|
|
69
81
|
) {
|
|
70
82
|
out.push({
|
|
71
83
|
...m,
|
|
72
|
-
metadata: {
|
|
84
|
+
metadata: {
|
|
85
|
+
...m.metadata,
|
|
86
|
+
forwardedExport: true,
|
|
87
|
+
...(moduleGlobals ? { moduleGlobals } : {}),
|
|
88
|
+
} as ResourceManifest["metadata"],
|
|
73
89
|
});
|
|
74
90
|
}
|
|
75
91
|
}
|
|
76
92
|
return out;
|
|
77
93
|
}
|
|
78
94
|
|
|
95
|
+
/** The declaring module's config contract, carried on a forwarded manifest so
|
|
96
|
+
* the consumer's analysis can type its CEL globals in the right scope. Only the
|
|
97
|
+
* namespaces whose typing is scope-dependent: `resources` is deliberately
|
|
98
|
+
* absent, see {@link readModuleGlobals}. */
|
|
99
|
+
export interface ModuleGlobals {
|
|
100
|
+
variables?: Record<string, unknown>;
|
|
101
|
+
secrets?: Record<string, unknown>;
|
|
102
|
+
ports?: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Read the schema-map blocks off a module doc, or `undefined` when it declares
|
|
106
|
+
* none (the globals then stay permissive, exactly as an absent doc does).
|
|
107
|
+
*
|
|
108
|
+
* `resources` is NOT carried. The flat list holds only this module's EXPORTED
|
|
109
|
+
* instances, so a name list built from it would omit every internal one — and a
|
|
110
|
+
* `with:`-scoped resource is declared inline rather than as a doc, so it would
|
|
111
|
+
* be missing too. Both would read as "no such resource" in a consumer's pass:
|
|
112
|
+
* a false positive worse than the unchecked read it replaced. The consumer
|
|
113
|
+
* leaves a forwarded manifest's `resources` node open and the library's own
|
|
114
|
+
* analysis, which sees every name, does that check. */
|
|
115
|
+
function readModuleGlobals(libDoc: ResourceManifest | undefined): ModuleGlobals | undefined {
|
|
116
|
+
const doc = libDoc as Record<string, any> | undefined;
|
|
117
|
+
if (!doc) return undefined;
|
|
118
|
+
const globals: ModuleGlobals = {};
|
|
119
|
+
for (const key of ["variables", "secrets", "ports"] as const) {
|
|
120
|
+
const block = doc[key];
|
|
121
|
+
if (block && typeof block === "object" && !Array.isArray(block)) globals[key] = block;
|
|
122
|
+
}
|
|
123
|
+
return Object.keys(globals).length > 0 ? globals : undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function withModuleGlobals(
|
|
127
|
+
m: ResourceManifest,
|
|
128
|
+
moduleGlobals: ModuleGlobals | undefined,
|
|
129
|
+
): ResourceManifest {
|
|
130
|
+
if (!moduleGlobals) return m;
|
|
131
|
+
return {
|
|
132
|
+
...m,
|
|
133
|
+
metadata: { ...m.metadata, moduleGlobals } as ResourceManifest["metadata"],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
79
137
|
/** Produce the flat manifest list `analyze()` consumes today.
|
|
80
138
|
*
|
|
81
139
|
* Combines the entry module's manifests with `Telo.Definition`,
|
package/src/index.ts
CHANGED
|
@@ -97,6 +97,7 @@ export {
|
|
|
97
97
|
transfersControl,
|
|
98
98
|
} from "./ref-slot.js";
|
|
99
99
|
export type { RefSlot, RefUse, RefUseCases } from "./ref-slot.js";
|
|
100
|
+
export { binaryKeyword, isBinarySlot, X_TELO_BINARY } from "./binary-slot.js";
|
|
100
101
|
export {
|
|
101
102
|
hasProvidesZone,
|
|
102
103
|
hasRequiresZone,
|
package/src/kernel-globals.ts
CHANGED
|
@@ -22,28 +22,43 @@ const SYSTEM_KINDS = new Set([
|
|
|
22
22
|
"Telo.Abstract",
|
|
23
23
|
]);
|
|
24
24
|
|
|
25
|
+
/** Kernel globals as ONE resource's declaring module sees them. */
|
|
26
|
+
export interface KernelGlobalsIndex {
|
|
27
|
+
forResource(m: ResourceManifest): Record<string, any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
/**
|
|
26
|
-
* Build
|
|
27
|
-
*
|
|
28
|
-
* chain-access validation recognises kernel globals without module authors
|
|
31
|
+
* Build the typed JSON Schema describing the kernel globals available to each
|
|
32
|
+
* resource in a manifest set. Used to merge into `x-telo-context` schemas so
|
|
33
|
+
* that chain-access validation recognises kernel globals without module authors
|
|
29
34
|
* having to re-declare them.
|
|
30
35
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
36
|
+
* `variables` / `secrets` / `ports` are typed **per declaring module**, because
|
|
37
|
+
* that is the contract the resource's CEL is evaluated against at runtime. An
|
|
38
|
+
* application analysis is flattened — `selectModuleManifestsForAnalysis` drops
|
|
39
|
+
* an imported library's module doc and carries its config blocks across as
|
|
40
|
+
* `metadata.moduleGlobals` instead — so a resource forwarded from a library is
|
|
41
|
+
* typed from that stamp, and everything else from the entry module's own doc
|
|
42
|
+
* (the only module doc left in the set).
|
|
43
|
+
*
|
|
44
|
+
* Typing every resource from the entry doc is what made a library's
|
|
45
|
+
* `variables.x` a hard error the library author could not act on, and — in the
|
|
46
|
+
* other direction — let a library read a variable it never declared whenever the
|
|
47
|
+
* app happened to declare that name.
|
|
48
|
+
*
|
|
49
|
+
* `resources` is NOT per module: it enumerates every non-system resource name in
|
|
50
|
+
* the set, and stays OPEN for a forwarded manifest (see `readModuleGlobals` for
|
|
51
|
+
* why a name list cannot be carried across the boundary honestly).
|
|
37
52
|
*/
|
|
38
|
-
export function
|
|
53
|
+
export function buildKernelGlobalsIndex(
|
|
39
54
|
manifests: ResourceManifest[],
|
|
40
55
|
/** Every resource a CEL read can name, including scope-declared ones (see
|
|
41
56
|
* `buildObservedStateIndex`). Kinds that declare a `status:` get a typed,
|
|
42
57
|
* closed `status` node; every other resource node stays open, so no flat read
|
|
43
58
|
* that passes today can start failing. */
|
|
44
59
|
resources?: ReadonlyMap<string, { kind: string; status?: Record<string, any> }>,
|
|
45
|
-
):
|
|
46
|
-
const
|
|
60
|
+
): KernelGlobalsIndex {
|
|
61
|
+
const entryDoc =
|
|
47
62
|
(manifests.find((m) => m.kind === "Telo.Application") as
|
|
48
63
|
| Record<string, any>
|
|
49
64
|
| undefined) ??
|
|
@@ -51,6 +66,52 @@ export function buildKernelGlobalsSchema(
|
|
|
51
66
|
| Record<string, any>
|
|
52
67
|
| undefined);
|
|
53
68
|
|
|
69
|
+
const entrySchema = globalsSchema(entryDoc, buildResourcesSchema(manifests, resources));
|
|
70
|
+
const openResources = { type: "object", additionalProperties: true };
|
|
71
|
+
const byModule = new Map<string, Record<string, any>>();
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
forResource(m: ResourceManifest): Record<string, any> {
|
|
75
|
+
const meta = m.metadata as { module?: string; moduleGlobals?: ModuleGlobals } | undefined;
|
|
76
|
+
const stamped = meta?.moduleGlobals;
|
|
77
|
+
if (!stamped) return entrySchema;
|
|
78
|
+
const key = meta?.module ?? "";
|
|
79
|
+
const cached = byModule.get(key);
|
|
80
|
+
if (cached) return cached;
|
|
81
|
+
const schema = globalsSchema(stamped, openResources);
|
|
82
|
+
byModule.set(key, schema);
|
|
83
|
+
return schema;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The blocks a declaring module contributes to its resources' CEL globals. */
|
|
89
|
+
interface ModuleGlobals {
|
|
90
|
+
variables?: Record<string, unknown>;
|
|
91
|
+
secrets?: Record<string, unknown>;
|
|
92
|
+
ports?: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function globalsSchema(
|
|
96
|
+
doc: ModuleGlobals | Record<string, any> | undefined,
|
|
97
|
+
resourcesSchema: Record<string, any>,
|
|
98
|
+
): Record<string, any> {
|
|
99
|
+
return {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
variables: buildSchemaMapSchema(doc?.variables as Record<string, any> | undefined),
|
|
103
|
+
secrets: buildSchemaMapSchema(doc?.secrets as Record<string, any> | undefined),
|
|
104
|
+
resources: resourcesSchema,
|
|
105
|
+
ports: buildPortsSchema(doc?.ports as Record<string, any> | undefined),
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Every non-system resource name in the set, plus the scope-declared ones. */
|
|
111
|
+
function buildResourcesSchema(
|
|
112
|
+
manifests: ResourceManifest[],
|
|
113
|
+
resources?: ReadonlyMap<string, { kind: string; status?: Record<string, any> }>,
|
|
114
|
+
): Record<string, any> {
|
|
54
115
|
const resourceProps: Record<string, any> = {};
|
|
55
116
|
for (const m of manifests) {
|
|
56
117
|
const name = m.metadata?.name as string | undefined;
|
|
@@ -76,19 +137,7 @@ export function buildKernelGlobalsSchema(
|
|
|
76
137
|
applyObservedStateNode(resourceProps, key, entry.status);
|
|
77
138
|
}
|
|
78
139
|
|
|
79
|
-
return {
|
|
80
|
-
type: "object",
|
|
81
|
-
properties: {
|
|
82
|
-
variables: buildSchemaMapSchema(moduleManifest?.variables),
|
|
83
|
-
secrets: buildSchemaMapSchema(moduleManifest?.secrets),
|
|
84
|
-
resources: {
|
|
85
|
-
type: "object",
|
|
86
|
-
properties: resourceProps,
|
|
87
|
-
additionalProperties: false,
|
|
88
|
-
},
|
|
89
|
-
ports: buildPortsSchema(moduleManifest?.ports),
|
|
90
|
-
},
|
|
91
|
-
};
|
|
140
|
+
return { type: "object", properties: resourceProps, additionalProperties: false };
|
|
92
141
|
}
|
|
93
142
|
|
|
94
143
|
/** Build the closed `ports` chain-access schema: each declared port is an
|
package/src/schema-compat.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AjvModule from "ajv";
|
|
2
2
|
import addFormats from "ajv-formats";
|
|
3
3
|
import { isRefSentinel, isTaggedSentinel, ManifestRootSchema, normalizeRefSlots } from "@telorun/templating";
|
|
4
|
+
import { binaryKeyword, isBinarySlot } from "./binary-slot.js";
|
|
4
5
|
|
|
5
6
|
const Ajv = (AjvModule as any).default ?? AjvModule;
|
|
6
7
|
|
|
@@ -17,6 +18,10 @@ export function createAjv(): InstanceType<typeof Ajv> {
|
|
|
17
18
|
(addFormats as any).default
|
|
18
19
|
? (addFormats as any).default(instance)
|
|
19
20
|
: (addFormats as any)(instance);
|
|
21
|
+
// Bytes have no JSON Schema type, so the annotation carries the check. Registered
|
|
22
|
+
// here and in the kernel's validators from one definition, so a literal at a byte
|
|
23
|
+
// slot is rejected statically and at dispatch by the identical rule.
|
|
24
|
+
instance.addKeyword(binaryKeyword());
|
|
20
25
|
instance.addSchema(ManifestRootSchema);
|
|
21
26
|
return instance;
|
|
22
27
|
}
|
|
@@ -371,6 +376,11 @@ function foldedConstraints(schema: Record<string, any>): Record<string, any> {
|
|
|
371
376
|
|
|
372
377
|
export function celPlaceholderForSchema(rawSchema: Record<string, any>): unknown {
|
|
373
378
|
const schema = foldedConstraints(rawSchema);
|
|
379
|
+
// A byte slot's placeholder must BE bytes: the same keyword validates statically
|
|
380
|
+
// and at dispatch, so a CEL leaf standing in for a runtime buffer has to satisfy
|
|
381
|
+
// it. This is what keeps the rule single — a literal is rejected because no YAML
|
|
382
|
+
// literal is a Uint8Array, while a value arriving by reference passes.
|
|
383
|
+
if (isBinarySlot(schema)) return new Uint8Array();
|
|
374
384
|
if (schema.default !== undefined) return schema.default;
|
|
375
385
|
// An enum-constrained field needs a placeholder drawn from the enum: the
|
|
376
386
|
// type-based fallbacks below ("" for a string, 0 for a number) satisfy `type`
|