@telorun/templating 0.14.0 → 0.16.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/cel/catalog.d.ts.map +1 -1
- package/dist/cel/catalog.js +108 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/package.json +2 -2
- package/src/cel/catalog.ts +110 -0
- package/src/index.ts +0 -5
- package/dist/manifest-schemas.d.ts +0 -122
- package/dist/manifest-schemas.d.ts.map +0 -1
- package/dist/manifest-schemas.js +0 -77
- package/src/manifest-schemas.ts +0 -81
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/cel/catalog.ts"],"names":[],"mappings":"AAGA;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AA2CD,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,MAAM,GACN,UAAU,GACV,SAAS,GACT,MAAM,CAAC;AAEX;;;qBAGqB;AACrB,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;yDAIqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACjE;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/cel/catalog.ts"],"names":[],"mappings":"AAGA;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AA2CD,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,MAAM,GACN,UAAU,GACV,SAAS,GACT,MAAM,CAAC;AAEX;;;qBAGqB;AACrB,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;0EACsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;yDAIqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;wBACoB;IACpB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;2EACuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACjE;AAED,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAwH5D,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EA8lBlD,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,eAAe,EAAE,CAEtD"}
|
package/dist/cel/catalog.js
CHANGED
|
@@ -106,6 +106,51 @@ const dateInZone = (tz) => {
|
|
|
106
106
|
const p = zoneParts(now, tz, { year: "numeric", month: "2-digit", day: "2-digit" });
|
|
107
107
|
return `${p.year}-${p.month}-${p.day}`;
|
|
108
108
|
};
|
|
109
|
+
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
110
|
+
/** Base64 → bytes, written out rather than delegated: `Buffer` is not browser-
|
|
111
|
+
* safe and `atob` round-trips through a string, which is the corruption this
|
|
112
|
+
* pair exists to avoid. Accepts the URL-safe alphabet and tolerates missing
|
|
113
|
+
* padding, both of which appear in real API payloads; a character outside the
|
|
114
|
+
* alphabet is a hard error rather than a silently dropped byte. */
|
|
115
|
+
function decodeBase64ToBytes(input) {
|
|
116
|
+
const clean = input.replace(/[\r\n\t ]/g, "").replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
117
|
+
// A remainder of 1 cannot come from any byte sequence: base64 encodes 3 bytes
|
|
118
|
+
// as 4 characters, so the valid remainders are 0, 2 and 3. Left to the loop it
|
|
119
|
+
// would leave 6 bits unwritten and return a SHORT buffer — the silently dropped
|
|
120
|
+
// byte this function refuses for a bad character.
|
|
121
|
+
if (clean.length % 4 === 1) {
|
|
122
|
+
throw new Error("bytesFromBase64: input length is not valid base64");
|
|
123
|
+
}
|
|
124
|
+
const out = new Uint8Array(Math.floor((clean.length * 3) / 4));
|
|
125
|
+
let bits = 0;
|
|
126
|
+
let acc = 0;
|
|
127
|
+
let written = 0;
|
|
128
|
+
for (const ch of clean) {
|
|
129
|
+
const value = BASE64_ALPHABET.indexOf(ch);
|
|
130
|
+
if (value < 0)
|
|
131
|
+
throw new Error(`bytesFromBase64: '${ch}' is not a base64 character`);
|
|
132
|
+
acc = (acc << 6) | value;
|
|
133
|
+
bits += 6;
|
|
134
|
+
if (bits >= 8) {
|
|
135
|
+
bits -= 8;
|
|
136
|
+
out[written++] = (acc >> bits) & 0xff;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return out.subarray(0, written);
|
|
140
|
+
}
|
|
141
|
+
function encodeBytesToBase64(input) {
|
|
142
|
+
let out = "";
|
|
143
|
+
for (let i = 0; i < input.length; i += 3) {
|
|
144
|
+
const a = input[i];
|
|
145
|
+
const b = i + 1 < input.length ? input[i + 1] : undefined;
|
|
146
|
+
const c = i + 2 < input.length ? input[i + 2] : undefined;
|
|
147
|
+
out += BASE64_ALPHABET[a >> 2];
|
|
148
|
+
out += BASE64_ALPHABET[((a & 0x03) << 4) | ((b ?? 0) >> 4)];
|
|
149
|
+
out += b === undefined ? "=" : BASE64_ALPHABET[((b & 0x0f) << 2) | ((c ?? 0) >> 6)];
|
|
150
|
+
out += c === undefined ? "=" : BASE64_ALPHABET[c & 0x3f];
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
109
154
|
export const CEL_FUNCTIONS = [
|
|
110
155
|
// Collections
|
|
111
156
|
{
|
|
@@ -303,6 +348,45 @@ export const CEL_FUNCTIONS = [
|
|
|
303
348
|
hostBacked: false,
|
|
304
349
|
build: () => (s, suffix) => suffix && s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s,
|
|
305
350
|
},
|
|
351
|
+
// One `slice` over the three sequence types rather than three names: CEL
|
|
352
|
+
// already treats `size()` that way, and the alternative teaches an author that
|
|
353
|
+
// slicing bytes is a different operation from slicing a string.
|
|
354
|
+
//
|
|
355
|
+
// OVERLOADED BY PARAMETER TYPE, so a wrong receiver is rejected — `slice(42, 0,
|
|
356
|
+
// 1)` matches nothing. What cannot ALSO be registered is a `dyn` parameter
|
|
357
|
+
// variant: cel-js refuses overloads that overlap, and a `dyn` first parameter
|
|
358
|
+
// overlaps all three. That is why the RETURN is `dyn` rather than the input's
|
|
359
|
+
// own type. A step result whose producer declares no output type is `dyn`, and
|
|
360
|
+
// with concrete returns the checker resolves such a call to whichever overload
|
|
361
|
+
// was registered first and pins the result to it — so slicing an untyped byte
|
|
362
|
+
// buffer came back typed `string`, and a byte consumer downstream reported a
|
|
363
|
+
// mismatch against perfectly valid CEL. Precision on the way out would be lost
|
|
364
|
+
// at the CEL boundary anyway, where type arguments are erased; being wrong
|
|
365
|
+
// about an untyped receiver would not be.
|
|
366
|
+
{
|
|
367
|
+
name: "slice",
|
|
368
|
+
signature: "slice(dyn, int, int): dyn",
|
|
369
|
+
register: [
|
|
370
|
+
"slice(bytes, int, int): dyn",
|
|
371
|
+
"slice(string, int, int): dyn",
|
|
372
|
+
"slice(list, int, int): dyn",
|
|
373
|
+
],
|
|
374
|
+
// Not "string": it is the one function over strings, bytes AND lists, and the
|
|
375
|
+
// category is what groups it in the generated reference — filing it under
|
|
376
|
+
// strings hides it from the byte and list readers who need it most.
|
|
377
|
+
category: "collection",
|
|
378
|
+
summary: "Take the half-open range [start, end) of a string, bytes or list.",
|
|
379
|
+
deterministic: true,
|
|
380
|
+
hostBacked: false,
|
|
381
|
+
// Indices arrive as BigInt (a CEL int is int64), and both `String.slice` and
|
|
382
|
+
// `TypedArray.subarray` reject one. `subarray` rather than `slice` for bytes:
|
|
383
|
+
// a view costs no copy, and every consumer treats the result as read-only.
|
|
384
|
+
build: () => (value, start, end) => {
|
|
385
|
+
const from = Number(start);
|
|
386
|
+
const to = Number(end);
|
|
387
|
+
return value instanceof Uint8Array ? value.subarray(from, to) : value.slice(from, to);
|
|
388
|
+
},
|
|
389
|
+
},
|
|
306
390
|
// Math
|
|
307
391
|
{
|
|
308
392
|
name: "abs",
|
|
@@ -416,6 +500,30 @@ export const CEL_FUNCTIONS = [
|
|
|
416
500
|
hostBacked: true,
|
|
417
501
|
build: (h) => (s) => h.base64Decode(s),
|
|
418
502
|
},
|
|
503
|
+
// Base64 <-> BYTES, as distinct from the two above, which are UTF-8
|
|
504
|
+
// string<->string and silently corrupt anything that is not text. Those keep
|
|
505
|
+
// their meaning (changing it would change what shipped manifests mean); these
|
|
506
|
+
// are the correct path for binary, and the pair a byte-typed slot needs.
|
|
507
|
+
// Pure-JS on purpose: `Buffer` is what made the string pair host-backed, and
|
|
508
|
+
// this package has to stay browser-safe.
|
|
509
|
+
{
|
|
510
|
+
name: "bytesFromBase64",
|
|
511
|
+
signature: "bytesFromBase64(string): bytes",
|
|
512
|
+
category: "encoding",
|
|
513
|
+
summary: "Decode a base64 string to raw bytes.",
|
|
514
|
+
deterministic: true,
|
|
515
|
+
hostBacked: false,
|
|
516
|
+
build: () => (s) => decodeBase64ToBytes(s),
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
name: "bytesToBase64",
|
|
520
|
+
signature: "bytesToBase64(bytes): string",
|
|
521
|
+
category: "encoding",
|
|
522
|
+
summary: "Encode raw bytes as a base64 string.",
|
|
523
|
+
deterministic: true,
|
|
524
|
+
hostBacked: false,
|
|
525
|
+
build: () => (b) => encodeBytesToBase64(b),
|
|
526
|
+
},
|
|
419
527
|
{
|
|
420
528
|
name: "urlEncode",
|
|
421
529
|
signature: "urlEncode(string): string",
|
package/dist/index.d.ts
CHANGED
|
@@ -14,5 +14,4 @@ export { builtinEngines, createDefaultRegistry, defaultRegistry, producedTypeOf,
|
|
|
14
14
|
export type { AnalyzeEnv, AnalyzeResult, CallSite, CompileEnv, DiagnosticFix, EngineDiagnostic, EngineFileClaim, TemplatingEngine, } from "./engine.js";
|
|
15
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, } from "./manifest-schemas.js";
|
|
18
17
|
//# 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,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
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -13,4 +13,3 @@ export { TemplatingEngineRegistry } from "./registry.js";
|
|
|
13
13
|
export { builtinEngines, createDefaultRegistry, defaultRegistry, producedTypeOf, } from "./builtins.js";
|
|
14
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, } from "./manifest-schemas.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/templating",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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.75.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@telorun/sdk": "*"
|
package/src/cel/catalog.ts
CHANGED
|
@@ -170,6 +170,53 @@ const dateInZone = (tz: string): string => {
|
|
|
170
170
|
return `${p.year}-${p.month}-${p.day}`;
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
+
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
174
|
+
|
|
175
|
+
/** Base64 → bytes, written out rather than delegated: `Buffer` is not browser-
|
|
176
|
+
* safe and `atob` round-trips through a string, which is the corruption this
|
|
177
|
+
* pair exists to avoid. Accepts the URL-safe alphabet and tolerates missing
|
|
178
|
+
* padding, both of which appear in real API payloads; a character outside the
|
|
179
|
+
* alphabet is a hard error rather than a silently dropped byte. */
|
|
180
|
+
function decodeBase64ToBytes(input: string): Uint8Array {
|
|
181
|
+
const clean = input.replace(/[\r\n\t ]/g, "").replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
182
|
+
// A remainder of 1 cannot come from any byte sequence: base64 encodes 3 bytes
|
|
183
|
+
// as 4 characters, so the valid remainders are 0, 2 and 3. Left to the loop it
|
|
184
|
+
// would leave 6 bits unwritten and return a SHORT buffer — the silently dropped
|
|
185
|
+
// byte this function refuses for a bad character.
|
|
186
|
+
if (clean.length % 4 === 1) {
|
|
187
|
+
throw new Error("bytesFromBase64: input length is not valid base64");
|
|
188
|
+
}
|
|
189
|
+
const out = new Uint8Array(Math.floor((clean.length * 3) / 4));
|
|
190
|
+
let bits = 0;
|
|
191
|
+
let acc = 0;
|
|
192
|
+
let written = 0;
|
|
193
|
+
for (const ch of clean) {
|
|
194
|
+
const value = BASE64_ALPHABET.indexOf(ch);
|
|
195
|
+
if (value < 0) throw new Error(`bytesFromBase64: '${ch}' is not a base64 character`);
|
|
196
|
+
acc = (acc << 6) | value;
|
|
197
|
+
bits += 6;
|
|
198
|
+
if (bits >= 8) {
|
|
199
|
+
bits -= 8;
|
|
200
|
+
out[written++] = (acc >> bits) & 0xff;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return out.subarray(0, written);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function encodeBytesToBase64(input: Uint8Array): string {
|
|
207
|
+
let out = "";
|
|
208
|
+
for (let i = 0; i < input.length; i += 3) {
|
|
209
|
+
const a = input[i]!;
|
|
210
|
+
const b = i + 1 < input.length ? input[i + 1]! : undefined;
|
|
211
|
+
const c = i + 2 < input.length ? input[i + 2]! : undefined;
|
|
212
|
+
out += BASE64_ALPHABET[a >> 2];
|
|
213
|
+
out += BASE64_ALPHABET[((a & 0x03) << 4) | ((b ?? 0) >> 4)];
|
|
214
|
+
out += b === undefined ? "=" : BASE64_ALPHABET[((b & 0x0f) << 2) | ((c ?? 0) >> 6)];
|
|
215
|
+
out += c === undefined ? "=" : BASE64_ALPHABET[c & 0x3f];
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
|
|
173
220
|
export const CEL_FUNCTIONS: readonly CelFunctionDoc[] = [
|
|
174
221
|
// Collections
|
|
175
222
|
{
|
|
@@ -373,6 +420,45 @@ export const CEL_FUNCTIONS: readonly CelFunctionDoc[] = [
|
|
|
373
420
|
build: () => (s: string, suffix: string) =>
|
|
374
421
|
suffix && s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s,
|
|
375
422
|
},
|
|
423
|
+
// One `slice` over the three sequence types rather than three names: CEL
|
|
424
|
+
// already treats `size()` that way, and the alternative teaches an author that
|
|
425
|
+
// slicing bytes is a different operation from slicing a string.
|
|
426
|
+
//
|
|
427
|
+
// OVERLOADED BY PARAMETER TYPE, so a wrong receiver is rejected — `slice(42, 0,
|
|
428
|
+
// 1)` matches nothing. What cannot ALSO be registered is a `dyn` parameter
|
|
429
|
+
// variant: cel-js refuses overloads that overlap, and a `dyn` first parameter
|
|
430
|
+
// overlaps all three. That is why the RETURN is `dyn` rather than the input's
|
|
431
|
+
// own type. A step result whose producer declares no output type is `dyn`, and
|
|
432
|
+
// with concrete returns the checker resolves such a call to whichever overload
|
|
433
|
+
// was registered first and pins the result to it — so slicing an untyped byte
|
|
434
|
+
// buffer came back typed `string`, and a byte consumer downstream reported a
|
|
435
|
+
// mismatch against perfectly valid CEL. Precision on the way out would be lost
|
|
436
|
+
// at the CEL boundary anyway, where type arguments are erased; being wrong
|
|
437
|
+
// about an untyped receiver would not be.
|
|
438
|
+
{
|
|
439
|
+
name: "slice",
|
|
440
|
+
signature: "slice(dyn, int, int): dyn",
|
|
441
|
+
register: [
|
|
442
|
+
"slice(bytes, int, int): dyn",
|
|
443
|
+
"slice(string, int, int): dyn",
|
|
444
|
+
"slice(list, int, int): dyn",
|
|
445
|
+
],
|
|
446
|
+
// Not "string": it is the one function over strings, bytes AND lists, and the
|
|
447
|
+
// category is what groups it in the generated reference — filing it under
|
|
448
|
+
// strings hides it from the byte and list readers who need it most.
|
|
449
|
+
category: "collection",
|
|
450
|
+
summary: "Take the half-open range [start, end) of a string, bytes or list.",
|
|
451
|
+
deterministic: true,
|
|
452
|
+
hostBacked: false,
|
|
453
|
+
// Indices arrive as BigInt (a CEL int is int64), and both `String.slice` and
|
|
454
|
+
// `TypedArray.subarray` reject one. `subarray` rather than `slice` for bytes:
|
|
455
|
+
// a view costs no copy, and every consumer treats the result as read-only.
|
|
456
|
+
build: () => (value: string | Uint8Array | unknown[], start: bigint, end: bigint) => {
|
|
457
|
+
const from = Number(start);
|
|
458
|
+
const to = Number(end);
|
|
459
|
+
return value instanceof Uint8Array ? value.subarray(from, to) : value.slice(from, to);
|
|
460
|
+
},
|
|
461
|
+
},
|
|
376
462
|
// Math
|
|
377
463
|
{
|
|
378
464
|
name: "abs",
|
|
@@ -488,6 +574,30 @@ export const CEL_FUNCTIONS: readonly CelFunctionDoc[] = [
|
|
|
488
574
|
hostBacked: true,
|
|
489
575
|
build: (h) => (s: string) => h.base64Decode(s),
|
|
490
576
|
},
|
|
577
|
+
// Base64 <-> BYTES, as distinct from the two above, which are UTF-8
|
|
578
|
+
// string<->string and silently corrupt anything that is not text. Those keep
|
|
579
|
+
// their meaning (changing it would change what shipped manifests mean); these
|
|
580
|
+
// are the correct path for binary, and the pair a byte-typed slot needs.
|
|
581
|
+
// Pure-JS on purpose: `Buffer` is what made the string pair host-backed, and
|
|
582
|
+
// this package has to stay browser-safe.
|
|
583
|
+
{
|
|
584
|
+
name: "bytesFromBase64",
|
|
585
|
+
signature: "bytesFromBase64(string): bytes",
|
|
586
|
+
category: "encoding",
|
|
587
|
+
summary: "Decode a base64 string to raw bytes.",
|
|
588
|
+
deterministic: true,
|
|
589
|
+
hostBacked: false,
|
|
590
|
+
build: () => (s: string) => decodeBase64ToBytes(s),
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
name: "bytesToBase64",
|
|
594
|
+
signature: "bytesToBase64(bytes): string",
|
|
595
|
+
category: "encoding",
|
|
596
|
+
summary: "Encode raw bytes as a base64 string.",
|
|
597
|
+
deterministic: true,
|
|
598
|
+
hostBacked: false,
|
|
599
|
+
build: () => (b: Uint8Array) => encodeBytesToBase64(b),
|
|
600
|
+
},
|
|
491
601
|
{
|
|
492
602
|
name: "urlEncode",
|
|
493
603
|
signature: "urlEncode(string): string",
|
package/src/index.ts
CHANGED
|
@@ -1,122 +0,0 @@
|
|
|
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 slot. The only form a manifest
|
|
9
|
-
* author writes is the `!ref <name>` (or `!ref <Alias>.<name>`) YAML tag,
|
|
10
|
-
* which parses to a `TaggedSentinel` (engine "ref") whose `source` is the
|
|
11
|
-
* bare resource name. In practice module schemas mark a ref slot with a bare
|
|
12
|
-
* `x-telo-ref` annotation (plus, where the slot only ever holds a reference,
|
|
13
|
-
* `type: object` to reject a stray scalar); the analyzer's reference walker
|
|
14
|
-
* reads `x-telo-ref` to look the name up against that constraint, independent
|
|
15
|
-
* of this fragment. A slot opts into `$ref:
|
|
16
|
-
* "telo://manifest#/$defs/ResourceRef"` only when it wants this exact
|
|
17
|
-
* two-branch shape enforced at the AJV layer too — it is not required, and
|
|
18
|
-
* slots that also accept an inline value (e.g. `inputType` / `outputType`)
|
|
19
|
-
* deliberately do not use it (an inline JSON Schema has no `kind`).
|
|
20
|
-
*
|
|
21
|
-
* Two `anyOf` branches because the value's shape depends on the phase at
|
|
22
|
-
* which it is validated:
|
|
23
|
-
*
|
|
24
|
-
* 1. The raw `!ref` sentinel — what survives to AJV when a cross-module
|
|
25
|
-
* reference can't be resolved in standalone single-file analysis (the
|
|
26
|
-
* imported module isn't loaded). `substituteCelFields` deliberately
|
|
27
|
-
* keeps the sentinel so this branch matches.
|
|
28
|
-
* 2. A resolved reference object — `{kind, name, alias?}` substituted in
|
|
29
|
-
* place of a sentinel (or an inline definition `{kind, ...config}`
|
|
30
|
-
* reached through a local `$ref` that escapes extraction). Both the
|
|
31
|
-
* kernel and the analyzer validate ref slots *after* sentinel
|
|
32
|
-
* resolution, so this is the shape AJV usually sees.
|
|
33
|
-
*
|
|
34
|
-
* The object-form `{kind, name}` reference a user could once type directly
|
|
35
|
-
* is gone: a plain object at a ref slot is only ever an inline definition
|
|
36
|
-
* or the resolver's own substitution, never an author-written reference.
|
|
37
|
-
* That removal is enforced by the analyzer (it rejects an author-written
|
|
38
|
-
* `{kind, name}` before normalization), not by this schema — branch 2
|
|
39
|
-
* cannot distinguish an author's `{kind, name}` from the resolver's. */
|
|
40
|
-
export declare const ResourceRefSchema: {
|
|
41
|
-
title: string;
|
|
42
|
-
anyOf: ({
|
|
43
|
-
type: string;
|
|
44
|
-
required: string[];
|
|
45
|
-
properties: {
|
|
46
|
-
__tagged: {
|
|
47
|
-
const: boolean;
|
|
48
|
-
};
|
|
49
|
-
engine: {
|
|
50
|
-
const: string;
|
|
51
|
-
};
|
|
52
|
-
source: {
|
|
53
|
-
type: string;
|
|
54
|
-
minLength: number;
|
|
55
|
-
};
|
|
56
|
-
kind?: undefined;
|
|
57
|
-
};
|
|
58
|
-
additionalProperties: boolean;
|
|
59
|
-
} | {
|
|
60
|
-
type: string;
|
|
61
|
-
required: string[];
|
|
62
|
-
properties: {
|
|
63
|
-
kind: {
|
|
64
|
-
type: string;
|
|
65
|
-
};
|
|
66
|
-
__tagged?: undefined;
|
|
67
|
-
engine?: undefined;
|
|
68
|
-
source?: undefined;
|
|
69
|
-
};
|
|
70
|
-
additionalProperties: boolean;
|
|
71
|
-
})[];
|
|
72
|
-
};
|
|
73
|
-
/** Stable URI under which the shared manifest root schema is registered
|
|
74
|
-
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
75
|
-
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
76
|
-
* the symbol names intentionally omit a host-specific prefix since the
|
|
77
|
-
* fragments live in `@telorun/templating` (the only layer both kernel
|
|
78
|
-
* and analyzer depend on). */
|
|
79
|
-
export declare const MANIFEST_SCHEMA_URI = "telo://manifest";
|
|
80
|
-
/** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
|
|
81
|
-
* `$defs` only — it isn't validated against directly. Adding a new
|
|
82
|
-
* shared fragment means putting it under `$defs` here and `$ref`-ing
|
|
83
|
-
* it from module schemas. */
|
|
84
|
-
export declare const ManifestRootSchema: {
|
|
85
|
-
$id: string;
|
|
86
|
-
$defs: {
|
|
87
|
-
ResourceRef: {
|
|
88
|
-
title: string;
|
|
89
|
-
anyOf: ({
|
|
90
|
-
type: string;
|
|
91
|
-
required: string[];
|
|
92
|
-
properties: {
|
|
93
|
-
__tagged: {
|
|
94
|
-
const: boolean;
|
|
95
|
-
};
|
|
96
|
-
engine: {
|
|
97
|
-
const: string;
|
|
98
|
-
};
|
|
99
|
-
source: {
|
|
100
|
-
type: string;
|
|
101
|
-
minLength: number;
|
|
102
|
-
};
|
|
103
|
-
kind?: undefined;
|
|
104
|
-
};
|
|
105
|
-
additionalProperties: boolean;
|
|
106
|
-
} | {
|
|
107
|
-
type: string;
|
|
108
|
-
required: string[];
|
|
109
|
-
properties: {
|
|
110
|
-
kind: {
|
|
111
|
-
type: string;
|
|
112
|
-
};
|
|
113
|
-
__tagged?: undefined;
|
|
114
|
-
engine?: undefined;
|
|
115
|
-
source?: undefined;
|
|
116
|
-
};
|
|
117
|
-
additionalProperties: boolean;
|
|
118
|
-
})[];
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
//# sourceMappingURL=manifest-schemas.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
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 slot. The only form a manifest
|
|
9
|
-
* author writes is the `!ref <name>` (or `!ref <Alias>.<name>`) YAML tag,
|
|
10
|
-
* which parses to a `TaggedSentinel` (engine "ref") whose `source` is the
|
|
11
|
-
* bare resource name. In practice module schemas mark a ref slot with a bare
|
|
12
|
-
* `x-telo-ref` annotation (plus, where the slot only ever holds a reference,
|
|
13
|
-
* `type: object` to reject a stray scalar); the analyzer's reference walker
|
|
14
|
-
* reads `x-telo-ref` to look the name up against that constraint, independent
|
|
15
|
-
* of this fragment. A slot opts into `$ref:
|
|
16
|
-
* "telo://manifest#/$defs/ResourceRef"` only when it wants this exact
|
|
17
|
-
* two-branch shape enforced at the AJV layer too — it is not required, and
|
|
18
|
-
* slots that also accept an inline value (e.g. `inputType` / `outputType`)
|
|
19
|
-
* deliberately do not use it (an inline JSON Schema has no `kind`).
|
|
20
|
-
*
|
|
21
|
-
* Two `anyOf` branches because the value's shape depends on the phase at
|
|
22
|
-
* which it is validated:
|
|
23
|
-
*
|
|
24
|
-
* 1. The raw `!ref` sentinel — what survives to AJV when a cross-module
|
|
25
|
-
* reference can't be resolved in standalone single-file analysis (the
|
|
26
|
-
* imported module isn't loaded). `substituteCelFields` deliberately
|
|
27
|
-
* keeps the sentinel so this branch matches.
|
|
28
|
-
* 2. A resolved reference object — `{kind, name, alias?}` substituted in
|
|
29
|
-
* place of a sentinel (or an inline definition `{kind, ...config}`
|
|
30
|
-
* reached through a local `$ref` that escapes extraction). Both the
|
|
31
|
-
* kernel and the analyzer validate ref slots *after* sentinel
|
|
32
|
-
* resolution, so this is the shape AJV usually sees.
|
|
33
|
-
*
|
|
34
|
-
* The object-form `{kind, name}` reference a user could once type directly
|
|
35
|
-
* is gone: a plain object at a ref slot is only ever an inline definition
|
|
36
|
-
* or the resolver's own substitution, never an author-written reference.
|
|
37
|
-
* That removal is enforced by the analyzer (it rejects an author-written
|
|
38
|
-
* `{kind, name}` before normalization), not by this schema — branch 2
|
|
39
|
-
* cannot distinguish an author's `{kind, name}` from the resolver's. */
|
|
40
|
-
export const ResourceRefSchema = {
|
|
41
|
-
title: "Resource reference",
|
|
42
|
-
anyOf: [
|
|
43
|
-
{
|
|
44
|
-
type: "object",
|
|
45
|
-
required: ["__tagged", "engine", "source"],
|
|
46
|
-
properties: {
|
|
47
|
-
__tagged: { const: true },
|
|
48
|
-
engine: { const: "ref" },
|
|
49
|
-
source: { type: "string", minLength: 1 },
|
|
50
|
-
},
|
|
51
|
-
additionalProperties: false,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
type: "object",
|
|
55
|
-
required: ["kind"],
|
|
56
|
-
properties: { kind: { type: "string" } },
|
|
57
|
-
additionalProperties: true,
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
};
|
|
61
|
-
/** Stable URI under which the shared manifest root schema is registered
|
|
62
|
-
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
63
|
-
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
64
|
-
* the symbol names intentionally omit a host-specific prefix since the
|
|
65
|
-
* fragments live in `@telorun/templating` (the only layer both kernel
|
|
66
|
-
* and analyzer depend on). */
|
|
67
|
-
export const MANIFEST_SCHEMA_URI = "telo://manifest";
|
|
68
|
-
/** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
|
|
69
|
-
* `$defs` only — it isn't validated against directly. Adding a new
|
|
70
|
-
* shared fragment means putting it under `$defs` here and `$ref`-ing
|
|
71
|
-
* it from module schemas. */
|
|
72
|
-
export const ManifestRootSchema = {
|
|
73
|
-
$id: MANIFEST_SCHEMA_URI,
|
|
74
|
-
$defs: {
|
|
75
|
-
ResourceRef: ResourceRefSchema,
|
|
76
|
-
},
|
|
77
|
-
};
|
package/src/manifest-schemas.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
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 slot. The only form a manifest
|
|
10
|
-
* author writes is the `!ref <name>` (or `!ref <Alias>.<name>`) YAML tag,
|
|
11
|
-
* which parses to a `TaggedSentinel` (engine "ref") whose `source` is the
|
|
12
|
-
* bare resource name. In practice module schemas mark a ref slot with a bare
|
|
13
|
-
* `x-telo-ref` annotation (plus, where the slot only ever holds a reference,
|
|
14
|
-
* `type: object` to reject a stray scalar); the analyzer's reference walker
|
|
15
|
-
* reads `x-telo-ref` to look the name up against that constraint, independent
|
|
16
|
-
* of this fragment. A slot opts into `$ref:
|
|
17
|
-
* "telo://manifest#/$defs/ResourceRef"` only when it wants this exact
|
|
18
|
-
* two-branch shape enforced at the AJV layer too — it is not required, and
|
|
19
|
-
* slots that also accept an inline value (e.g. `inputType` / `outputType`)
|
|
20
|
-
* deliberately do not use it (an inline JSON Schema has no `kind`).
|
|
21
|
-
*
|
|
22
|
-
* Two `anyOf` branches because the value's shape depends on the phase at
|
|
23
|
-
* which it is validated:
|
|
24
|
-
*
|
|
25
|
-
* 1. The raw `!ref` sentinel — what survives to AJV when a cross-module
|
|
26
|
-
* reference can't be resolved in standalone single-file analysis (the
|
|
27
|
-
* imported module isn't loaded). `substituteCelFields` deliberately
|
|
28
|
-
* keeps the sentinel so this branch matches.
|
|
29
|
-
* 2. A resolved reference object — `{kind, name, alias?}` substituted in
|
|
30
|
-
* place of a sentinel (or an inline definition `{kind, ...config}`
|
|
31
|
-
* reached through a local `$ref` that escapes extraction). Both the
|
|
32
|
-
* kernel and the analyzer validate ref slots *after* sentinel
|
|
33
|
-
* resolution, so this is the shape AJV usually sees.
|
|
34
|
-
*
|
|
35
|
-
* The object-form `{kind, name}` reference a user could once type directly
|
|
36
|
-
* is gone: a plain object at a ref slot is only ever an inline definition
|
|
37
|
-
* or the resolver's own substitution, never an author-written reference.
|
|
38
|
-
* That removal is enforced by the analyzer (it rejects an author-written
|
|
39
|
-
* `{kind, name}` before normalization), not by this schema — branch 2
|
|
40
|
-
* cannot distinguish an author's `{kind, name}` from the resolver's. */
|
|
41
|
-
export const ResourceRefSchema = {
|
|
42
|
-
title: "Resource reference",
|
|
43
|
-
anyOf: [
|
|
44
|
-
{
|
|
45
|
-
type: "object",
|
|
46
|
-
required: ["__tagged", "engine", "source"],
|
|
47
|
-
properties: {
|
|
48
|
-
__tagged: { const: true },
|
|
49
|
-
engine: { const: "ref" },
|
|
50
|
-
source: { type: "string", minLength: 1 },
|
|
51
|
-
},
|
|
52
|
-
additionalProperties: false,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
type: "object",
|
|
56
|
-
required: ["kind"],
|
|
57
|
-
properties: { kind: { type: "string" } },
|
|
58
|
-
additionalProperties: true,
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/** Stable URI under which the shared manifest root schema is registered
|
|
65
|
-
* with module-side AJV instances. Module YAMLs reach the fragments via
|
|
66
|
-
* `$ref: "telo://manifest#/$defs/<Name>"`. The URI is the contract;
|
|
67
|
-
* the symbol names intentionally omit a host-specific prefix since the
|
|
68
|
-
* fragments live in `@telorun/templating` (the only layer both kernel
|
|
69
|
-
* and analyzer depend on). */
|
|
70
|
-
export const MANIFEST_SCHEMA_URI = "telo://manifest";
|
|
71
|
-
|
|
72
|
-
/** Root schema registered with AJV under `MANIFEST_SCHEMA_URI`. Carries
|
|
73
|
-
* `$defs` only — it isn't validated against directly. Adding a new
|
|
74
|
-
* shared fragment means putting it under `$defs` here and `$ref`-ing
|
|
75
|
-
* it from module schemas. */
|
|
76
|
-
export const ManifestRootSchema = {
|
|
77
|
-
$id: MANIFEST_SCHEMA_URI,
|
|
78
|
-
$defs: {
|
|
79
|
-
ResourceRef: ResourceRefSchema,
|
|
80
|
-
},
|
|
81
|
-
};
|