@telorun/templating 0.13.0 → 0.14.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/builtins.d.ts +11 -0
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +13 -0
- package/dist/cel/analyze.d.ts +2 -1
- package/dist/cel/analyze.d.ts.map +1 -1
- package/dist/cel/analyze.js +4 -5
- package/dist/engine.d.ts +19 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engines/include.d.ts +5 -2
- package/dist/engines/include.d.ts.map +1 -1
- package/dist/engines/include.js +15 -5
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/manifest-schemas.d.ts +0 -16
- package/dist/manifest-schemas.d.ts.map +1 -1
- package/dist/manifest-schemas.js +0 -95
- package/dist/sentinel.d.ts +18 -0
- package/dist/sentinel.d.ts.map +1 -1
- package/dist/sentinel.js +32 -0
- package/package.json +2 -2
- package/src/builtins.ts +14 -0
- package/src/cel/analyze.ts +4 -7
- package/src/engine.ts +20 -0
- package/src/engines/include.ts +16 -5
- package/src/index.ts +8 -2
- package/src/manifest-schemas.ts +0 -98
- package/src/sentinel.ts +33 -0
package/dist/builtins.d.ts
CHANGED
|
@@ -8,6 +8,17 @@ import type { TemplatingEngine } from "./engine.js";
|
|
|
8
8
|
* another (e.g. `cel + literal`); always ship the same set. */
|
|
9
9
|
export declare const builtinEngines: readonly TemplatingEngine[];
|
|
10
10
|
export declare function createDefaultRegistry(): TemplatingEngineRegistry;
|
|
11
|
+
/**
|
|
12
|
+
* The type a tag always produces, or undefined when its produced type is a
|
|
13
|
+
* function of the slot rather than of the tag (`!cel`, `!ref`).
|
|
14
|
+
*
|
|
15
|
+
* The single reader of `TemplatingEngine.producedType`, so a consumer asks the
|
|
16
|
+
* registry what a tag produces instead of recognising tag names — the same seam
|
|
17
|
+
* `fileClaims` opened for payload membership. Reads the default registry
|
|
18
|
+
* because a produced type is a property of the engine, not of a host's
|
|
19
|
+
* configuration, and every host ships the same built-in set.
|
|
20
|
+
*/
|
|
21
|
+
export declare function producedTypeOf(engineName: string): Record<string, unknown> | undefined;
|
|
11
22
|
/** Memoized singleton: returns the default registry. Hosts that don't need
|
|
12
23
|
* per-instance isolation (precompile, the analyzer's tagged-value walker)
|
|
13
24
|
* should use this so they share the same registry instance the YAML tag
|
package/dist/builtins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;gEAKgE;AAChE,eAAO,MAAM,cAAc,EAAE,SAAS,gBAAgB,EAOrD,CAAC;AAEF,wBAAgB,qBAAqB,IAAI,wBAAwB,CAMhE;AAID;;;oBAGoB;AACpB,wBAAgB,eAAe,IAAI,wBAAwB,CAK1D"}
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;gEAKgE;AAChE,eAAO,MAAM,cAAc,EAAE,SAAS,gBAAgB,EAOrD,CAAC;AAEF,wBAAgB,qBAAqB,IAAI,wBAAwB,CAMhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAEtF;AAID;;;oBAGoB;AACpB,wBAAgB,eAAe,IAAI,wBAAwB,CAK1D"}
|
package/dist/builtins.js
CHANGED
|
@@ -25,6 +25,19 @@ export function createDefaultRegistry() {
|
|
|
25
25
|
}
|
|
26
26
|
return registry;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* The type a tag always produces, or undefined when its produced type is a
|
|
30
|
+
* function of the slot rather than of the tag (`!cel`, `!ref`).
|
|
31
|
+
*
|
|
32
|
+
* The single reader of `TemplatingEngine.producedType`, so a consumer asks the
|
|
33
|
+
* registry what a tag produces instead of recognising tag names — the same seam
|
|
34
|
+
* `fileClaims` opened for payload membership. Reads the default registry
|
|
35
|
+
* because a produced type is a property of the engine, not of a host's
|
|
36
|
+
* configuration, and every host ships the same built-in set.
|
|
37
|
+
*/
|
|
38
|
+
export function producedTypeOf(engineName) {
|
|
39
|
+
return defaultRegistry().get(engineName)?.producedType?.();
|
|
40
|
+
}
|
|
28
41
|
let defaultRegistryCache;
|
|
29
42
|
/** Memoized singleton: returns the default registry. Hosts that don't need
|
|
30
43
|
* per-instance isolation (precompile, the analyzer's tagged-value walker)
|
package/dist/cel/analyze.d.ts
CHANGED
|
@@ -29,7 +29,8 @@ export declare function findNullableAccessIssues(node: ASTNode, contextSchema: R
|
|
|
29
29
|
* Check whether a member-access chain accesses only fields declared in a JSON Schema.
|
|
30
30
|
* Returns an error string if a field is unknown in a schema that declares explicit
|
|
31
31
|
* properties without `additionalProperties: true`, or if the chain attempts to
|
|
32
|
-
* reach inside
|
|
32
|
+
* reach inside a `live` value type — one whose consumption has effects, so its
|
|
33
|
+
* contents exist only for a consumer that drains it.
|
|
33
34
|
* Returns null when the chain is valid or the schema is too open to judge.
|
|
34
35
|
*/
|
|
35
36
|
export declare function validateChainAgainstSchema(chain: string[], schema: Record<string, any>): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/cel/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/cel/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGpD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,EAAE,CAI7D;AA0DD;;+DAE+D;AAC/D,eAAO,MAAM,aAAa,QAAQ,CAAC;AAqBnC,UAAU,aAAa;IACrB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;CAChB;AAoFD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,aAAa,EAAE,CAIjB;AA2FD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC1B,MAAM,GAAG,IAAI,CAsBf"}
|
package/dist/cel/analyze.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isLiveSlot } from "@telorun/sdk";
|
|
1
2
|
/**
|
|
2
3
|
* Extract all member-access chains from a CEL AST.
|
|
3
4
|
* Returns arrays like ["request", "query", "name"] for `request.query.name`.
|
|
@@ -254,7 +255,8 @@ function walkNullable(node, nonNull, boundVars, issues, schema) {
|
|
|
254
255
|
* Check whether a member-access chain accesses only fields declared in a JSON Schema.
|
|
255
256
|
* Returns an error string if a field is unknown in a schema that declares explicit
|
|
256
257
|
* properties without `additionalProperties: true`, or if the chain attempts to
|
|
257
|
-
* reach inside
|
|
258
|
+
* reach inside a `live` value type — one whose consumption has effects, so its
|
|
259
|
+
* contents exist only for a consumer that drains it.
|
|
258
260
|
* Returns null when the chain is valid or the schema is too open to judge.
|
|
259
261
|
*/
|
|
260
262
|
export function validateChainAgainstSchema(chain, schema) {
|
|
@@ -268,10 +270,7 @@ export function validateChainAgainstSchema(chain, schema) {
|
|
|
268
270
|
return null;
|
|
269
271
|
if (key in props) {
|
|
270
272
|
const propSchema = props[key];
|
|
271
|
-
if (propSchema &&
|
|
272
|
-
typeof propSchema === "object" &&
|
|
273
|
-
propSchema["x-telo-stream"] === true &&
|
|
274
|
-
i < chain.length - 1) {
|
|
273
|
+
if (isLiveSlot(propSchema) && i < chain.length - 1) {
|
|
275
274
|
const path = chain.slice(0, i + 1).join(".");
|
|
276
275
|
return `'${path}' yields a stream — pipe it through an Encoder or iterate in a JS.Script step (no member access on stream-typed values)`;
|
|
277
276
|
}
|
package/dist/engine.d.ts
CHANGED
|
@@ -131,5 +131,24 @@ export interface TemplatingEngine {
|
|
|
131
131
|
* A source the engine considers malformed claims nothing; `analyze` is what
|
|
132
132
|
* reports why. */
|
|
133
133
|
fileClaims?(source: string): readonly EngineFileClaim[];
|
|
134
|
+
/** The type this tag ALWAYS produces, as a JSON Schema fragment.
|
|
135
|
+
*
|
|
136
|
+
* Declared by the engine, never recognised by a consumer — the `fileClaims`
|
|
137
|
+
* precedent applied to the one fact it left behind. Before this, the analyzer
|
|
138
|
+
* hardcoded two tag names to hand an `!include-bytes` a byte placeholder and
|
|
139
|
+
* an `!include-text` a string one; the only place a tag's produced type was
|
|
140
|
+
* written down was in its consumer, so a future tag producing bytes had to be
|
|
141
|
+
* added to a set rather than declaring it.
|
|
142
|
+
*
|
|
143
|
+
* Absent for an engine whose produced type is a function of the SLOT rather
|
|
144
|
+
* than of the tag — `!cel`, whose type is only derivable from the expression,
|
|
145
|
+
* and `!ref`, which is an identity marker. Their values keep taking a
|
|
146
|
+
* slot-shaped placeholder.
|
|
147
|
+
*
|
|
148
|
+
* What falls out is the property this preserves exactly: because an embed's
|
|
149
|
+
* type is a constant of the tag, a byte embed at a string slot and text at a
|
|
150
|
+
* byte slot both fail statically, through the ordinary schema check and with
|
|
151
|
+
* no diagnostic code of their own. */
|
|
152
|
+
producedType?(): Record<string, unknown>;
|
|
134
153
|
}
|
|
135
154
|
//# sourceMappingURL=engine.d.ts.map
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;wEAEwE;AACxE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;;;;;;;;eASe;AACf,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;;0EAa0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;oDAEoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED;;;;yBAIyB;AACzB,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IACrC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;oDAEgD;IAChD,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;iFAGiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAClD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;4CAgB4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;wBAEwB;AACxB,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;2EAKuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;yDAGqD;IACrD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC;IAElE;;iEAE6D;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAAC;IAExD;;;;;;;;;;;;uBAYmB;IACnB,UAAU,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;wEAEwE;AACxE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;;;;;;;;eASe;AACf,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;;0EAa0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;oDAEoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED;;;;yBAIyB;AACzB,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IACrC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;oDAEgD;IAChD,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;iFAGiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAClD,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;4CAgB4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;wBAEwB;AACxB,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;2EAKuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;yDAGqD;IACrD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,GAAG,OAAO,CAAC;IAElE;;iEAE6D;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,aAAa,CAAC;IAExD;;;;;;;;;;;;uBAYmB;IACnB,UAAU,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAAC;IAExD;;;;;;;;;;;;;;;;;2CAiBuC;IACvC,YAAY,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C"}
|
|
@@ -19,7 +19,10 @@ export interface NormalizedIncludePath {
|
|
|
19
19
|
export declare function normalizeIncludePath(source: string): NormalizedIncludePath;
|
|
20
20
|
/** Embeds a file's contents as a UTF-8 string. */
|
|
21
21
|
export declare const includeTextEngine: TemplatingEngine;
|
|
22
|
-
/** Embeds a file's contents as raw bytes —
|
|
23
|
-
*
|
|
22
|
+
/** Embeds a file's contents as raw bytes — the shape every `Telo.Bytes` slot
|
|
23
|
+
* accepts. The name is written as a literal rather than imported from the SDK:
|
|
24
|
+
* templating is the lower package, and a produced type is a schema fragment,
|
|
25
|
+
* which is data. What checks it is the registry the analyzer and the kernel both
|
|
26
|
+
* read. */
|
|
24
27
|
export declare const includeBytesEngine: TemplatingEngine;
|
|
25
28
|
//# sourceMappingURL=include.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../../src/engines/include.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAaxF,MAAM,WAAW,qBAAqB;IACpC;kEAC8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CA0E1E;
|
|
1
|
+
{"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../../src/engines/include.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAaxF,MAAM,WAAW,qBAAqB;IACpC;kEAC8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CA0E1E;AA8CD,kDAAkD;AAClD,eAAO,MAAM,iBAAiB,EAAE,gBAE9B,CAAC;AAEH;;;;YAIY;AACZ,eAAO,MAAM,kBAAkB,EAAE,gBAE/B,CAAC"}
|
package/dist/engines/include.js
CHANGED
|
@@ -106,12 +106,15 @@ export function normalizeIncludePath(source) {
|
|
|
106
106
|
* `analyze` reports why a path is unusable; `fileClaims` reports the path itself
|
|
107
107
|
* so publish can place the file in a layer without recognising the tag by name.
|
|
108
108
|
*/
|
|
109
|
-
function includeEngine(name) {
|
|
109
|
+
function includeEngine(name, produced) {
|
|
110
110
|
return {
|
|
111
111
|
name,
|
|
112
112
|
compile(source) {
|
|
113
113
|
return makeTaggedSentinel(name, source);
|
|
114
114
|
},
|
|
115
|
+
producedType() {
|
|
116
|
+
return produced;
|
|
117
|
+
},
|
|
115
118
|
analyze(source) {
|
|
116
119
|
const { diagnostic } = normalizeIncludePath(source);
|
|
117
120
|
return { diagnostics: diagnostic ? [diagnostic] : [], calls: [] };
|
|
@@ -126,7 +129,14 @@ function includeEngine(name) {
|
|
|
126
129
|
};
|
|
127
130
|
}
|
|
128
131
|
/** Embeds a file's contents as a UTF-8 string. */
|
|
129
|
-
export const includeTextEngine = includeEngine(INCLUDE_TEXT_ENGINE
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
export const includeTextEngine = includeEngine(INCLUDE_TEXT_ENGINE, {
|
|
133
|
+
type: "string",
|
|
134
|
+
});
|
|
135
|
+
/** Embeds a file's contents as raw bytes — the shape every `Telo.Bytes` slot
|
|
136
|
+
* accepts. The name is written as a literal rather than imported from the SDK:
|
|
137
|
+
* templating is the lower package, and a produced type is a schema fragment,
|
|
138
|
+
* which is data. What checks it is the registry the analyzer and the kernel both
|
|
139
|
+
* read. */
|
|
140
|
+
export const includeBytesEngine = includeEngine(INCLUDE_BYTES_ENGINE, {
|
|
141
|
+
"x-telo-type": "Telo.Bytes",
|
|
142
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -10,9 +10,9 @@ export { literalEngine } from "./engines/literal.js";
|
|
|
10
10
|
export { refEngine } from "./engines/ref.js";
|
|
11
11
|
export { sqlEngine, isParameterizedSql, type ParameterizedSql } from "./engines/sql.js";
|
|
12
12
|
export { TemplatingEngineRegistry } from "./registry.js";
|
|
13
|
-
export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
|
|
13
|
+
export { builtinEngines, createDefaultRegistry, defaultRegistry, producedTypeOf, } from "./builtins.js";
|
|
14
14
|
export type { AnalyzeEnv, AnalyzeResult, CallSite, CompileEnv, DiagnosticFix, EngineDiagnostic, EngineFileClaim, TemplatingEngine, } from "./engine.js";
|
|
15
|
-
export { INCLUDE_BYTES_ENGINE, INCLUDE_ENGINE_NAMES, INCLUDE_TEXT_ENGINE, isIncludeSentinel, isRefSentinel, isTaggedSentinel, makeTaggedSentinel, type TaggedSentinel, } from "./sentinel.js";
|
|
15
|
+
export { CEL_ENGINE, INCLUDE_BYTES_ENGINE, INCLUDE_ENGINE_NAMES, INCLUDE_TEXT_ENGINE, isIncludeSentinel, isRefSentinel, isTaggedSentinel, makeTaggedSentinel, plainChainOf, type TaggedSentinel, } from "./sentinel.js";
|
|
16
16
|
export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
|
|
17
|
-
export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema,
|
|
17
|
+
export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema, } from "./manifest-schemas.js";
|
|
18
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAExF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAExF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,UAAU,EACV,aAAa,EACb,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export { literalEngine } from "./engines/literal.js";
|
|
|
10
10
|
export { refEngine } from "./engines/ref.js";
|
|
11
11
|
export { sqlEngine, isParameterizedSql } from "./engines/sql.js";
|
|
12
12
|
export { TemplatingEngineRegistry } from "./registry.js";
|
|
13
|
-
export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
|
|
14
|
-
export { INCLUDE_BYTES_ENGINE, INCLUDE_ENGINE_NAMES, INCLUDE_TEXT_ENGINE, isIncludeSentinel, isRefSentinel, isTaggedSentinel, makeTaggedSentinel, } from "./sentinel.js";
|
|
13
|
+
export { builtinEngines, createDefaultRegistry, defaultRegistry, producedTypeOf, } from "./builtins.js";
|
|
14
|
+
export { CEL_ENGINE, INCLUDE_BYTES_ENGINE, INCLUDE_ENGINE_NAMES, INCLUDE_TEXT_ENGINE, isIncludeSentinel, isRefSentinel, isTaggedSentinel, makeTaggedSentinel, plainChainOf, } from "./sentinel.js";
|
|
15
15
|
export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
|
|
16
|
-
export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema,
|
|
16
|
+
export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema, } from "./manifest-schemas.js";
|
|
@@ -70,22 +70,6 @@ export declare const ResourceRefSchema: {
|
|
|
70
70
|
additionalProperties: boolean;
|
|
71
71
|
})[];
|
|
72
72
|
};
|
|
73
|
-
/** Deep-clone `schema`, dropping the stale scalar `type` constraint from every
|
|
74
|
-
* reference-slot node — one carrying an `x-telo-ref` string annotation.
|
|
75
|
-
*
|
|
76
|
-
* A reference slot's value is always a `!ref` sentinel or its resolved
|
|
77
|
-
* `{kind, name, alias?}` object (never a bare string, post-migration). Older
|
|
78
|
-
* published modules still pin `type: "string"` on these slots — the encoding
|
|
79
|
-
* references took when they were written as plain strings — which now rejects
|
|
80
|
-
* the resolved object. Removing only the scalar `type` lets the analyzer and
|
|
81
|
-
* kernel accept references uniformly across module versions during the
|
|
82
|
-
* migration away from `{kind, name}` / string references, without disturbing
|
|
83
|
-
* slots that legitimately accept an inline object (e.g. `inputType` /
|
|
84
|
-
* `outputType`, which take a Telo.Type reference *or* an inline JSON schema).
|
|
85
|
-
* The `x-telo-ref` constraint itself (which kind the reference must satisfy) is
|
|
86
|
-
* checked separately by the analyzer's reference walker, which reads the
|
|
87
|
-
* original schema — not this validation-only copy. */
|
|
88
|
-
export declare function normalizeRefSlots(schema: unknown): unknown;
|
|
89
73
|
/** Stable URI under which the shared manifest root schema is registered
|
|
90
74
|
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
91
75
|
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-schemas.d.ts","sourceRoot":"","sources":["../src/manifest-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;kBAMkB;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA+ByE;AACzE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB7B,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest-schemas.d.ts","sourceRoot":"","sources":["../src/manifest-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;kBAMkB;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA+ByE;AACzE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB7B,CAAC;AAGF;;;;;+BAK+B;AAC/B,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;8BAG8B;AAC9B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAK9B,CAAC"}
|
package/dist/manifest-schemas.js
CHANGED
|
@@ -58,101 +58,6 @@ export const ResourceRefSchema = {
|
|
|
58
58
|
},
|
|
59
59
|
],
|
|
60
60
|
};
|
|
61
|
-
const REF_ANNOTATION = "x-telo-ref";
|
|
62
|
-
// The legacy base types a reference slot used to pin when references were
|
|
63
|
-
// written as plain strings. Post-migration a reference resolves to an object
|
|
64
|
-
// (the `{kind, name, alias?}` shape, or an unresolved `!ref` sentinel), so a
|
|
65
|
-
// scalar `type` on a ref slot is a stale constraint that would reject the
|
|
66
|
-
// resolved value. Object / array `type`s are left alone — they already admit
|
|
67
|
-
// the reference object (and any inline value a slot like `inputType` accepts).
|
|
68
|
-
const LEGACY_REF_SCALAR_TYPES = new Set(["string", "number", "integer", "boolean"]);
|
|
69
|
-
// JSON Schema keywords whose values are themselves subschemas. Split by shape so
|
|
70
|
-
// the ref-slot normalizer recurses only into schema positions — never into
|
|
71
|
-
// data-bearing keywords (`default`, `const`, `enum`, `examples`), where a stray
|
|
72
|
-
// `x-telo-ref` key would be data, not an annotation.
|
|
73
|
-
const SUBSCHEMA_SINGLE = [
|
|
74
|
-
"additionalProperties",
|
|
75
|
-
"additionalItems",
|
|
76
|
-
"contains",
|
|
77
|
-
"not",
|
|
78
|
-
"if",
|
|
79
|
-
"then",
|
|
80
|
-
"else",
|
|
81
|
-
"propertyNames",
|
|
82
|
-
"unevaluatedItems",
|
|
83
|
-
"unevaluatedProperties",
|
|
84
|
-
];
|
|
85
|
-
const SUBSCHEMA_LIST = ["allOf", "anyOf", "oneOf", "prefixItems"];
|
|
86
|
-
const SUBSCHEMA_MAP = [
|
|
87
|
-
"properties",
|
|
88
|
-
"patternProperties",
|
|
89
|
-
"$defs",
|
|
90
|
-
"definitions",
|
|
91
|
-
"dependentSchemas",
|
|
92
|
-
];
|
|
93
|
-
/** Deep-clone `schema`, dropping the stale scalar `type` constraint from every
|
|
94
|
-
* reference-slot node — one carrying an `x-telo-ref` string annotation.
|
|
95
|
-
*
|
|
96
|
-
* A reference slot's value is always a `!ref` sentinel or its resolved
|
|
97
|
-
* `{kind, name, alias?}` object (never a bare string, post-migration). Older
|
|
98
|
-
* published modules still pin `type: "string"` on these slots — the encoding
|
|
99
|
-
* references took when they were written as plain strings — which now rejects
|
|
100
|
-
* the resolved object. Removing only the scalar `type` lets the analyzer and
|
|
101
|
-
* kernel accept references uniformly across module versions during the
|
|
102
|
-
* migration away from `{kind, name}` / string references, without disturbing
|
|
103
|
-
* slots that legitimately accept an inline object (e.g. `inputType` /
|
|
104
|
-
* `outputType`, which take a Telo.Type reference *or* an inline JSON schema).
|
|
105
|
-
* The `x-telo-ref` constraint itself (which kind the reference must satisfy) is
|
|
106
|
-
* checked separately by the analyzer's reference walker, which reads the
|
|
107
|
-
* original schema — not this validation-only copy. */
|
|
108
|
-
export function normalizeRefSlots(schema) {
|
|
109
|
-
if (schema === null || typeof schema !== "object" || Array.isArray(schema)) {
|
|
110
|
-
return schema;
|
|
111
|
-
}
|
|
112
|
-
const node = schema;
|
|
113
|
-
const out = { ...node };
|
|
114
|
-
// Reference slot with a stale scalar `type` (legacy string-ref encoding):
|
|
115
|
-
// drop the constraint so the resolved reference object / sentinel validates.
|
|
116
|
-
//
|
|
117
|
-
// A presence test, not a shape test — deliberately, since `templating` sits
|
|
118
|
-
// BELOW the analyzer in the dependency order and cannot reach the shared
|
|
119
|
-
// `readRefSlot` accessor. Presence is the only thing this rule needs, and it
|
|
120
|
-
// is stable across every annotation shape.
|
|
121
|
-
if (node[REF_ANNOTATION] !== undefined &&
|
|
122
|
-
typeof node.type === "string" &&
|
|
123
|
-
LEGACY_REF_SCALAR_TYPES.has(node.type)) {
|
|
124
|
-
delete out.type;
|
|
125
|
-
}
|
|
126
|
-
for (const key of SUBSCHEMA_SINGLE) {
|
|
127
|
-
const value = node[key];
|
|
128
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
129
|
-
out[key] = normalizeRefSlots(value);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
for (const key of SUBSCHEMA_LIST) {
|
|
133
|
-
const value = node[key];
|
|
134
|
-
if (Array.isArray(value))
|
|
135
|
-
out[key] = value.map(normalizeRefSlots);
|
|
136
|
-
}
|
|
137
|
-
// `items` is either a single subschema or a tuple of subschemas.
|
|
138
|
-
if (Array.isArray(node.items)) {
|
|
139
|
-
out.items = node.items.map(normalizeRefSlots);
|
|
140
|
-
}
|
|
141
|
-
else if (node.items && typeof node.items === "object") {
|
|
142
|
-
out.items = normalizeRefSlots(node.items);
|
|
143
|
-
}
|
|
144
|
-
for (const key of SUBSCHEMA_MAP) {
|
|
145
|
-
const value = node[key];
|
|
146
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
147
|
-
const mapped = {};
|
|
148
|
-
for (const [name, sub] of Object.entries(value)) {
|
|
149
|
-
mapped[name] = normalizeRefSlots(sub);
|
|
150
|
-
}
|
|
151
|
-
out[key] = mapped;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return out;
|
|
155
|
-
}
|
|
156
61
|
/** Stable URI under which the shared manifest root schema is registered
|
|
157
62
|
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
158
63
|
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
package/dist/sentinel.d.ts
CHANGED
|
@@ -19,11 +19,29 @@ export declare function makeTaggedSentinel(engine: string, source: string): Tagg
|
|
|
19
19
|
export declare function isRefSentinel(v: unknown): v is TaggedSentinel & {
|
|
20
20
|
engine: "ref";
|
|
21
21
|
};
|
|
22
|
+
/** The CEL engine's name. Beside the other sentinel predicates for the same
|
|
23
|
+
* reason they are: a consumer that spells an engine name inline is a second
|
|
24
|
+
* place the registry's key is written down. */
|
|
25
|
+
export declare const CEL_ENGINE = "cel";
|
|
22
26
|
/** Engine names of the two file-embedding tags. Named here beside the other
|
|
23
27
|
* sentinel predicates so the kernel's resolution pass and the engines
|
|
24
28
|
* themselves agree on one spelling. */
|
|
25
29
|
export declare const INCLUDE_TEXT_ENGINE = "include-text";
|
|
26
30
|
export declare const INCLUDE_BYTES_ENGINE = "include-bytes";
|
|
31
|
+
/**
|
|
32
|
+
* The dotted chain a value names, or undefined.
|
|
33
|
+
*
|
|
34
|
+
* Only a PLAIN CHAIN — `steps.encode.result.output` — in either spelling a
|
|
35
|
+
* manifest may carry it: a `!cel` sentinel or the `${{ }}` string form. An
|
|
36
|
+
* expression that COMPUTES rather than names has no schema to read off a context,
|
|
37
|
+
* so a caller that navigates one gets nothing and reports nothing: silence where
|
|
38
|
+
* the analyzer knows least is the conservative direction.
|
|
39
|
+
*
|
|
40
|
+
* Here rather than in each caller because "is this expression a plain chain" is
|
|
41
|
+
* one question, and two copies of the answer would eventually disagree about a
|
|
42
|
+
* shape like `a.b[0]`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function plainChainOf(value: unknown): string | undefined;
|
|
27
45
|
/** Both file-embedding tag names, for a consumer holding an engine NAME rather
|
|
28
46
|
* than a value (the analyzer's expression walk reports names). */
|
|
29
47
|
export declare const INCLUDE_ENGINE_NAMES: ReadonlySet<string>;
|
package/dist/sentinel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA;;;;;YAKY;AACZ,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,CAQhE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAEjF;AAED;;;;4CAI4C;AAC5C,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,GAAG;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,CAEjF;AAED;;wCAEwC;AACxC,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD;mEACmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAGnD,CAAC;AAEH;;;;;;;qCAOqC;AACrC,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,OAAO,GACT,CAAC,IAAI,cAAc,GAAG;IAAE,MAAM,EAAE,OAAO,mBAAmB,GAAG,OAAO,oBAAoB,CAAA;CAAE,CAI5F"}
|
|
1
|
+
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../src/sentinel.ts"],"names":[],"mappings":"AAAA;;;;;YAKY;AACZ,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,CAQhE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAEjF;AAED;;;;4CAI4C;AAC5C,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,GAAG;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,CAEjF;AAED;;gDAEgD;AAChD,eAAO,MAAM,UAAU,QAAQ,CAAC;AAEhC;;wCAEwC;AACxC,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAa/D;AAED;mEACmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAGnD,CAAC;AAEH;;;;;;;qCAOqC;AACrC,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,OAAO,GACT,CAAC,IAAI,cAAc,GAAG;IAAE,MAAM,EAAE,OAAO,mBAAmB,GAAG,OAAO,oBAAoB,CAAA;CAAE,CAI5F"}
|
package/dist/sentinel.js
CHANGED
|
@@ -16,11 +16,43 @@ export function makeTaggedSentinel(engine, source) {
|
|
|
16
16
|
export function isRefSentinel(v) {
|
|
17
17
|
return isTaggedSentinel(v) && v.engine === "ref";
|
|
18
18
|
}
|
|
19
|
+
/** The CEL engine's name. Beside the other sentinel predicates for the same
|
|
20
|
+
* reason they are: a consumer that spells an engine name inline is a second
|
|
21
|
+
* place the registry's key is written down. */
|
|
22
|
+
export const CEL_ENGINE = "cel";
|
|
19
23
|
/** Engine names of the two file-embedding tags. Named here beside the other
|
|
20
24
|
* sentinel predicates so the kernel's resolution pass and the engines
|
|
21
25
|
* themselves agree on one spelling. */
|
|
22
26
|
export const INCLUDE_TEXT_ENGINE = "include-text";
|
|
23
27
|
export const INCLUDE_BYTES_ENGINE = "include-bytes";
|
|
28
|
+
/**
|
|
29
|
+
* The dotted chain a value names, or undefined.
|
|
30
|
+
*
|
|
31
|
+
* Only a PLAIN CHAIN — `steps.encode.result.output` — in either spelling a
|
|
32
|
+
* manifest may carry it: a `!cel` sentinel or the `${{ }}` string form. An
|
|
33
|
+
* expression that COMPUTES rather than names has no schema to read off a context,
|
|
34
|
+
* so a caller that navigates one gets nothing and reports nothing: silence where
|
|
35
|
+
* the analyzer knows least is the conservative direction.
|
|
36
|
+
*
|
|
37
|
+
* Here rather than in each caller because "is this expression a plain chain" is
|
|
38
|
+
* one question, and two copies of the answer would eventually disagree about a
|
|
39
|
+
* shape like `a.b[0]`.
|
|
40
|
+
*/
|
|
41
|
+
export function plainChainOf(value) {
|
|
42
|
+
const source = isTaggedSentinel(value)
|
|
43
|
+
? value.engine === CEL_ENGINE
|
|
44
|
+
? value.source
|
|
45
|
+
: undefined
|
|
46
|
+
: typeof value === "string"
|
|
47
|
+
? /^\s*\$\{\{(.+)\}\}\s*$/.exec(value)?.[1]
|
|
48
|
+
: undefined;
|
|
49
|
+
if (typeof source !== "string")
|
|
50
|
+
return undefined;
|
|
51
|
+
const trimmed = source.trim();
|
|
52
|
+
return /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(trimmed)
|
|
53
|
+
? trimmed
|
|
54
|
+
: undefined;
|
|
55
|
+
}
|
|
24
56
|
/** Both file-embedding tag names, for a consumer holding an engine NAME rather
|
|
25
57
|
* than a value (the analyzer's expression walk reports names). */
|
|
26
58
|
export const INCLUDE_ENGINE_NAMES = new Set([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/templating",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Telo Templating - Engine registry and shared CEL core for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@types/node": "^20.0.0",
|
|
45
45
|
"typescript": "^5.0.0",
|
|
46
46
|
"vitest": "^2.1.8",
|
|
47
|
-
"@telorun/sdk": "0.
|
|
47
|
+
"@telorun/sdk": "0.73.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@telorun/sdk": "*"
|
package/src/builtins.ts
CHANGED
|
@@ -29,6 +29,20 @@ export function createDefaultRegistry(): TemplatingEngineRegistry {
|
|
|
29
29
|
return registry;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* The type a tag always produces, or undefined when its produced type is a
|
|
34
|
+
* function of the slot rather than of the tag (`!cel`, `!ref`).
|
|
35
|
+
*
|
|
36
|
+
* The single reader of `TemplatingEngine.producedType`, so a consumer asks the
|
|
37
|
+
* registry what a tag produces instead of recognising tag names — the same seam
|
|
38
|
+
* `fileClaims` opened for payload membership. Reads the default registry
|
|
39
|
+
* because a produced type is a property of the engine, not of a host's
|
|
40
|
+
* configuration, and every host ships the same built-in set.
|
|
41
|
+
*/
|
|
42
|
+
export function producedTypeOf(engineName: string): Record<string, unknown> | undefined {
|
|
43
|
+
return defaultRegistry().get(engineName)?.producedType?.();
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
let defaultRegistryCache: TemplatingEngineRegistry | undefined;
|
|
33
47
|
|
|
34
48
|
/** Memoized singleton: returns the default registry. Hosts that don't need
|
package/src/cel/analyze.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ASTNode } from "@marcbachmann/cel-js";
|
|
2
|
+
import { isLiveSlot } from "@telorun/sdk";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Extract all member-access chains from a CEL AST.
|
|
@@ -291,7 +292,8 @@ function walkNullable(
|
|
|
291
292
|
* Check whether a member-access chain accesses only fields declared in a JSON Schema.
|
|
292
293
|
* Returns an error string if a field is unknown in a schema that declares explicit
|
|
293
294
|
* properties without `additionalProperties: true`, or if the chain attempts to
|
|
294
|
-
* reach inside
|
|
295
|
+
* reach inside a `live` value type — one whose consumption has effects, so its
|
|
296
|
+
* contents exist only for a consumer that drains it.
|
|
295
297
|
* Returns null when the chain is valid or the schema is too open to judge.
|
|
296
298
|
*/
|
|
297
299
|
export function validateChainAgainstSchema(
|
|
@@ -306,12 +308,7 @@ export function validateChainAgainstSchema(
|
|
|
306
308
|
if (!props) return null;
|
|
307
309
|
if (key in props) {
|
|
308
310
|
const propSchema = props[key];
|
|
309
|
-
if (
|
|
310
|
-
propSchema &&
|
|
311
|
-
typeof propSchema === "object" &&
|
|
312
|
-
propSchema["x-telo-stream"] === true &&
|
|
313
|
-
i < chain.length - 1
|
|
314
|
-
) {
|
|
311
|
+
if (isLiveSlot(propSchema) && i < chain.length - 1) {
|
|
315
312
|
const path = chain.slice(0, i + 1).join(".");
|
|
316
313
|
return `'${path}' yields a stream — pipe it through an Encoder or iterate in a JS.Script step (no member access on stream-typed values)`;
|
|
317
314
|
}
|
package/src/engine.ts
CHANGED
|
@@ -143,4 +143,24 @@ export interface TemplatingEngine {
|
|
|
143
143
|
* A source the engine considers malformed claims nothing; `analyze` is what
|
|
144
144
|
* reports why. */
|
|
145
145
|
fileClaims?(source: string): readonly EngineFileClaim[];
|
|
146
|
+
|
|
147
|
+
/** The type this tag ALWAYS produces, as a JSON Schema fragment.
|
|
148
|
+
*
|
|
149
|
+
* Declared by the engine, never recognised by a consumer — the `fileClaims`
|
|
150
|
+
* precedent applied to the one fact it left behind. Before this, the analyzer
|
|
151
|
+
* hardcoded two tag names to hand an `!include-bytes` a byte placeholder and
|
|
152
|
+
* an `!include-text` a string one; the only place a tag's produced type was
|
|
153
|
+
* written down was in its consumer, so a future tag producing bytes had to be
|
|
154
|
+
* added to a set rather than declaring it.
|
|
155
|
+
*
|
|
156
|
+
* Absent for an engine whose produced type is a function of the SLOT rather
|
|
157
|
+
* than of the tag — `!cel`, whose type is only derivable from the expression,
|
|
158
|
+
* and `!ref`, which is an identity marker. Their values keep taking a
|
|
159
|
+
* slot-shaped placeholder.
|
|
160
|
+
*
|
|
161
|
+
* What falls out is the property this preserves exactly: because an embed's
|
|
162
|
+
* type is a constant of the tag, a byte embed at a string slot and text at a
|
|
163
|
+
* byte slot both fail statically, through the ordinary schema check and with
|
|
164
|
+
* no diagnostic code of their own. */
|
|
165
|
+
producedType?(): Record<string, unknown>;
|
|
146
166
|
}
|
package/src/engines/include.ts
CHANGED
|
@@ -122,7 +122,7 @@ export function normalizeIncludePath(source: string): NormalizedIncludePath {
|
|
|
122
122
|
* `analyze` reports why a path is unusable; `fileClaims` reports the path itself
|
|
123
123
|
* so publish can place the file in a layer without recognising the tag by name.
|
|
124
124
|
*/
|
|
125
|
-
function includeEngine(name: string): TemplatingEngine {
|
|
125
|
+
function includeEngine(name: string, produced: Record<string, unknown>): TemplatingEngine {
|
|
126
126
|
return {
|
|
127
127
|
name,
|
|
128
128
|
|
|
@@ -130,6 +130,10 @@ function includeEngine(name: string): TemplatingEngine {
|
|
|
130
130
|
return makeTaggedSentinel(name, source);
|
|
131
131
|
},
|
|
132
132
|
|
|
133
|
+
producedType() {
|
|
134
|
+
return produced;
|
|
135
|
+
},
|
|
136
|
+
|
|
133
137
|
analyze(source) {
|
|
134
138
|
const { diagnostic } = normalizeIncludePath(source);
|
|
135
139
|
return { diagnostics: diagnostic ? [diagnostic] : [], calls: [] };
|
|
@@ -146,8 +150,15 @@ function includeEngine(name: string): TemplatingEngine {
|
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
/** Embeds a file's contents as a UTF-8 string. */
|
|
149
|
-
export const includeTextEngine: TemplatingEngine = includeEngine(INCLUDE_TEXT_ENGINE
|
|
153
|
+
export const includeTextEngine: TemplatingEngine = includeEngine(INCLUDE_TEXT_ENGINE, {
|
|
154
|
+
type: "string",
|
|
155
|
+
});
|
|
150
156
|
|
|
151
|
-
/** Embeds a file's contents as raw bytes —
|
|
152
|
-
*
|
|
153
|
-
|
|
157
|
+
/** Embeds a file's contents as raw bytes — the shape every `Telo.Bytes` slot
|
|
158
|
+
* accepts. The name is written as a literal rather than imported from the SDK:
|
|
159
|
+
* templating is the lower package, and a produced type is a schema fragment,
|
|
160
|
+
* which is data. What checks it is the registry the analyzer and the kernel both
|
|
161
|
+
* read. */
|
|
162
|
+
export const includeBytesEngine: TemplatingEngine = includeEngine(INCLUDE_BYTES_ENGINE, {
|
|
163
|
+
"x-telo-type": "Telo.Bytes",
|
|
164
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -39,7 +39,12 @@ export { refEngine } from "./engines/ref.js";
|
|
|
39
39
|
export { sqlEngine, isParameterizedSql, type ParameterizedSql } from "./engines/sql.js";
|
|
40
40
|
|
|
41
41
|
export { TemplatingEngineRegistry } from "./registry.js";
|
|
42
|
-
export {
|
|
42
|
+
export {
|
|
43
|
+
builtinEngines,
|
|
44
|
+
createDefaultRegistry,
|
|
45
|
+
defaultRegistry,
|
|
46
|
+
producedTypeOf,
|
|
47
|
+
} from "./builtins.js";
|
|
43
48
|
export type {
|
|
44
49
|
AnalyzeEnv,
|
|
45
50
|
AnalyzeResult,
|
|
@@ -52,6 +57,7 @@ export type {
|
|
|
52
57
|
} from "./engine.js";
|
|
53
58
|
|
|
54
59
|
export {
|
|
60
|
+
CEL_ENGINE,
|
|
55
61
|
INCLUDE_BYTES_ENGINE,
|
|
56
62
|
INCLUDE_ENGINE_NAMES,
|
|
57
63
|
INCLUDE_TEXT_ENGINE,
|
|
@@ -59,6 +65,7 @@ export {
|
|
|
59
65
|
isRefSentinel,
|
|
60
66
|
isTaggedSentinel,
|
|
61
67
|
makeTaggedSentinel,
|
|
68
|
+
plainChainOf,
|
|
62
69
|
type TaggedSentinel,
|
|
63
70
|
} from "./sentinel.js";
|
|
64
71
|
export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
|
|
@@ -66,5 +73,4 @@ export {
|
|
|
66
73
|
MANIFEST_SCHEMA_URI,
|
|
67
74
|
ManifestRootSchema,
|
|
68
75
|
ResourceRefSchema,
|
|
69
|
-
normalizeRefSlots,
|
|
70
76
|
} from "./manifest-schemas.js";
|
package/src/manifest-schemas.ts
CHANGED
|
@@ -60,104 +60,6 @@ export const ResourceRefSchema = {
|
|
|
60
60
|
],
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
const REF_ANNOTATION = "x-telo-ref";
|
|
64
|
-
|
|
65
|
-
// The legacy base types a reference slot used to pin when references were
|
|
66
|
-
// written as plain strings. Post-migration a reference resolves to an object
|
|
67
|
-
// (the `{kind, name, alias?}` shape, or an unresolved `!ref` sentinel), so a
|
|
68
|
-
// scalar `type` on a ref slot is a stale constraint that would reject the
|
|
69
|
-
// resolved value. Object / array `type`s are left alone — they already admit
|
|
70
|
-
// the reference object (and any inline value a slot like `inputType` accepts).
|
|
71
|
-
const LEGACY_REF_SCALAR_TYPES = new Set(["string", "number", "integer", "boolean"]);
|
|
72
|
-
|
|
73
|
-
// JSON Schema keywords whose values are themselves subschemas. Split by shape so
|
|
74
|
-
// the ref-slot normalizer recurses only into schema positions — never into
|
|
75
|
-
// data-bearing keywords (`default`, `const`, `enum`, `examples`), where a stray
|
|
76
|
-
// `x-telo-ref` key would be data, not an annotation.
|
|
77
|
-
const SUBSCHEMA_SINGLE = [
|
|
78
|
-
"additionalProperties",
|
|
79
|
-
"additionalItems",
|
|
80
|
-
"contains",
|
|
81
|
-
"not",
|
|
82
|
-
"if",
|
|
83
|
-
"then",
|
|
84
|
-
"else",
|
|
85
|
-
"propertyNames",
|
|
86
|
-
"unevaluatedItems",
|
|
87
|
-
"unevaluatedProperties",
|
|
88
|
-
] as const;
|
|
89
|
-
const SUBSCHEMA_LIST = ["allOf", "anyOf", "oneOf", "prefixItems"] as const;
|
|
90
|
-
const SUBSCHEMA_MAP = [
|
|
91
|
-
"properties",
|
|
92
|
-
"patternProperties",
|
|
93
|
-
"$defs",
|
|
94
|
-
"definitions",
|
|
95
|
-
"dependentSchemas",
|
|
96
|
-
] as const;
|
|
97
|
-
|
|
98
|
-
/** Deep-clone `schema`, dropping the stale scalar `type` constraint from every
|
|
99
|
-
* reference-slot node — one carrying an `x-telo-ref` string annotation.
|
|
100
|
-
*
|
|
101
|
-
* A reference slot's value is always a `!ref` sentinel or its resolved
|
|
102
|
-
* `{kind, name, alias?}` object (never a bare string, post-migration). Older
|
|
103
|
-
* published modules still pin `type: "string"` on these slots — the encoding
|
|
104
|
-
* references took when they were written as plain strings — which now rejects
|
|
105
|
-
* the resolved object. Removing only the scalar `type` lets the analyzer and
|
|
106
|
-
* kernel accept references uniformly across module versions during the
|
|
107
|
-
* migration away from `{kind, name}` / string references, without disturbing
|
|
108
|
-
* slots that legitimately accept an inline object (e.g. `inputType` /
|
|
109
|
-
* `outputType`, which take a Telo.Type reference *or* an inline JSON schema).
|
|
110
|
-
* The `x-telo-ref` constraint itself (which kind the reference must satisfy) is
|
|
111
|
-
* checked separately by the analyzer's reference walker, which reads the
|
|
112
|
-
* original schema — not this validation-only copy. */
|
|
113
|
-
export function normalizeRefSlots(schema: unknown): unknown {
|
|
114
|
-
if (schema === null || typeof schema !== "object" || Array.isArray(schema)) {
|
|
115
|
-
return schema;
|
|
116
|
-
}
|
|
117
|
-
const node = schema as Record<string, unknown>;
|
|
118
|
-
const out: Record<string, unknown> = { ...node };
|
|
119
|
-
// Reference slot with a stale scalar `type` (legacy string-ref encoding):
|
|
120
|
-
// drop the constraint so the resolved reference object / sentinel validates.
|
|
121
|
-
//
|
|
122
|
-
// A presence test, not a shape test — deliberately, since `templating` sits
|
|
123
|
-
// BELOW the analyzer in the dependency order and cannot reach the shared
|
|
124
|
-
// `readRefSlot` accessor. Presence is the only thing this rule needs, and it
|
|
125
|
-
// is stable across every annotation shape.
|
|
126
|
-
if (
|
|
127
|
-
node[REF_ANNOTATION] !== undefined &&
|
|
128
|
-
typeof node.type === "string" &&
|
|
129
|
-
LEGACY_REF_SCALAR_TYPES.has(node.type)
|
|
130
|
-
) {
|
|
131
|
-
delete out.type;
|
|
132
|
-
}
|
|
133
|
-
for (const key of SUBSCHEMA_SINGLE) {
|
|
134
|
-
const value = node[key];
|
|
135
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
136
|
-
out[key] = normalizeRefSlots(value);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
for (const key of SUBSCHEMA_LIST) {
|
|
140
|
-
const value = node[key];
|
|
141
|
-
if (Array.isArray(value)) out[key] = value.map(normalizeRefSlots);
|
|
142
|
-
}
|
|
143
|
-
// `items` is either a single subschema or a tuple of subschemas.
|
|
144
|
-
if (Array.isArray(node.items)) {
|
|
145
|
-
out.items = node.items.map(normalizeRefSlots);
|
|
146
|
-
} else if (node.items && typeof node.items === "object") {
|
|
147
|
-
out.items = normalizeRefSlots(node.items);
|
|
148
|
-
}
|
|
149
|
-
for (const key of SUBSCHEMA_MAP) {
|
|
150
|
-
const value = node[key];
|
|
151
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
152
|
-
const mapped: Record<string, unknown> = {};
|
|
153
|
-
for (const [name, sub] of Object.entries(value as Record<string, unknown>)) {
|
|
154
|
-
mapped[name] = normalizeRefSlots(sub);
|
|
155
|
-
}
|
|
156
|
-
out[key] = mapped;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return out;
|
|
160
|
-
}
|
|
161
63
|
|
|
162
64
|
/** Stable URI under which the shared manifest root schema is registered
|
|
163
65
|
* with module-side AJV instances. Module YAMLs reach the fragments via
|
package/src/sentinel.ts
CHANGED
|
@@ -33,12 +33,45 @@ export function isRefSentinel(v: unknown): v is TaggedSentinel & { engine: "ref"
|
|
|
33
33
|
return isTaggedSentinel(v) && v.engine === "ref";
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** The CEL engine's name. Beside the other sentinel predicates for the same
|
|
37
|
+
* reason they are: a consumer that spells an engine name inline is a second
|
|
38
|
+
* place the registry's key is written down. */
|
|
39
|
+
export const CEL_ENGINE = "cel";
|
|
40
|
+
|
|
36
41
|
/** Engine names of the two file-embedding tags. Named here beside the other
|
|
37
42
|
* sentinel predicates so the kernel's resolution pass and the engines
|
|
38
43
|
* themselves agree on one spelling. */
|
|
39
44
|
export const INCLUDE_TEXT_ENGINE = "include-text";
|
|
40
45
|
export const INCLUDE_BYTES_ENGINE = "include-bytes";
|
|
41
46
|
|
|
47
|
+
/**
|
|
48
|
+
* The dotted chain a value names, or undefined.
|
|
49
|
+
*
|
|
50
|
+
* Only a PLAIN CHAIN — `steps.encode.result.output` — in either spelling a
|
|
51
|
+
* manifest may carry it: a `!cel` sentinel or the `${{ }}` string form. An
|
|
52
|
+
* expression that COMPUTES rather than names has no schema to read off a context,
|
|
53
|
+
* so a caller that navigates one gets nothing and reports nothing: silence where
|
|
54
|
+
* the analyzer knows least is the conservative direction.
|
|
55
|
+
*
|
|
56
|
+
* Here rather than in each caller because "is this expression a plain chain" is
|
|
57
|
+
* one question, and two copies of the answer would eventually disagree about a
|
|
58
|
+
* shape like `a.b[0]`.
|
|
59
|
+
*/
|
|
60
|
+
export function plainChainOf(value: unknown): string | undefined {
|
|
61
|
+
const source = isTaggedSentinel(value)
|
|
62
|
+
? value.engine === CEL_ENGINE
|
|
63
|
+
? value.source
|
|
64
|
+
: undefined
|
|
65
|
+
: typeof value === "string"
|
|
66
|
+
? /^\s*\$\{\{(.+)\}\}\s*$/.exec(value)?.[1]
|
|
67
|
+
: undefined;
|
|
68
|
+
if (typeof source !== "string") return undefined;
|
|
69
|
+
const trimmed = source.trim();
|
|
70
|
+
return /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(trimmed)
|
|
71
|
+
? trimmed
|
|
72
|
+
: undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
42
75
|
/** Both file-embedding tag names, for a consumer holding an engine NAME rather
|
|
43
76
|
* than a value (the analyzer's expression walk reports names). */
|
|
44
77
|
export const INCLUDE_ENGINE_NAMES: ReadonlySet<string> = new Set([
|