@telorun/analyzer 0.73.0 → 0.74.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/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +26 -7
- package/dist/artifact-axes.d.ts +14 -0
- package/dist/artifact-axes.d.ts.map +1 -0
- package/dist/artifact-axes.js +12 -0
- package/dist/artifact-layer-index.d.ts +2 -2
- package/dist/artifact-layer-index.d.ts.map +1 -1
- package/dist/artifact-layer-index.js +9 -4
- package/dist/artifact-selector.d.ts +47 -21
- package/dist/artifact-selector.d.ts.map +1 -1
- package/dist/artifact-selector.js +85 -16
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +73 -16
- package/dist/call-graph.d.ts +12 -1
- package/dist/call-graph.d.ts.map +1 -1
- package/dist/call-graph.js +22 -0
- package/dist/catch-scope.js +1 -1
- package/dist/forwarded-declaration.d.ts +26 -0
- package/dist/forwarded-declaration.d.ts.map +1 -0
- package/dist/forwarded-declaration.js +31 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +21 -5
- package/dist/module-alias-scope.d.ts +11 -0
- package/dist/module-alias-scope.d.ts.map +1 -1
- package/dist/module-alias-scope.js +10 -0
- package/dist/module-file-claims.d.ts +17 -1
- package/dist/module-file-claims.d.ts.map +1 -1
- package/dist/module-file-claims.js +38 -3
- package/dist/module-named-files.d.ts +72 -0
- package/dist/module-named-files.d.ts.map +1 -0
- package/dist/module-named-files.js +115 -0
- package/dist/native-entries.d.ts +83 -0
- package/dist/native-entries.d.ts.map +1 -0
- package/dist/native-entries.js +169 -0
- package/dist/normalize-inline-resources.d.ts +41 -13
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +236 -54
- package/dist/reference-field-map.d.ts +59 -1
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +200 -34
- package/dist/release/payload-digest.d.ts +2 -1
- package/dist/release/payload-digest.d.ts.map +1 -1
- package/dist/release/payload-digest.js +2 -1
- package/dist/resolve-ref-sentinels.d.ts.map +1 -1
- package/dist/resolve-ref-sentinels.js +8 -5
- package/dist/resolve-throws-union.d.ts +25 -4
- package/dist/resolve-throws-union.d.ts.map +1 -1
- package/dist/resolve-throws-union.js +62 -32
- package/dist/resolve-zone-requirements.d.ts.map +1 -1
- package/dist/resolve-zone-requirements.js +2 -1
- package/dist/rewrite-synthetic-origins.d.ts +6 -1
- package/dist/rewrite-synthetic-origins.d.ts.map +1 -1
- package/dist/rewrite-synthetic-origins.js +107 -13
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +18 -5
- package/dist/schema-walk.d.ts +47 -13
- package/dist/schema-walk.d.ts.map +1 -1
- package/dist/schema-walk.js +150 -30
- package/dist/scope-declarations.d.ts +59 -0
- package/dist/scope-declarations.d.ts.map +1 -0
- package/dist/scope-declarations.js +45 -0
- package/dist/source-entries.d.ts +119 -0
- package/dist/source-entries.d.ts.map +1 -0
- package/dist/source-entries.js +349 -0
- package/dist/step-bodies.d.ts +24 -0
- package/dist/step-bodies.d.ts.map +1 -0
- package/dist/step-bodies.js +28 -0
- package/dist/telo-version.d.ts +1 -1
- package/dist/telo-version.js +1 -1
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +3 -2
- package/dist/validate-module-artifact.d.ts +4 -2
- package/dist/validate-module-artifact.d.ts.map +1 -1
- package/dist/validate-module-artifact.js +43 -4
- package/dist/validate-native-entries.d.ts +15 -0
- package/dist/validate-native-entries.d.ts.map +1 -0
- package/dist/validate-native-entries.js +103 -0
- package/dist/validate-observed-state.d.ts +7 -1
- package/dist/validate-observed-state.d.ts.map +1 -1
- package/dist/validate-observed-state.js +24 -6
- package/dist/validate-reference-forms.d.ts.map +1 -1
- package/dist/validate-reference-forms.js +2 -2
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +11 -5
- package/dist/validate-scope-reach.d.ts +31 -0
- package/dist/validate-scope-reach.d.ts.map +1 -0
- package/dist/validate-scope-reach.js +180 -0
- package/dist/validate-source-entries.d.ts +16 -0
- package/dist/validate-source-entries.d.ts.map +1 -0
- package/dist/validate-source-entries.js +90 -0
- package/dist/validate-throws-coverage.d.ts.map +1 -1
- package/dist/validate-throws-coverage.js +21 -39
- package/dist/value-type-keyword.d.ts +22 -5
- package/dist/value-type-keyword.d.ts.map +1 -1
- package/dist/value-type-keyword.js +16 -7
- package/package.json +3 -3
- package/src/analyzer.ts +33 -9
- package/src/artifact-axes.ts +24 -0
- package/src/artifact-layer-index.ts +9 -6
- package/src/artifact-selector.ts +110 -35
- package/src/builtins.ts +75 -16
- package/src/call-graph.ts +30 -0
- package/src/catch-scope.ts +1 -1
- package/src/forwarded-declaration.ts +32 -0
- package/src/index.ts +36 -1
- package/src/kernel-globals.ts +28 -5
- package/src/migrations/CLAUDE.md +35 -0
- package/src/module-alias-scope.ts +17 -0
- package/src/module-file-claims.ts +63 -4
- package/src/module-named-files.ts +162 -0
- package/src/native-entries.ts +227 -0
- package/src/normalize-inline-resources.ts +326 -70
- package/src/reference-field-map.ts +257 -52
- package/src/release/CLAUDE.md +32 -0
- package/src/release/payload-digest.ts +2 -1
- package/src/resolve-ref-sentinels.ts +7 -6
- package/src/resolve-throws-union.ts +87 -32
- package/src/resolve-zone-requirements.ts +2 -1
- package/src/rewrite-synthetic-origins.ts +107 -16
- package/src/schema-compat.ts +18 -4
- package/src/schema-walk.ts +162 -34
- package/src/scope-declarations.ts +98 -0
- package/src/source-entries.ts +502 -0
- package/src/step-bodies.ts +55 -0
- package/src/telo-version.ts +1 -1
- package/src/validate-cel-context.ts +3 -2
- package/src/validate-module-artifact.ts +46 -3
- package/src/validate-native-entries.ts +147 -0
- package/src/validate-observed-state.ts +36 -7
- package/src/validate-reference-forms.ts +2 -3
- package/src/validate-references.ts +10 -6
- package/src/validate-scope-reach.ts +213 -0
- package/src/validate-source-entries.ts +120 -0
- package/src/validate-throws-coverage.ts +24 -31
- package/src/value-type-keyword.ts +35 -9
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
|
|
3
|
+
import { moduleDocumentClaims } from "./module-file-claims.js";
|
|
4
|
+
import {
|
|
5
|
+
crossLayerSourceLinks,
|
|
6
|
+
stageableFiles,
|
|
7
|
+
unclaimedSourceEntries,
|
|
8
|
+
} from "./module-named-files.js";
|
|
9
|
+
import { normalizeNativePath, readNativeEntries } from "./native-entries.js";
|
|
10
|
+
import { readModuleSources } from "./source-entries.js";
|
|
11
|
+
import { DiagnosticSeverity, type AnalysisDiagnostic } from "./types.js";
|
|
12
|
+
|
|
13
|
+
const SOURCE = "telo-analyzer";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The strict half of `source-entries.ts`: what `telo check` reports about a
|
|
17
|
+
* module doc's `sources:` block.
|
|
18
|
+
*
|
|
19
|
+
* Every rule the block alone decides is a reader problem, reported here under
|
|
20
|
+
* its own code. This adds the rules that need the rest of the module or that
|
|
21
|
+
* only publish enforces: an entry must stage a file the manifest names, a link
|
|
22
|
+
* ships in its target's layer, and every file entry and every build carries its
|
|
23
|
+
* pin. Shape errors are left to the owner doc's JSON Schema.
|
|
24
|
+
*
|
|
25
|
+
* Entry-module-scoped: a dependency's block is not the consumer's to fix.
|
|
26
|
+
*/
|
|
27
|
+
export function validateSourceEntries(
|
|
28
|
+
manifests: ResourceManifest[],
|
|
29
|
+
entryModules?: ReadonlySet<string>,
|
|
30
|
+
): AnalysisDiagnostic[] {
|
|
31
|
+
const out: AnalysisDiagnostic[] = [];
|
|
32
|
+
for (const manifest of manifests) {
|
|
33
|
+
if (manifest.kind !== "Telo.Application" && manifest.kind !== "Telo.Library") continue;
|
|
34
|
+
const metadata = (manifest.metadata ?? {}) as Record<string, unknown>;
|
|
35
|
+
const name = typeof metadata.name === "string" ? metadata.name : undefined;
|
|
36
|
+
const owned =
|
|
37
|
+
entryModules === undefined ||
|
|
38
|
+
(typeof metadata.module === "string"
|
|
39
|
+
? entryModules.has(metadata.module)
|
|
40
|
+
: name === undefined || entryModules.has(name));
|
|
41
|
+
if (!owned) continue;
|
|
42
|
+
|
|
43
|
+
const label = `${manifest.kind}/${name ?? "(unnamed)"}`;
|
|
44
|
+
const report = (code: string, path: string, message: string) =>
|
|
45
|
+
out.push({
|
|
46
|
+
severity: DiagnosticSeverity.Error,
|
|
47
|
+
code,
|
|
48
|
+
source: SOURCE,
|
|
49
|
+
message: `${label}: ${message}`,
|
|
50
|
+
data: {
|
|
51
|
+
resource: { kind: manifest.kind, name },
|
|
52
|
+
filePath: typeof metadata.source === "string" ? metadata.source : undefined,
|
|
53
|
+
path,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const { sources, problems } = readModuleSources(manifest);
|
|
58
|
+
for (const problem of problems) {
|
|
59
|
+
if (problem.code === "SHAPE") continue;
|
|
60
|
+
report(problem.code, problem.path, problem.message);
|
|
61
|
+
}
|
|
62
|
+
if (sources.length === 0) continue;
|
|
63
|
+
|
|
64
|
+
for (const source of sources) {
|
|
65
|
+
if (source.build && source.build.inputs === undefined) {
|
|
66
|
+
report(
|
|
67
|
+
"SOURCE_BUILD_UNPINNED",
|
|
68
|
+
`sources.${source.name}.build`,
|
|
69
|
+
`source '${source.name}' names the crate its files are built from but records no build ` +
|
|
70
|
+
`inputs, so a later edit to that crate cannot be caught — run \`telo release stage ` +
|
|
71
|
+
`--pin\` after building its files. \`telo release check\` and publish refuse it.`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
for (const entry of source.entries) {
|
|
75
|
+
if (entry.kind !== "file" || entry.pin) continue;
|
|
76
|
+
report(
|
|
77
|
+
"SOURCE_ENTRY_UNPINNED",
|
|
78
|
+
`sources.${source.name}.entries.${entry.key}`,
|
|
79
|
+
`source '${source.name}' entry '${entry.key}' carries no pin (sha256 and executable), so ` +
|
|
80
|
+
`nothing can verify the file it stages: a kernel refuses to read it and publish refuses ` +
|
|
81
|
+
`to ship it — run \`telo release stage --pin\`.`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const moduleName = typeof metadata.module === "string" ? metadata.module : name;
|
|
87
|
+
const stageable = stageableFiles(
|
|
88
|
+
readNativeEntries(manifest).entries,
|
|
89
|
+
moduleDocumentClaims(manifests, moduleName),
|
|
90
|
+
);
|
|
91
|
+
for (const { source, entry, layer, targetLayer } of crossLayerSourceLinks(sources, stageable)) {
|
|
92
|
+
report(
|
|
93
|
+
"SOURCE_LINK_TARGET_UNRESOLVED",
|
|
94
|
+
`sources.${source.name}.entries.${entry.key}.target`,
|
|
95
|
+
`source '${source.name}' entry '${entry.key}': the link ships in the ${layer} layer, but ` +
|
|
96
|
+
`its target '${entry.resolved}' ships in the ${targetLayer} layer. A runtime extracts ` +
|
|
97
|
+
`only the layers it needs, so the link would dangle — point it at a file of its own layer.`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
// A `native:` entry with an unrelated problem still names its path; reported
|
|
101
|
+
// once, as that problem, rather than again as an unclaimed source entry.
|
|
102
|
+
const unreadNative = new Set<string>();
|
|
103
|
+
const native = (manifest as { native?: unknown }).native;
|
|
104
|
+
for (const entry of Array.isArray(native) ? native : []) {
|
|
105
|
+
const path = (entry as { path?: unknown } | null)?.path;
|
|
106
|
+
const verdict = typeof path === "string" ? normalizeNativePath(path.trim()) : undefined;
|
|
107
|
+
if (verdict && "path" in verdict) unreadNative.add(verdict.path);
|
|
108
|
+
}
|
|
109
|
+
for (const { source, entry } of unclaimedSourceEntries(sources, stageable, unreadNative)) {
|
|
110
|
+
report(
|
|
111
|
+
"SOURCE_ENTRY_UNCLAIMED",
|
|
112
|
+
`sources.${source.name}.entries.${entry.key}`,
|
|
113
|
+
`source '${source.name}' entry '${entry.key}': nothing in the module names '${entry.path}' ` +
|
|
114
|
+
`— no native: entry's path, no platform-qualified controller candidate's path=, and ` +
|
|
115
|
+
`none of the source's notices. Name the file where it is used, or remove the entry.`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
@@ -10,11 +10,14 @@ import { scopeResolverForModule, type AliasResolver } from "./alias-resolver.js"
|
|
|
10
10
|
import type { DefinitionRegistry } from "./definition-registry.js";
|
|
11
11
|
import {
|
|
12
12
|
createResolveCtx,
|
|
13
|
+
handsFailureBack,
|
|
13
14
|
resolveScopeUnion,
|
|
14
15
|
resolveThrowsUnion,
|
|
16
|
+
throwsUses,
|
|
15
17
|
type ThrowsCodeMeta,
|
|
16
18
|
type ThrowsUnion,
|
|
17
19
|
} from "./resolve-throws-union.js";
|
|
20
|
+
import { forEachDeclaredSlot } from "./schema-walk.js";
|
|
18
21
|
import {
|
|
19
22
|
buildEnclosers,
|
|
20
23
|
collectScopedManifests,
|
|
@@ -24,7 +27,6 @@ import {
|
|
|
24
27
|
} from "./catch-scope.js";
|
|
25
28
|
import { DiagnosticSeverity, type AnalysisDiagnostic } from "./types.js";
|
|
26
29
|
import { extractAccessChains, validateChainAgainstSchema } from "./validate-cel-context.js";
|
|
27
|
-
import { isStepSlot } from "./step-slot.js";
|
|
28
30
|
|
|
29
31
|
const SOURCE = "telo-analyzer";
|
|
30
32
|
const TEMPLATE_REGEX = /\$\{\{\s*([^}]+?)\s*\}\}/g;
|
|
@@ -507,12 +509,6 @@ function checkCelChainAgainstDataSchema(
|
|
|
507
509
|
return diagnostics;
|
|
508
510
|
}
|
|
509
511
|
|
|
510
|
-
/** Rule 8 extension: `inherit: true` only makes sense on a definition whose
|
|
511
|
-
* schema declares at least one STEP BODY — an array whose items point at the
|
|
512
|
-
* shared grammar, or, for a module published before that fragment existed, one
|
|
513
|
-
* carrying the legacy `x-telo-step-context` annotation. That is what drives the
|
|
514
|
-
* resolver's generic step traversal; a definition with `inherit: true` and no
|
|
515
|
-
* such array has no invocables to inherit from. */
|
|
516
512
|
/** The capabilities whose lifecycle includes a dispatch a caller can catch. On
|
|
517
513
|
* every other one a thrown error is a boot-time failure, not a structured
|
|
518
514
|
* runtime error for a downstream caller — a provider resolves configuration, a
|
|
@@ -574,10 +570,14 @@ function validateThrowsDeclarations(manifests: ResourceManifest[]): AnalysisDiag
|
|
|
574
570
|
code: "INHERIT_WITHOUT_STEP_CONTEXT",
|
|
575
571
|
source: SOURCE,
|
|
576
572
|
message:
|
|
577
|
-
`Telo.Definition '${name}' declares throws.inherit: true but its schema
|
|
578
|
-
`
|
|
579
|
-
`
|
|
580
|
-
`x-telo-step-context
|
|
573
|
+
`Telo.Definition '${name}' declares throws.inherit: true but its schema dispatches nothing ` +
|
|
574
|
+
`a failure can come back through. inherit makes the kind's union the union of what it ` +
|
|
575
|
+
`dispatches, so the schema needs a step body (an array field with ` +
|
|
576
|
+
`'items: { $ref: "telo://manifest#/$defs/Step" }', or the legacy x-telo-step-context ` +
|
|
577
|
+
`annotation) or a reference slot whose use includes 'call' or 'trigger.consumer' ` +
|
|
578
|
+
`(for a use case map, in any case; a slot declaring no use counts as 'call'). ` +
|
|
579
|
+
`'detached' and 'trigger.inbound' run where no caller awaits them, and ` +
|
|
580
|
+
`'dependency' / 'schema' dispatch nothing.`,
|
|
581
581
|
data: { resource: { kind: m.kind, name }, filePath, path: "throws.inherit" },
|
|
582
582
|
});
|
|
583
583
|
}
|
|
@@ -586,28 +586,21 @@ function validateThrowsDeclarations(manifests: ResourceManifest[]): AnalysisDiag
|
|
|
586
586
|
return diagnostics;
|
|
587
587
|
}
|
|
588
588
|
|
|
589
|
+
/** Rule 8 extension: `inherit: true` needs something to inherit from — a step
|
|
590
|
+
* body, or a reference slot whose use (any case of a case map) hands a failure
|
|
591
|
+
* back. The schema-only mode of the walk the resolver runs per instance, so the
|
|
592
|
+
* check and the union reach the same slots. */
|
|
589
593
|
function schemaDrivesInvocables(schema: Record<string, any> | undefined): boolean {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
594
|
+
let drives = false;
|
|
595
|
+
forEachDeclaredSlot(schema, (declared) => {
|
|
596
|
+
if (
|
|
597
|
+
declared.kind === "step" ||
|
|
598
|
+
declared.slots.some((slot) => throwsUses(slot).some(handsFailureBack))
|
|
599
|
+
) {
|
|
600
|
+
drives = true;
|
|
596
601
|
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
for (const key of ["oneOf", "anyOf", "allOf"] as const) {
|
|
600
|
-
const arr = schema[key];
|
|
601
|
-
if (Array.isArray(arr)) {
|
|
602
|
-
for (const sub of arr) if (schemaDrivesInvocables(sub)) return true;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
if (schema.$defs && typeof schema.$defs === "object") {
|
|
606
|
-
for (const v of Object.values(schema.$defs as Record<string, any>)) {
|
|
607
|
-
if (schemaDrivesInvocables(v)) return true;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
return false;
|
|
602
|
+
});
|
|
603
|
+
return drives;
|
|
611
604
|
}
|
|
612
605
|
|
|
613
606
|
/** Entry point — invoked once per analyze() run. */
|
|
@@ -76,6 +76,21 @@ export const ANNOTATION_KEYWORDS = [
|
|
|
76
76
|
"x-telo-widget",
|
|
77
77
|
] as const;
|
|
78
78
|
|
|
79
|
+
/** How an AJV instance treats a `live` value type. */
|
|
80
|
+
export interface TeloKeywordOptions {
|
|
81
|
+
/**
|
|
82
|
+
* Assert a `live` type as an instance too, so a literal at such a slot is
|
|
83
|
+
* refused — a value written in a manifest can never be a live instance.
|
|
84
|
+
*
|
|
85
|
+
* STATIC analysis only, where a CEL leaf's stand-in is an instance
|
|
86
|
+
* (`celPlaceholderForSchema`). The kernel's instances leave it off: at
|
|
87
|
+
* dispatch a live value is exempt from validation, and the stage that pulls
|
|
88
|
+
* from it refuses a non-stream itself — so the static half refuses a strict
|
|
89
|
+
* subset of what the runtime refuses, and the two cannot disagree.
|
|
90
|
+
*/
|
|
91
|
+
readonly assertLive?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
79
94
|
/**
|
|
80
95
|
* The `x-telo-type` keyword.
|
|
81
96
|
*
|
|
@@ -84,8 +99,10 @@ export const ANNOTATION_KEYWORDS = [
|
|
|
84
99
|
* - a `json` representation validates through its own declared schema, so the
|
|
85
100
|
* keyword emits nothing — the name carries nominal identity for static wiring
|
|
86
101
|
* and has no runtime existence at all;
|
|
87
|
-
* - a `live` instance is EXEMPT: its value is never traversed,
|
|
88
|
-
* a stream to check it is precisely what the exemption is
|
|
102
|
+
* - a `live` instance is EXEMPT at dispatch: its value is never traversed,
|
|
103
|
+
* because iterating a stream to check it is precisely what the exemption is
|
|
104
|
+
* for. Statically ({@link TeloKeywordOptions.assertLive}) it is asserted like
|
|
105
|
+
* any other instance, which checks what the value IS and never iterates it;
|
|
89
106
|
* - every other instance is ASSERTED against the constructor its binding names.
|
|
90
107
|
*
|
|
91
108
|
* An unknown name emits nothing here. It is a hard diagnostic in the analyzer
|
|
@@ -93,7 +110,7 @@ export const ANNOTATION_KEYWORDS = [
|
|
|
93
110
|
* manifest that wrote it; failing compilation instead would take out every
|
|
94
111
|
* validator in a module for one typo in one slot.
|
|
95
112
|
*/
|
|
96
|
-
export function valueTypeKeyword(): KeywordDefinition {
|
|
113
|
+
export function valueTypeKeyword(options: TeloKeywordOptions = {}): KeywordDefinition {
|
|
97
114
|
return {
|
|
98
115
|
keyword: X_TELO_TYPE,
|
|
99
116
|
// Both spellings: a bare name, or the object form carrying type arguments.
|
|
@@ -102,7 +119,8 @@ export function valueTypeKeyword(): KeywordDefinition {
|
|
|
102
119
|
const entry: ValueTypeEntry | undefined = readValueTypeSlot({
|
|
103
120
|
[X_TELO_TYPE]: cxt.schema,
|
|
104
121
|
})?.entry;
|
|
105
|
-
if (!entry || entry.representation !== "instance"
|
|
122
|
+
if (!entry || entry.representation !== "instance") return;
|
|
123
|
+
if (entry.live && !options.assertLive) return;
|
|
106
124
|
const binding = VALUE_TYPE_BINDINGS[entry.binding!];
|
|
107
125
|
if (!binding) return;
|
|
108
126
|
// The constructor reaches generated code through AJV's value scope, which
|
|
@@ -118,6 +136,12 @@ export function valueTypeKeyword(): KeywordDefinition {
|
|
|
118
136
|
error: {
|
|
119
137
|
message: (cxt: any) => {
|
|
120
138
|
const entry = readValueTypeSlot({ [X_TELO_TYPE]: cxt.schema })?.entry;
|
|
139
|
+
if (entry?.live) {
|
|
140
|
+
return (
|
|
141
|
+
`must be a live ${entry.name} — a value written in the manifest can never be one; ` +
|
|
142
|
+
"pass the result of a step that produces it, with a !cel expression"
|
|
143
|
+
);
|
|
144
|
+
}
|
|
121
145
|
return entry?.binding === "bytes"
|
|
122
146
|
? "must be raw bytes (a Uint8Array) — bytes cannot be written inline in a manifest"
|
|
123
147
|
: `must be a ${entry?.name ?? "declared value type"} — this value is not writable inline in a manifest`;
|
|
@@ -131,11 +155,13 @@ export function valueTypeKeyword(): KeywordDefinition {
|
|
|
131
155
|
* `x-telo-type` as the one that checks.
|
|
132
156
|
*
|
|
133
157
|
* Every AJV instance in the runtime and the analyzer goes through this, so a
|
|
134
|
-
* schema means the same thing wherever it is validated
|
|
158
|
+
* schema means the same thing wherever it is validated — apart from the one
|
|
159
|
+
* posture {@link TeloKeywordOptions} names, which only static analysis takes.
|
|
135
160
|
*/
|
|
136
|
-
export function registerTeloKeywords(
|
|
137
|
-
addKeyword: (keyword: any, definition?: any) => unknown
|
|
138
|
-
|
|
161
|
+
export function registerTeloKeywords(
|
|
162
|
+
ajv: { addKeyword: (keyword: any, definition?: any) => unknown },
|
|
163
|
+
options: TeloKeywordOptions = {},
|
|
164
|
+
): void {
|
|
139
165
|
for (const keyword of ANNOTATION_KEYWORDS) ajv.addKeyword(keyword);
|
|
140
|
-
ajv.addKeyword(valueTypeKeyword());
|
|
166
|
+
ajv.addKeyword(valueTypeKeyword(options));
|
|
141
167
|
}
|