@telorun/kernel 0.39.0 → 0.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAiC,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAiC,QAAQ,EAAE,MAAM,cAAc,CAAC;AAqItF,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,kBAAkB,CAAwC;IAClE,OAAO,CAAC,QAAQ,CAAqB;IACrC;;;kEAG8D;IAC9D,OAAO,CAAC,aAAa,CAAQ;IAC7B;;;4BAGwB;IACxB,OAAO,CAAC,SAAS,CAAoC;;IAkCrD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAO7C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI;IAInD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,SAAS;IAIlD;;;;;;;;uDAQmD;IACnD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAKtE,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,aAAa;IAyFnC;;;;;;;;;;yDAUqD;IACrD,OAAO,CAAC,sBAAsB;IAoD9B,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa;CAmC1F"}
|
package/dist/schema-validator.js
CHANGED
|
@@ -8,7 +8,7 @@ import * as fs from "node:fs";
|
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import { formatAjvErrors } from "./manifest-schemas.js";
|
|
11
|
-
import { isTaggedSentinel, ManifestRootSchema, normalizeRefSlots } from "@telorun/templating";
|
|
11
|
+
import { EXACT_TEMPLATE_REGEX, isTaggedSentinel, ManifestRootSchema, normalizeRefSlots, } from "@telorun/templating";
|
|
12
12
|
const Ajv = AjvModule.default ?? AjvModule;
|
|
13
13
|
// AJV's standalone subpath is CJS — the default export shows up as either
|
|
14
14
|
// the function itself or `.default` depending on how the bundler/loader
|
|
@@ -76,34 +76,50 @@ function verifyAndExtractBody(text) {
|
|
|
76
76
|
const actual = createHash("sha256").update(body).digest("hex");
|
|
77
77
|
return actual === match[1] ? body : null;
|
|
78
78
|
}
|
|
79
|
-
/** Deep-clone `value
|
|
80
|
-
*
|
|
79
|
+
/** Deep-clone `value`, canonicalizing every CEL/template carrier to the bare
|
|
80
|
+
* source text `compileString` records — applied to the schema *before* both
|
|
81
|
+
* AJV compilation and cache hashing.
|
|
81
82
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* warmed cache (and fails to persist it read-only). Both sentinel shapes carry
|
|
91
|
-
* the same original `source`, so collapsing each to that string converges them.
|
|
83
|
+
* A `Telo.Definition` schema carries inline `${{ }}` templates and `!cel` /
|
|
84
|
+
* `!sql` tags (most commonly inside `description` / `examples`, but the loader
|
|
85
|
+
* rewrites them at any position), and reaches `compile()` in two forms:
|
|
86
|
+
* - the runtime feeds the precompiled tree, where each carrier is a sentinel
|
|
87
|
+
* object (`{__compiled, source, parts}` / `{__tagged, engine, source}`);
|
|
88
|
+
* - the build-time validator warm (`precompileDefinitionSchemas`) feeds the
|
|
89
|
+
* raw analysis graph, where an inline `${{ }}` is still a plain string and a
|
|
90
|
+
* `!cel` tag is a `{__tagged}` sentinel.
|
|
92
91
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
|
|
92
|
+
* AJV meta-validates the schema it compiles and rejects a sentinel object
|
|
93
|
+
* where a keyword (`description`, …) expects a scalar, so the precompiled tree
|
|
94
|
+
* throws "schema is invalid: …/description must be string" on a cache miss.
|
|
95
|
+
* Canonicalizing fixes that *and* converges the two forms onto one cache key,
|
|
96
|
+
* so the runtime hits the warmed `__validators` entry instead of recompiling
|
|
97
|
+
* (and, on a read-only image, failing to persist) every boot:
|
|
98
|
+
* - a sentinel collapses to its `source` (interpolated → full string, exact
|
|
99
|
+
* `${{ expr }}` → bare `expr`, matching `compileString`);
|
|
100
|
+
* - a raw exact-form `"${{ expr }}"` string is reduced to the same bare `expr`,
|
|
101
|
+
* so the warm path's plain string lands on the sentinel's stripped source.
|
|
102
|
+
* Interpolated raw strings already equal the sentinel's full-string source,
|
|
103
|
+
* so they pass through untouched.
|
|
104
|
+
*
|
|
105
|
+
* Canonicalizing never removes a structural node — a property literally named
|
|
106
|
+
* `description` / `examples` keeps its schema — so two genuinely different
|
|
107
|
+
* shapes never collide and the produced validator accepts the same data. A
|
|
108
|
+
* sentinel with no captured `source` collapses to `""` (a valid scalar). */
|
|
109
|
+
function collapseSentinelsToSource(value) {
|
|
98
110
|
if (isCompiledValue(value) || isTaggedSentinel(value)) {
|
|
99
|
-
return typeof value.source === "string" ? value.source :
|
|
111
|
+
return typeof value.source === "string" ? value.source : "";
|
|
112
|
+
}
|
|
113
|
+
if (typeof value === "string") {
|
|
114
|
+
const exact = value.match(EXACT_TEMPLATE_REGEX);
|
|
115
|
+
return exact ? exact[1].trim() : value;
|
|
100
116
|
}
|
|
101
117
|
if (Array.isArray(value))
|
|
102
|
-
return value.map(
|
|
118
|
+
return value.map(collapseSentinelsToSource);
|
|
103
119
|
if (value && typeof value === "object") {
|
|
104
120
|
const out = {};
|
|
105
121
|
for (const [k, v] of Object.entries(value)) {
|
|
106
|
-
out[k] =
|
|
122
|
+
out[k] = collapseSentinelsToSource(v);
|
|
107
123
|
}
|
|
108
124
|
return out;
|
|
109
125
|
}
|
|
@@ -217,10 +233,17 @@ export class SchemaValidator {
|
|
|
217
233
|
// (or an unresolved sentinel) — both objects the stale `type: "string"`
|
|
218
234
|
// would otherwise reject.
|
|
219
235
|
const injected = normalizeRefSlots(withImplicit);
|
|
236
|
+
// Canonicalize CEL/template carriers (an inline `${{ }}` left in a
|
|
237
|
+
// `description`, a `!cel` tag, …) to their bare source text so AJV can
|
|
238
|
+
// meta-validate the schema it compiles, and so the raw (warm-pass) and
|
|
239
|
+
// precompiled (runtime) views of one schema land on the same cache key. The
|
|
240
|
+
// hashed and the compiled schema are this same canonical form. See
|
|
241
|
+
// `collapseSentinelsToSource`.
|
|
242
|
+
const sanitized = collapseSentinelsToSource(injected);
|
|
220
243
|
const hash = createHash("sha256")
|
|
221
244
|
.update(JSON.stringify({
|
|
222
245
|
runtime: VALIDATOR_RUNTIME_TAG,
|
|
223
|
-
schema:
|
|
246
|
+
schema: sanitized,
|
|
224
247
|
}))
|
|
225
248
|
.digest("hex")
|
|
226
249
|
.slice(0, 32);
|
|
@@ -231,7 +254,7 @@ export class SchemaValidator {
|
|
|
231
254
|
}
|
|
232
255
|
return cachedByHash;
|
|
233
256
|
}
|
|
234
|
-
const validate = this.compileAjvOrLoadCached(
|
|
257
|
+
const validate = this.compileAjvOrLoadCached(sanitized, hash);
|
|
235
258
|
const validator = {
|
|
236
259
|
validate: (data) => {
|
|
237
260
|
const isValid = validate(data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAiB,eAAe,EAAE,YAAY,EAAY,MAAM,cAAc,CAAC;AACtF,OAAO,SAAoC,MAAM,KAAK,CAAC;AACvD,OAAO,iBAAiB,MAAM,8BAA8B,CAAC;AAC7D,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/schema-validator.ts
CHANGED
|
@@ -8,7 +8,12 @@ import * as fs from "node:fs";
|
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import { formatAjvErrors } from "./manifest-schemas.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
EXACT_TEMPLATE_REGEX,
|
|
13
|
+
isTaggedSentinel,
|
|
14
|
+
ManifestRootSchema,
|
|
15
|
+
normalizeRefSlots,
|
|
16
|
+
} from "@telorun/templating";
|
|
12
17
|
|
|
13
18
|
const Ajv = AjvModule.default ?? AjvModule;
|
|
14
19
|
// AJV's standalone subpath is CJS — the default export shows up as either
|
|
@@ -78,33 +83,49 @@ function verifyAndExtractBody(text: string): string | null {
|
|
|
78
83
|
return actual === match[1] ? body : null;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
/** Deep-clone `value
|
|
82
|
-
*
|
|
86
|
+
/** Deep-clone `value`, canonicalizing every CEL/template carrier to the bare
|
|
87
|
+
* source text `compileString` records — applied to the schema *before* both
|
|
88
|
+
* AJV compilation and cache hashing.
|
|
83
89
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* warmed cache (and fails to persist it read-only). Both sentinel shapes carry
|
|
93
|
-
* the same original `source`, so collapsing each to that string converges them.
|
|
90
|
+
* A `Telo.Definition` schema carries inline `${{ }}` templates and `!cel` /
|
|
91
|
+
* `!sql` tags (most commonly inside `description` / `examples`, but the loader
|
|
92
|
+
* rewrites them at any position), and reaches `compile()` in two forms:
|
|
93
|
+
* - the runtime feeds the precompiled tree, where each carrier is a sentinel
|
|
94
|
+
* object (`{__compiled, source, parts}` / `{__tagged, engine, source}`);
|
|
95
|
+
* - the build-time validator warm (`precompileDefinitionSchemas`) feeds the
|
|
96
|
+
* raw analysis graph, where an inline `${{ }}` is still a plain string and a
|
|
97
|
+
* `!cel` tag is a `{__tagged}` sentinel.
|
|
94
98
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
|
|
99
|
+
* AJV meta-validates the schema it compiles and rejects a sentinel object
|
|
100
|
+
* where a keyword (`description`, …) expects a scalar, so the precompiled tree
|
|
101
|
+
* throws "schema is invalid: …/description must be string" on a cache miss.
|
|
102
|
+
* Canonicalizing fixes that *and* converges the two forms onto one cache key,
|
|
103
|
+
* so the runtime hits the warmed `__validators` entry instead of recompiling
|
|
104
|
+
* (and, on a read-only image, failing to persist) every boot:
|
|
105
|
+
* - a sentinel collapses to its `source` (interpolated → full string, exact
|
|
106
|
+
* `${{ expr }}` → bare `expr`, matching `compileString`);
|
|
107
|
+
* - a raw exact-form `"${{ expr }}"` string is reduced to the same bare `expr`,
|
|
108
|
+
* so the warm path's plain string lands on the sentinel's stripped source.
|
|
109
|
+
* Interpolated raw strings already equal the sentinel's full-string source,
|
|
110
|
+
* so they pass through untouched.
|
|
111
|
+
*
|
|
112
|
+
* Canonicalizing never removes a structural node — a property literally named
|
|
113
|
+
* `description` / `examples` keeps its schema — so two genuinely different
|
|
114
|
+
* shapes never collide and the produced validator accepts the same data. A
|
|
115
|
+
* sentinel with no captured `source` collapses to `""` (a valid scalar). */
|
|
116
|
+
function collapseSentinelsToSource(value: unknown): unknown {
|
|
100
117
|
if (isCompiledValue(value) || isTaggedSentinel(value)) {
|
|
101
|
-
return typeof value.source === "string" ? value.source :
|
|
118
|
+
return typeof value.source === "string" ? value.source : "";
|
|
119
|
+
}
|
|
120
|
+
if (typeof value === "string") {
|
|
121
|
+
const exact = value.match(EXACT_TEMPLATE_REGEX);
|
|
122
|
+
return exact ? exact[1].trim() : value;
|
|
102
123
|
}
|
|
103
|
-
if (Array.isArray(value)) return value.map(
|
|
124
|
+
if (Array.isArray(value)) return value.map(collapseSentinelsToSource);
|
|
104
125
|
if (value && typeof value === "object") {
|
|
105
126
|
const out: Record<string, unknown> = {};
|
|
106
127
|
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
107
|
-
out[k] =
|
|
128
|
+
out[k] = collapseSentinelsToSource(v);
|
|
108
129
|
}
|
|
109
130
|
return out;
|
|
110
131
|
}
|
|
@@ -232,11 +253,19 @@ export class SchemaValidator {
|
|
|
232
253
|
// would otherwise reject.
|
|
233
254
|
const injected = normalizeRefSlots(withImplicit) as typeof withImplicit;
|
|
234
255
|
|
|
256
|
+
// Canonicalize CEL/template carriers (an inline `${{ }}` left in a
|
|
257
|
+
// `description`, a `!cel` tag, …) to their bare source text so AJV can
|
|
258
|
+
// meta-validate the schema it compiles, and so the raw (warm-pass) and
|
|
259
|
+
// precompiled (runtime) views of one schema land on the same cache key. The
|
|
260
|
+
// hashed and the compiled schema are this same canonical form. See
|
|
261
|
+
// `collapseSentinelsToSource`.
|
|
262
|
+
const sanitized = collapseSentinelsToSource(injected);
|
|
263
|
+
|
|
235
264
|
const hash = createHash("sha256")
|
|
236
265
|
.update(
|
|
237
266
|
JSON.stringify({
|
|
238
267
|
runtime: VALIDATOR_RUNTIME_TAG,
|
|
239
|
-
schema:
|
|
268
|
+
schema: sanitized,
|
|
240
269
|
}),
|
|
241
270
|
)
|
|
242
271
|
.digest("hex")
|
|
@@ -249,7 +278,7 @@ export class SchemaValidator {
|
|
|
249
278
|
return cachedByHash;
|
|
250
279
|
}
|
|
251
280
|
|
|
252
|
-
const validate = this.compileAjvOrLoadCached(
|
|
281
|
+
const validate = this.compileAjvOrLoadCached(sanitized, hash);
|
|
253
282
|
|
|
254
283
|
const validator = {
|
|
255
284
|
validate: (data: any) => {
|