@telorun/templating 0.2.3 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # SUSTAINABLE USE LICENSE (Fair-code)
2
2
 
3
- Copyright (c) 2026 DiglyAI
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 DiglyAI.
17
+ For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="./assets/telo.png" alt="Telo" width="200" />
2
+ <img src="https://raw.githubusercontent.com/telorun/telo/main/assets/telo.png" alt="Telo" width="200" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">Telo</h1>
@@ -61,12 +61,12 @@ targets:
61
61
  kind: Telo.Import
62
62
  metadata:
63
63
  name: Http
64
- source: ../modules/http-server
64
+ source: std/http-server@0.4.0
65
65
  ---
66
66
  kind: Telo.Import
67
67
  metadata:
68
68
  name: Sql
69
- source: ../modules/sql
69
+ source: std/sql@0.2.3
70
70
  ---
71
71
  # SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
72
72
  kind: Sql.Connection
@@ -1 +1 @@
1
- {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAEA,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,EAA+B,CAAC;AAEtF,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":"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
@@ -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;AAErD,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,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,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
+ };
@@ -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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/templating",
3
- "version": "0.2.3",
3
+ "version": "1.1.0",
4
4
  "description": "Telo Templating - Engine registry and shared CEL core for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -36,14 +36,16 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@marcbachmann/cel-js": "^7.5.3",
39
- "yaml": "^2.8.3",
40
- "@telorun/sdk": "0.11.1"
39
+ "yaml": "^2.8.3"
41
40
  },
42
41
  "devDependencies": {
43
42
  "@types/node": "^20.0.0",
44
43
  "typescript": "^5.0.0",
45
44
  "vitest": "^2.1.8"
46
45
  },
46
+ "peerDependencies": {
47
+ "@telorun/sdk": "1.0.0"
48
+ },
47
49
  "scripts": {
48
50
  "build": "tsc -p tsconfig.lib.json",
49
51
  "test": "vitest run",
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
+ }