@telorun/templating 1.0.0 → 1.1.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/LICENSE +2 -2
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +2 -1
- package/dist/engines/ref.d.ts +12 -0
- package/dist/engines/ref.d.ts.map +1 -0
- package/dist/engines/ref.js +18 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/manifest-schemas.d.ts +70 -0
- package/dist/manifest-schemas.d.ts.map +1 -0
- package/dist/manifest-schemas.js +46 -0
- package/dist/sentinel.d.ts +8 -0
- package/dist/sentinel.d.ts.map +1 -1
- package/dist/sentinel.js +8 -0
- package/package.json +1 -1
- package/src/builtins.ts +2 -1
- package/src/engines/ref.ts +22 -0
- package/src/index.ts +7 -1
- package/src/manifest-schemas.ts +49 -0
- package/src/sentinel.ts +9 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SUSTAINABLE USE LICENSE (Fair-code)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 CodeNet Sp. z o.o.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
|
|
6
6
|
|
|
@@ -14,4 +14,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
14
14
|
|
|
15
15
|
5. DISCLAIMER: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
|
|
16
16
|
|
|
17
|
-
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact
|
|
17
|
+
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
|
package/dist/builtins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAGA,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,EAA0C,CAAC;AAEjG,wBAAgB,qBAAqB,IAAI,wBAAwB,CAMhE;AAID;;;oBAGoB;AACpB,wBAAgB,eAAe,IAAI,wBAAwB,CAK1D"}
|
package/dist/builtins.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { celEngine } from "./engines/cel.js";
|
|
2
2
|
import { literalEngine } from "./engines/literal.js";
|
|
3
|
+
import { refEngine } from "./engines/ref.js";
|
|
3
4
|
import { TemplatingEngineRegistry } from "./registry.js";
|
|
4
5
|
/** Single source of truth for the built-in templating engines. Every host
|
|
5
6
|
* (kernel, analyzer, editor, vscode extension) calls `createDefaultRegistry`
|
|
@@ -7,7 +8,7 @@ import { TemplatingEngineRegistry } from "./registry.js";
|
|
|
7
8
|
* agree on which engines exist. Per-host à-la-carte registration would let
|
|
8
9
|
* a manifest validate clean in one host (e.g. `cel` only) and crash in
|
|
9
10
|
* another (e.g. `cel + literal`); always ship the same set. */
|
|
10
|
-
export const builtinEngines = [celEngine, literalEngine];
|
|
11
|
+
export const builtinEngines = [celEngine, literalEngine, refEngine];
|
|
11
12
|
export function createDefaultRegistry() {
|
|
12
13
|
const registry = new TemplatingEngineRegistry();
|
|
13
14
|
for (const engine of builtinEngines) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemplatingEngine } from "../engine.js";
|
|
2
|
+
/** The `!ref` engine. Marks a tagged scalar as a resource reference: the
|
|
3
|
+
* source is the bare resource name, looked up against the slot's
|
|
4
|
+
* `x-telo-ref` constraint by the analyzer and the kernel.
|
|
5
|
+
*
|
|
6
|
+
* Returns the source string verbatim at compile time. The actual lookup
|
|
7
|
+
* (sentinel → live ResourceInstance) happens at resource-context init,
|
|
8
|
+
* not at templating-engine compile — the engine just preserves the source
|
|
9
|
+
* through the parse/serialize round-trip. Analysis is a no-op; reference
|
|
10
|
+
* resolution is the analyzer's responsibility via the ref-aware walker. */
|
|
11
|
+
export declare const refEngine: TemplatingEngine;
|
|
12
|
+
//# sourceMappingURL=ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/engines/ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;;;;4EAQ4E;AAC5E,eAAO,MAAM,SAAS,EAAE,gBAUvB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The `!ref` engine. Marks a tagged scalar as a resource reference: the
|
|
2
|
+
* source is the bare resource name, looked up against the slot's
|
|
3
|
+
* `x-telo-ref` constraint by the analyzer and the kernel.
|
|
4
|
+
*
|
|
5
|
+
* Returns the source string verbatim at compile time. The actual lookup
|
|
6
|
+
* (sentinel → live ResourceInstance) happens at resource-context init,
|
|
7
|
+
* not at templating-engine compile — the engine just preserves the source
|
|
8
|
+
* through the parse/serialize round-trip. Analysis is a no-op; reference
|
|
9
|
+
* resolution is the analyzer's responsibility via the ref-aware walker. */
|
|
10
|
+
export const refEngine = {
|
|
11
|
+
name: "ref",
|
|
12
|
+
compile(source) {
|
|
13
|
+
return source;
|
|
14
|
+
},
|
|
15
|
+
analyze() {
|
|
16
|
+
return [];
|
|
17
|
+
},
|
|
18
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,11 @@ export { extractAccessChains, validateChainAgainstSchema } from "./cel/analyze.j
|
|
|
4
4
|
export { walkCelExpressions } from "./cel/walk.js";
|
|
5
5
|
export { celEngine } from "./engines/cel.js";
|
|
6
6
|
export { literalEngine } from "./engines/literal.js";
|
|
7
|
+
export { refEngine } from "./engines/ref.js";
|
|
7
8
|
export { TemplatingEngineRegistry } from "./registry.js";
|
|
8
9
|
export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
|
|
9
10
|
export type { AnalyzeEnv, CompileEnv, EngineDiagnostic, TemplatingEngine, } from "./engine.js";
|
|
10
|
-
export { isTaggedSentinel, makeTaggedSentinel, type TaggedSentinel } from "./sentinel.js";
|
|
11
|
+
export { isRefSentinel, isTaggedSentinel, makeTaggedSentinel, type TaggedSentinel } from "./sentinel.js";
|
|
11
12
|
export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
|
|
13
|
+
export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema, } from "./manifest-schemas.js";
|
|
12
14
|
//# 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,EAAE,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvF,YAAY,EACV,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACzG,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
|
@@ -4,7 +4,9 @@ export { extractAccessChains, validateChainAgainstSchema } from "./cel/analyze.j
|
|
|
4
4
|
export { walkCelExpressions } from "./cel/walk.js";
|
|
5
5
|
export { celEngine } from "./engines/cel.js";
|
|
6
6
|
export { literalEngine } from "./engines/literal.js";
|
|
7
|
+
export { refEngine } from "./engines/ref.js";
|
|
7
8
|
export { TemplatingEngineRegistry } from "./registry.js";
|
|
8
9
|
export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
|
|
9
|
-
export { isTaggedSentinel, makeTaggedSentinel } from "./sentinel.js";
|
|
10
|
+
export { isRefSentinel, isTaggedSentinel, makeTaggedSentinel } from "./sentinel.js";
|
|
10
11
|
export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
|
|
12
|
+
export { MANIFEST_SCHEMA_URI, ManifestRootSchema, ResourceRefSchema, } from "./manifest-schemas.js";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Shared JSON-Schema fragments referenced from every module's `telo.yaml`
|
|
2
|
+
* via `$ref: "telo://manifest#/$defs/<Name>"`. Lives in the templating
|
|
3
|
+
* package (alongside the YAML tag mechanism) because the analyzer cannot
|
|
4
|
+
* depend on the kernel — both consume this package, so this is the only
|
|
5
|
+
* layer where a single source of truth can sit. The kernel re-exports
|
|
6
|
+
* these symbols from its own `manifest-schemas` surface for downstream
|
|
7
|
+
* ergonomics. */
|
|
8
|
+
/** Schema fragment for a resource reference produced by the `!ref` YAML
|
|
9
|
+
* tag. After parsing, the value is a `TaggedSentinel` (engine "ref")
|
|
10
|
+
* whose `source` is the bare resource name. Module schemas declare a
|
|
11
|
+
* ref slot as `$ref: "telo://manifest#/$defs/ResourceRef"`; the
|
|
12
|
+
* analyzer's reference walker is what looks the name up against the
|
|
13
|
+
* slot's `x-telo-ref` constraint.
|
|
14
|
+
*
|
|
15
|
+
* Migration note: while the legacy bare-name string and `{kind, name}`
|
|
16
|
+
* object forms are still accepted by the analyzer/kernel walkers (in an
|
|
17
|
+
* additive transitional state), those shapes are *not* part of this
|
|
18
|
+
* schema. Slots that need to accept legacy forms during migration keep
|
|
19
|
+
* their hand-rolled `oneOf` until the cutover lands. */
|
|
20
|
+
export declare const ResourceRefSchema: {
|
|
21
|
+
type: string;
|
|
22
|
+
required: string[];
|
|
23
|
+
properties: {
|
|
24
|
+
__tagged: {
|
|
25
|
+
const: boolean;
|
|
26
|
+
};
|
|
27
|
+
engine: {
|
|
28
|
+
const: string;
|
|
29
|
+
};
|
|
30
|
+
source: {
|
|
31
|
+
type: string;
|
|
32
|
+
minLength: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
additionalProperties: boolean;
|
|
36
|
+
};
|
|
37
|
+
/** Stable URI under which the shared manifest root schema is registered
|
|
38
|
+
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
39
|
+
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
40
|
+
* the symbol names intentionally omit a host-specific prefix since the
|
|
41
|
+
* fragments live in `@telorun/templating` (the only layer both kernel
|
|
42
|
+
* and analyzer depend on). */
|
|
43
|
+
export declare const MANIFEST_SCHEMA_URI = "telo://manifest";
|
|
44
|
+
/** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
|
|
45
|
+
* `$defs` only — it isn't validated against directly. Adding a new
|
|
46
|
+
* shared fragment means putting it under `$defs` here and `$ref`-ing
|
|
47
|
+
* it from module schemas. */
|
|
48
|
+
export declare const ManifestRootSchema: {
|
|
49
|
+
$id: string;
|
|
50
|
+
$defs: {
|
|
51
|
+
ResourceRef: {
|
|
52
|
+
type: string;
|
|
53
|
+
required: string[];
|
|
54
|
+
properties: {
|
|
55
|
+
__tagged: {
|
|
56
|
+
const: boolean;
|
|
57
|
+
};
|
|
58
|
+
engine: {
|
|
59
|
+
const: string;
|
|
60
|
+
};
|
|
61
|
+
source: {
|
|
62
|
+
type: string;
|
|
63
|
+
minLength: number;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
additionalProperties: boolean;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=manifest-schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-schemas.d.ts","sourceRoot":"","sources":["../src/manifest-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;kBAMkB;AAElB;;;;;;;;;;;yDAWyD;AACzD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;CAS7B,CAAC;AAEF;;;;;+BAK+B;AAC/B,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;8BAG8B;AAC9B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;CAK9B,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Shared JSON-Schema fragments referenced from every module's `telo.yaml`
|
|
2
|
+
* via `$ref: "telo://manifest#/$defs/<Name>"`. Lives in the templating
|
|
3
|
+
* package (alongside the YAML tag mechanism) because the analyzer cannot
|
|
4
|
+
* depend on the kernel — both consume this package, so this is the only
|
|
5
|
+
* layer where a single source of truth can sit. The kernel re-exports
|
|
6
|
+
* these symbols from its own `manifest-schemas` surface for downstream
|
|
7
|
+
* ergonomics. */
|
|
8
|
+
/** Schema fragment for a resource reference produced by the `!ref` YAML
|
|
9
|
+
* tag. After parsing, the value is a `TaggedSentinel` (engine "ref")
|
|
10
|
+
* whose `source` is the bare resource name. Module schemas declare a
|
|
11
|
+
* ref slot as `$ref: "telo://manifest#/$defs/ResourceRef"`; the
|
|
12
|
+
* analyzer's reference walker is what looks the name up against the
|
|
13
|
+
* slot's `x-telo-ref` constraint.
|
|
14
|
+
*
|
|
15
|
+
* Migration note: while the legacy bare-name string and `{kind, name}`
|
|
16
|
+
* object forms are still accepted by the analyzer/kernel walkers (in an
|
|
17
|
+
* additive transitional state), those shapes are *not* part of this
|
|
18
|
+
* schema. Slots that need to accept legacy forms during migration keep
|
|
19
|
+
* their hand-rolled `oneOf` until the cutover lands. */
|
|
20
|
+
export const ResourceRefSchema = {
|
|
21
|
+
type: "object",
|
|
22
|
+
required: ["__tagged", "engine", "source"],
|
|
23
|
+
properties: {
|
|
24
|
+
__tagged: { const: true },
|
|
25
|
+
engine: { const: "ref" },
|
|
26
|
+
source: { type: "string", minLength: 1 },
|
|
27
|
+
},
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
};
|
|
30
|
+
/** Stable URI under which the shared manifest root schema is registered
|
|
31
|
+
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
32
|
+
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
33
|
+
* the symbol names intentionally omit a host-specific prefix since the
|
|
34
|
+
* fragments live in `@telorun/templating` (the only layer both kernel
|
|
35
|
+
* and analyzer depend on). */
|
|
36
|
+
export const MANIFEST_SCHEMA_URI = "telo://manifest";
|
|
37
|
+
/** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
|
|
38
|
+
* `$defs` only — it isn't validated against directly. Adding a new
|
|
39
|
+
* shared fragment means putting it under `$defs` here and `$ref`-ing
|
|
40
|
+
* it from module schemas. */
|
|
41
|
+
export const ManifestRootSchema = {
|
|
42
|
+
$id: MANIFEST_SCHEMA_URI,
|
|
43
|
+
$defs: {
|
|
44
|
+
ResourceRef: ResourceRefSchema,
|
|
45
|
+
},
|
|
46
|
+
};
|
package/dist/sentinel.d.ts
CHANGED
|
@@ -11,4 +11,12 @@ export interface TaggedSentinel {
|
|
|
11
11
|
}
|
|
12
12
|
export declare function isTaggedSentinel(v: unknown): v is TaggedSentinel;
|
|
13
13
|
export declare function makeTaggedSentinel(engine: string, source: string): TaggedSentinel;
|
|
14
|
+
/** True when `v` is a `!ref <name>` sentinel — i.e. a resource reference
|
|
15
|
+
* marked at parse time. Downstream walkers (analyzer ref validation,
|
|
16
|
+
* inline normalization, dependency graph, kernel resource resolution)
|
|
17
|
+
* use this to tell a reference from an inline definition without
|
|
18
|
+
* inferring intent from field presence. */
|
|
19
|
+
export declare function isRefSentinel(v: unknown): v is TaggedSentinel & {
|
|
20
|
+
engine: "ref";
|
|
21
|
+
};
|
|
14
22
|
//# sourceMappingURL=sentinel.d.ts.map
|
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"}
|
|
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"}
|
package/dist/sentinel.js
CHANGED
|
@@ -8,3 +8,11 @@ export function isTaggedSentinel(v) {
|
|
|
8
8
|
export function makeTaggedSentinel(engine, source) {
|
|
9
9
|
return { __tagged: true, engine, source };
|
|
10
10
|
}
|
|
11
|
+
/** True when `v` is a `!ref <name>` sentinel — i.e. a resource reference
|
|
12
|
+
* marked at parse time. Downstream walkers (analyzer ref validation,
|
|
13
|
+
* inline normalization, dependency graph, kernel resource resolution)
|
|
14
|
+
* use this to tell a reference from an inline definition without
|
|
15
|
+
* inferring intent from field presence. */
|
|
16
|
+
export function isRefSentinel(v) {
|
|
17
|
+
return isTaggedSentinel(v) && v.engine === "ref";
|
|
18
|
+
}
|
package/package.json
CHANGED
package/src/builtins.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { celEngine } from "./engines/cel.js";
|
|
2
2
|
import { literalEngine } from "./engines/literal.js";
|
|
3
|
+
import { refEngine } from "./engines/ref.js";
|
|
3
4
|
import { TemplatingEngineRegistry } from "./registry.js";
|
|
4
5
|
import type { TemplatingEngine } from "./engine.js";
|
|
5
6
|
|
|
@@ -9,7 +10,7 @@ import type { TemplatingEngine } from "./engine.js";
|
|
|
9
10
|
* agree on which engines exist. Per-host à-la-carte registration would let
|
|
10
11
|
* a manifest validate clean in one host (e.g. `cel` only) and crash in
|
|
11
12
|
* another (e.g. `cel + literal`); always ship the same set. */
|
|
12
|
-
export const builtinEngines: readonly TemplatingEngine[] = [celEngine, literalEngine];
|
|
13
|
+
export const builtinEngines: readonly TemplatingEngine[] = [celEngine, literalEngine, refEngine];
|
|
13
14
|
|
|
14
15
|
export function createDefaultRegistry(): TemplatingEngineRegistry {
|
|
15
16
|
const registry = new TemplatingEngineRegistry();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { TemplatingEngine } from "../engine.js";
|
|
2
|
+
|
|
3
|
+
/** The `!ref` engine. Marks a tagged scalar as a resource reference: the
|
|
4
|
+
* source is the bare resource name, looked up against the slot's
|
|
5
|
+
* `x-telo-ref` constraint by the analyzer and the kernel.
|
|
6
|
+
*
|
|
7
|
+
* Returns the source string verbatim at compile time. The actual lookup
|
|
8
|
+
* (sentinel → live ResourceInstance) happens at resource-context init,
|
|
9
|
+
* not at templating-engine compile — the engine just preserves the source
|
|
10
|
+
* through the parse/serialize round-trip. Analysis is a no-op; reference
|
|
11
|
+
* resolution is the analyzer's responsibility via the ref-aware walker. */
|
|
12
|
+
export const refEngine: TemplatingEngine = {
|
|
13
|
+
name: "ref",
|
|
14
|
+
|
|
15
|
+
compile(source) {
|
|
16
|
+
return source;
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
analyze() {
|
|
20
|
+
return [];
|
|
21
|
+
},
|
|
22
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { walkCelExpressions } from "./cel/walk.js";
|
|
|
10
10
|
|
|
11
11
|
export { celEngine } from "./engines/cel.js";
|
|
12
12
|
export { literalEngine } from "./engines/literal.js";
|
|
13
|
+
export { refEngine } from "./engines/ref.js";
|
|
13
14
|
|
|
14
15
|
export { TemplatingEngineRegistry } from "./registry.js";
|
|
15
16
|
export { builtinEngines, createDefaultRegistry, defaultRegistry } from "./builtins.js";
|
|
@@ -20,5 +21,10 @@ export type {
|
|
|
20
21
|
TemplatingEngine,
|
|
21
22
|
} from "./engine.js";
|
|
22
23
|
|
|
23
|
-
export { isTaggedSentinel, makeTaggedSentinel, type TaggedSentinel } from "./sentinel.js";
|
|
24
|
+
export { isRefSentinel, isTaggedSentinel, makeTaggedSentinel, type TaggedSentinel } from "./sentinel.js";
|
|
24
25
|
export { buildCustomTags, defaultCustomTags } from "./yaml-tags.js";
|
|
26
|
+
export {
|
|
27
|
+
MANIFEST_SCHEMA_URI,
|
|
28
|
+
ManifestRootSchema,
|
|
29
|
+
ResourceRefSchema,
|
|
30
|
+
} from "./manifest-schemas.js";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Shared JSON-Schema fragments referenced from every module's `telo.yaml`
|
|
2
|
+
* via `$ref: "telo://manifest#/$defs/<Name>"`. Lives in the templating
|
|
3
|
+
* package (alongside the YAML tag mechanism) because the analyzer cannot
|
|
4
|
+
* depend on the kernel — both consume this package, so this is the only
|
|
5
|
+
* layer where a single source of truth can sit. The kernel re-exports
|
|
6
|
+
* these symbols from its own `manifest-schemas` surface for downstream
|
|
7
|
+
* ergonomics. */
|
|
8
|
+
|
|
9
|
+
/** Schema fragment for a resource reference produced by the `!ref` YAML
|
|
10
|
+
* tag. After parsing, the value is a `TaggedSentinel` (engine "ref")
|
|
11
|
+
* whose `source` is the bare resource name. Module schemas declare a
|
|
12
|
+
* ref slot as `$ref: "telo://manifest#/$defs/ResourceRef"`; the
|
|
13
|
+
* analyzer's reference walker is what looks the name up against the
|
|
14
|
+
* slot's `x-telo-ref` constraint.
|
|
15
|
+
*
|
|
16
|
+
* Migration note: while the legacy bare-name string and `{kind, name}`
|
|
17
|
+
* object forms are still accepted by the analyzer/kernel walkers (in an
|
|
18
|
+
* additive transitional state), those shapes are *not* part of this
|
|
19
|
+
* schema. Slots that need to accept legacy forms during migration keep
|
|
20
|
+
* their hand-rolled `oneOf` until the cutover lands. */
|
|
21
|
+
export const ResourceRefSchema = {
|
|
22
|
+
type: "object",
|
|
23
|
+
required: ["__tagged", "engine", "source"],
|
|
24
|
+
properties: {
|
|
25
|
+
__tagged: { const: true },
|
|
26
|
+
engine: { const: "ref" },
|
|
27
|
+
source: { type: "string", minLength: 1 },
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Stable URI under which the shared manifest root schema is registered
|
|
33
|
+
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
34
|
+
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
35
|
+
* the symbol names intentionally omit a host-specific prefix since the
|
|
36
|
+
* fragments live in `@telorun/templating` (the only layer both kernel
|
|
37
|
+
* and analyzer depend on). */
|
|
38
|
+
export const MANIFEST_SCHEMA_URI = "telo://manifest";
|
|
39
|
+
|
|
40
|
+
/** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
|
|
41
|
+
* `$defs` only — it isn't validated against directly. Adding a new
|
|
42
|
+
* shared fragment means putting it under `$defs` here and `$ref`-ing
|
|
43
|
+
* it from module schemas. */
|
|
44
|
+
export const ManifestRootSchema = {
|
|
45
|
+
$id: MANIFEST_SCHEMA_URI,
|
|
46
|
+
$defs: {
|
|
47
|
+
ResourceRef: ResourceRefSchema,
|
|
48
|
+
},
|
|
49
|
+
};
|
package/src/sentinel.ts
CHANGED
|
@@ -23,3 +23,12 @@ export function isTaggedSentinel(v: unknown): v is TaggedSentinel {
|
|
|
23
23
|
export function makeTaggedSentinel(engine: string, source: string): TaggedSentinel {
|
|
24
24
|
return { __tagged: true, engine, source };
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/** True when `v` is a `!ref <name>` sentinel — i.e. a resource reference
|
|
28
|
+
* marked at parse time. Downstream walkers (analyzer ref validation,
|
|
29
|
+
* inline normalization, dependency graph, kernel resource resolution)
|
|
30
|
+
* use this to tell a reference from an inline definition without
|
|
31
|
+
* inferring intent from field presence. */
|
|
32
|
+
export function isRefSentinel(v: unknown): v is TaggedSentinel & { engine: "ref" } {
|
|
33
|
+
return isTaggedSentinel(v) && v.engine === "ref";
|
|
34
|
+
}
|