@telorun/analyzer 0.62.0 → 0.63.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/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +6 -0
- package/dist/analyzer.d.ts +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +343 -13
- package/dist/call-graph.d.ts.map +1 -1
- package/dist/call-graph.js +2 -7
- package/dist/flatten-for-analyzer.js +1 -1
- package/dist/import-resolution-diagnostics.d.ts +9 -2
- package/dist/import-resolution-diagnostics.d.ts.map +1 -1
- package/dist/import-resolution-diagnostics.js +26 -7
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/invocation-contract.d.ts +6 -1
- package/dist/invocation-contract.d.ts.map +1 -1
- package/dist/invocation-contract.js +27 -4
- package/dist/loaded-types.d.ts +12 -3
- package/dist/loaded-types.d.ts.map +1 -1
- package/dist/manifest-loader.d.ts +0 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +62 -41
- package/dist/manifest-schemas.d.ts +699 -0
- package/dist/manifest-schemas.d.ts.map +1 -1
- package/dist/manifest-schemas.js +306 -14
- package/dist/module-compatibility.d.ts +40 -0
- package/dist/module-compatibility.d.ts.map +1 -0
- package/dist/module-compatibility.js +52 -0
- package/dist/ref-slot-reading.d.ts +30 -0
- package/dist/ref-slot-reading.d.ts.map +1 -0
- package/dist/ref-slot-reading.js +139 -0
- package/dist/referrer-rule.d.ts +67 -0
- package/dist/referrer-rule.d.ts.map +1 -0
- package/dist/referrer-rule.js +96 -0
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -1
- package/dist/release/payload-digest.d.ts +7 -3
- package/dist/release/payload-digest.d.ts.map +1 -1
- package/dist/release/payload-digest.js +7 -3
- package/dist/release/version-stamp.d.ts +16 -0
- package/dist/release/version-stamp.d.ts.map +1 -1
- package/dist/release/version-stamp.js +52 -0
- package/dist/release/workspace-config.d.ts +10 -6
- package/dist/release/workspace-config.d.ts.map +1 -1
- package/dist/release/workspace-config.js +10 -6
- package/dist/resolve-schema-ref-kinds.d.ts +4 -3
- package/dist/resolve-schema-ref-kinds.d.ts.map +1 -1
- package/dist/resolve-schema-ref-kinds.js +16 -0
- package/dist/resolve-throws-union.d.ts.map +1 -1
- package/dist/resolve-throws-union.js +4 -3
- package/dist/resolve-zone-containment.d.ts +99 -0
- package/dist/resolve-zone-containment.d.ts.map +1 -0
- package/dist/resolve-zone-containment.js +212 -0
- package/dist/resolve-zone-requirements.d.ts +5 -0
- package/dist/resolve-zone-requirements.d.ts.map +1 -1
- package/dist/resolve-zone-requirements.js +48 -7
- package/dist/resource-rule.d.ts +106 -0
- package/dist/resource-rule.d.ts.map +1 -0
- package/dist/resource-rule.js +236 -0
- package/dist/rule-condition.d.ts +50 -0
- package/dist/rule-condition.d.ts.map +1 -0
- package/dist/rule-condition.js +108 -0
- package/dist/schema-keywords.d.ts.map +1 -1
- package/dist/schema-keywords.js +20 -0
- package/dist/schema-projection.d.ts +171 -0
- package/dist/schema-projection.d.ts.map +1 -0
- package/dist/schema-projection.js +317 -0
- package/dist/step-slot.d.ts +39 -0
- package/dist/step-slot.d.ts.map +1 -0
- package/dist/step-slot.js +49 -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 +17 -3
- package/dist/validate-durable-regions.d.ts +15 -0
- package/dist/validate-durable-regions.d.ts.map +1 -0
- package/dist/validate-durable-regions.js +336 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +44 -2
- package/dist/validate-referrer-rules.d.ts +120 -0
- package/dist/validate-referrer-rules.d.ts.map +1 -0
- package/dist/validate-referrer-rules.js +255 -0
- package/dist/validate-resource-rules.d.ts +114 -0
- package/dist/validate-resource-rules.d.ts.map +1 -0
- package/dist/validate-resource-rules.js +283 -0
- package/dist/validate-schema-projection.d.ts +32 -0
- package/dist/validate-schema-projection.d.ts.map +1 -0
- package/dist/validate-schema-projection.js +110 -0
- package/dist/validate-step-inputs.d.ts +2 -1
- package/dist/validate-step-inputs.d.ts.map +1 -1
- package/dist/validate-step-inputs.js +5 -3
- package/dist/validate-throws-coverage.d.ts.map +1 -1
- package/dist/validate-throws-coverage.js +11 -5
- package/dist/validate-zone-slots.d.ts +2 -2
- package/dist/validate-zone-slots.d.ts.map +1 -1
- package/dist/validate-zone-slots.js +202 -6
- package/dist/validate-zone-violations.d.ts +12 -0
- package/dist/validate-zone-violations.d.ts.map +1 -0
- package/dist/validate-zone-violations.js +169 -0
- package/dist/value-type-keyword.d.ts +1 -1
- package/dist/value-type-keyword.d.ts.map +1 -1
- package/dist/value-type-keyword.js +4 -0
- package/dist/zone-slot.d.ts +61 -0
- package/dist/zone-slot.d.ts.map +1 -1
- package/dist/zone-slot.js +86 -5
- package/package.json +2 -2
- package/src/analysis-registry.ts +6 -0
- package/src/analyzer.ts +395 -14
- package/src/call-graph.ts +2 -8
- package/src/flatten-for-analyzer.ts +1 -1
- package/src/import-resolution-diagnostics.ts +25 -7
- package/src/index.ts +73 -0
- package/src/invocation-contract.ts +45 -2
- package/src/loaded-types.ts +12 -3
- package/src/manifest-loader.ts +87 -55
- package/src/manifest-schemas.ts +328 -14
- package/src/module-compatibility.ts +72 -0
- package/src/ref-slot-reading.ts +156 -0
- package/src/referrer-rule.ts +116 -0
- package/src/release/index.ts +1 -0
- package/src/release/payload-digest.ts +7 -3
- package/src/release/version-stamp.ts +58 -0
- package/src/release/workspace-config.ts +10 -6
- package/src/resolve-schema-ref-kinds.ts +28 -4
- package/src/resolve-throws-union.ts +4 -3
- package/src/resolve-zone-containment.ts +329 -0
- package/src/resolve-zone-requirements.ts +67 -7
- package/src/resource-rule.ts +252 -0
- package/src/rule-condition.ts +126 -0
- package/src/schema-keywords.ts +24 -0
- package/src/schema-projection.ts +403 -0
- package/src/step-slot.ts +64 -0
- package/src/telo-version.ts +1 -1
- package/src/validate-cel-context.ts +17 -3
- package/src/validate-durable-regions.ts +401 -0
- package/src/validate-references.ts +57 -2
- package/src/validate-referrer-rules.ts +373 -0
- package/src/validate-resource-rules.ts +401 -0
- package/src/validate-schema-projection.ts +184 -0
- package/src/validate-step-inputs.ts +5 -3
- package/src/validate-throws-coverage.ts +12 -5
- package/src/validate-zone-slots.ts +192 -8
- package/src/validate-zone-violations.ts +190 -0
- package/src/value-type-keyword.ts +4 -0
- package/src/zone-slot.ts +114 -5
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The schema-projection annotations' single reader — the `ref-slot.ts` /
|
|
3
|
+
* `zone-slot.ts` precedent.
|
|
4
|
+
*
|
|
5
|
+
* A kind whose configuration is a COLLECTION OF TYPED ENTRIES can say what that
|
|
6
|
+
* collection means as a JSON Schema object, so a consumer can type the values
|
|
7
|
+
* it will read without the analyzer learning anything about the domain. A SQL
|
|
8
|
+
* table's columns are the first consumer; nothing in either annotation says
|
|
9
|
+
* SQL, column or table.
|
|
10
|
+
*
|
|
11
|
+
* Two halves, because the two facts have different owners:
|
|
12
|
+
*
|
|
13
|
+
* - `x-telo-schema-map`, on the field a projection keys on, gives the schema
|
|
14
|
+
* node each of its values means (`citext → {type: string}`). It sits with the
|
|
15
|
+
* field because that is where the value vocabulary is declared.
|
|
16
|
+
* - `x-telo-schema-projection`, on the KIND DOCUMENT (a sibling of `schema:`,
|
|
17
|
+
* not a keyword inside it), names the entry collection, the keying field, and
|
|
18
|
+
* the fields that MODIFY the mapped node. It sits on the document because it
|
|
19
|
+
* describes the kind's whole declaration rather than one field of it — but
|
|
20
|
+
* `schema:` is where every other `x-telo-*` keyword lives, so the reader
|
|
21
|
+
* accepts it in both positions and `validate-schema-projection.ts` reports the
|
|
22
|
+
* inner one. Silently ignoring a misplaced annotation is the exact failure the
|
|
23
|
+
* strict half exists to prevent: the projection stops typing its consumers and
|
|
24
|
+
* the diagnostic lands on the CONSUMER, blaming the wrong author.
|
|
25
|
+
*
|
|
26
|
+
* It is a declared LOOKUP, never a computed expression. The analyzer
|
|
27
|
+
* type-checks CEL and substitutes placeholders; it never evaluates, and a
|
|
28
|
+
* `base:`-style mapping is evaluated by the kernel at `create()` — too late for
|
|
29
|
+
* `telo check` to type the rows a consumer reads, which is the projection's
|
|
30
|
+
* whole purpose.
|
|
31
|
+
*
|
|
32
|
+
* Distinct from `x-telo-schema-from`, which derives a field's schema from a
|
|
33
|
+
* referenced KIND's definition schema. A projection is DECLARATION-derived: the
|
|
34
|
+
* row shape lives in one instance's own `columns:`, which no definition-level
|
|
35
|
+
* derivation can reach.
|
|
36
|
+
*/
|
|
37
|
+
function isObject(value) {
|
|
38
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
39
|
+
}
|
|
40
|
+
/** The projection a kind declares, or undefined. Invalid shapes read as absent;
|
|
41
|
+
* `validate-schema-projection.ts` is the half that reports them. */
|
|
42
|
+
export function readSchemaProjection(definition) {
|
|
43
|
+
if (!isObject(definition))
|
|
44
|
+
return undefined;
|
|
45
|
+
const raw = rawSchemaProjection(definition);
|
|
46
|
+
if (!isObject(raw))
|
|
47
|
+
return undefined;
|
|
48
|
+
const entries = raw.entries;
|
|
49
|
+
const key = raw.key;
|
|
50
|
+
if (typeof entries !== "string" || typeof key !== "string")
|
|
51
|
+
return undefined;
|
|
52
|
+
return {
|
|
53
|
+
entries,
|
|
54
|
+
key,
|
|
55
|
+
nameField: typeof raw.name === "string" ? raw.name : undefined,
|
|
56
|
+
nullable: typeof raw.nullable === "string" ? raw.nullable : undefined,
|
|
57
|
+
array: typeof raw.array === "string" ? raw.array : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** The annotation as written, from either position — the document (canonical)
|
|
61
|
+
* or `schema:` (accepted, and reported by the strict half). The document wins:
|
|
62
|
+
* a kind spelling it in both places is describing its own document. */
|
|
63
|
+
export function rawSchemaProjection(definition) {
|
|
64
|
+
if (!isObject(definition))
|
|
65
|
+
return undefined;
|
|
66
|
+
const own = definition["x-telo-schema-projection"];
|
|
67
|
+
if (own !== undefined)
|
|
68
|
+
return own;
|
|
69
|
+
const schema = definition.schema;
|
|
70
|
+
return isObject(schema) ? schema["x-telo-schema-projection"] : undefined;
|
|
71
|
+
}
|
|
72
|
+
/** True when the annotation was found inside `schema:` rather than on the
|
|
73
|
+
* document — the misplacement the strict half reports. */
|
|
74
|
+
export function schemaProjectionIsMisplaced(definition) {
|
|
75
|
+
if (!isObject(definition))
|
|
76
|
+
return false;
|
|
77
|
+
if (definition["x-telo-schema-projection"] !== undefined)
|
|
78
|
+
return false;
|
|
79
|
+
const schema = definition.schema;
|
|
80
|
+
return isObject(schema) && schema["x-telo-schema-projection"] !== undefined;
|
|
81
|
+
}
|
|
82
|
+
export function readSchemaMap(node) {
|
|
83
|
+
if (!isObject(node))
|
|
84
|
+
return undefined;
|
|
85
|
+
const raw = node["x-telo-schema-map"];
|
|
86
|
+
if (!isObject(raw))
|
|
87
|
+
return undefined;
|
|
88
|
+
const entries = Object.entries(raw).filter(([, value]) => isObject(value));
|
|
89
|
+
if (entries.length === 0)
|
|
90
|
+
return undefined;
|
|
91
|
+
return Object.fromEntries(entries);
|
|
92
|
+
}
|
|
93
|
+
/** The consumer-side annotation: a JSON Pointer to this resource's ref slot
|
|
94
|
+
* whose target declares the projection. */
|
|
95
|
+
export function readProjectionFrom(node) {
|
|
96
|
+
if (!isObject(node))
|
|
97
|
+
return undefined;
|
|
98
|
+
const raw = node["x-telo-schema-projection-from"];
|
|
99
|
+
return typeof raw === "string" ? raw : undefined;
|
|
100
|
+
}
|
|
101
|
+
function navigate(root, pointer) {
|
|
102
|
+
let current = root;
|
|
103
|
+
for (const segment of pointer.split("/")) {
|
|
104
|
+
if (segment === "")
|
|
105
|
+
continue;
|
|
106
|
+
if (!isObject(current))
|
|
107
|
+
return undefined;
|
|
108
|
+
current = current[segment];
|
|
109
|
+
}
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Find the `x-telo-schema-map` a projection keys on. The map sits on the entry
|
|
114
|
+
* field's schema, which is reached through the collection's own schema — a
|
|
115
|
+
* keyed map's `additionalProperties`, or an array's `items`.
|
|
116
|
+
*/
|
|
117
|
+
export function projectionKeyMap(kindSchema, projection) {
|
|
118
|
+
let node = kindSchema;
|
|
119
|
+
for (const segment of projection.entries.split("/")) {
|
|
120
|
+
if (segment === "")
|
|
121
|
+
continue;
|
|
122
|
+
if (!isObject(node) || !isObject(node.properties))
|
|
123
|
+
return undefined;
|
|
124
|
+
node = node.properties[segment];
|
|
125
|
+
}
|
|
126
|
+
if (!isObject(node))
|
|
127
|
+
return undefined;
|
|
128
|
+
const entry = isObject(node.additionalProperties)
|
|
129
|
+
? node.additionalProperties
|
|
130
|
+
: isObject(node.items)
|
|
131
|
+
? node.items
|
|
132
|
+
: undefined;
|
|
133
|
+
if (!isObject(entry) || !isObject(entry.properties))
|
|
134
|
+
return undefined;
|
|
135
|
+
return readSchemaMap(entry.properties[projection.key]);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Project one declaration to an object schema.
|
|
139
|
+
*
|
|
140
|
+
* Modifiers are a CLOSED set applied in a FIXED order — `array` wraps, then
|
|
141
|
+
* `nullable` widens. Closed because each changes how the schema is assembled,
|
|
142
|
+
* so a third-party modifier would be a name nothing acts on; ordered because
|
|
143
|
+
* leaving it implicit is how two implementations come to disagree.
|
|
144
|
+
*
|
|
145
|
+
* The projection is deliberately LOSSY. Length, precision, collation and check
|
|
146
|
+
* constraints do not reach it: a consumer needs the type, its nullability and
|
|
147
|
+
* its repetition, and the database enforces the rest. A per-entry schema rich
|
|
148
|
+
* enough to double as a validator would move the domain's semantics into the
|
|
149
|
+
* type layer.
|
|
150
|
+
*/
|
|
151
|
+
export function projectEntries(manifest, projection, map) {
|
|
152
|
+
const entries = navigate(manifest, projection.entries);
|
|
153
|
+
if (entries === undefined)
|
|
154
|
+
return undefined;
|
|
155
|
+
const pairs = [];
|
|
156
|
+
const consider = (name, entry) => {
|
|
157
|
+
if (!isObject(entry) || name === undefined)
|
|
158
|
+
return;
|
|
159
|
+
const key = entry[projection.key];
|
|
160
|
+
const mapped = typeof key === "string" ? map[key] : undefined;
|
|
161
|
+
// A value with no map entry projects to nothing rather than to `any`: the
|
|
162
|
+
// vocabulary is the kind's own enum, so an unmapped value is a gap in the
|
|
163
|
+
// kind's declaration, not a shape to guess at.
|
|
164
|
+
if (!mapped)
|
|
165
|
+
return;
|
|
166
|
+
let node = { ...mapped };
|
|
167
|
+
if (projection.array && entry[projection.array] === true) {
|
|
168
|
+
node = { type: "array", items: node };
|
|
169
|
+
}
|
|
170
|
+
if (projection.nullable && entry[projection.nullable] !== false) {
|
|
171
|
+
node = { anyOf: [node, { type: "null" }] };
|
|
172
|
+
}
|
|
173
|
+
pairs.push([name, node]);
|
|
174
|
+
};
|
|
175
|
+
if (Array.isArray(entries)) {
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
const name = isObject(entry) && projection.nameField
|
|
178
|
+
? entry[projection.nameField]
|
|
179
|
+
: undefined;
|
|
180
|
+
consider(name, entry);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else if (isObject(entries)) {
|
|
184
|
+
for (const [name, entry] of Object.entries(entries))
|
|
185
|
+
consider(name, entry);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
type: "object",
|
|
192
|
+
properties: Object.fromEntries(pairs),
|
|
193
|
+
additionalProperties: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** The `{kind, name, alias?}` reference a value holds, or undefined. Exported so
|
|
197
|
+
* a host whose slot may hold EITHER shape can fall back to this reading. */
|
|
198
|
+
export function readProjectionRef(value) {
|
|
199
|
+
if (!isObject(value))
|
|
200
|
+
return undefined;
|
|
201
|
+
const name = value.name;
|
|
202
|
+
if (typeof name !== "string")
|
|
203
|
+
return undefined;
|
|
204
|
+
return {
|
|
205
|
+
name,
|
|
206
|
+
kind: typeof value.kind === "string" ? value.kind : undefined,
|
|
207
|
+
alias: typeof value.alias === "string" ? value.alias : undefined,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The resolver for a FLATTENED manifest list — the analyzer's own shape.
|
|
212
|
+
*
|
|
213
|
+
* An alias narrows to the manifests forwarded from that import (stamped
|
|
214
|
+
* `metadata.alias` by flatten), so two libraries each exporting a `users` table
|
|
215
|
+
* stay distinguishable. Only when nothing carries the alias does it fall back to
|
|
216
|
+
* matching by name alone, which is the pre-flatten shape a standalone module
|
|
217
|
+
* analysis has.
|
|
218
|
+
*/
|
|
219
|
+
export function manifestListScope(manifests, resolveDefinition) {
|
|
220
|
+
return {
|
|
221
|
+
resolveDefinition,
|
|
222
|
+
resolveManifest(value) {
|
|
223
|
+
const ref = readProjectionRef(value);
|
|
224
|
+
if (!ref)
|
|
225
|
+
return undefined;
|
|
226
|
+
const byName = manifests.filter((candidate) => candidate?.metadata?.name === ref.name &&
|
|
227
|
+
(typeof ref.kind !== "string" || candidate.kind === ref.kind));
|
|
228
|
+
const aliased = ref.alias && ref.alias !== "Self"
|
|
229
|
+
? byName.filter((candidate) => candidate?.metadata?.alias === ref.alias)
|
|
230
|
+
: byName;
|
|
231
|
+
// A name that matches SEVERAL manifests is REFUSED rather than resolved to
|
|
232
|
+
// the first: picking one by flatten order would type the consumer's rows
|
|
233
|
+
// against the wrong declaration — a wrong answer, which is worse than no
|
|
234
|
+
// answer. Reported, so the author is told to disambiguate.
|
|
235
|
+
const matches = aliased.length > 0 ? aliased : byName;
|
|
236
|
+
if (matches.length === 0)
|
|
237
|
+
return undefined;
|
|
238
|
+
if (matches.length > 1)
|
|
239
|
+
return { ambiguous: true };
|
|
240
|
+
return { manifest: matches[0] };
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function refTarget(value, scope, pointer) {
|
|
245
|
+
if (!isObject(value))
|
|
246
|
+
return { reason: "no-ref", pointer };
|
|
247
|
+
const name = typeof value.name === "string" ? value.name : "<unnamed>";
|
|
248
|
+
const found = scope.resolveManifest(value);
|
|
249
|
+
if (!found)
|
|
250
|
+
return { reason: "unresolved", pointer, name };
|
|
251
|
+
if ("ambiguous" in found)
|
|
252
|
+
return { reason: "ambiguous", pointer, name };
|
|
253
|
+
const manifest = found.manifest;
|
|
254
|
+
if (typeof manifest.kind !== "string")
|
|
255
|
+
return { reason: "unresolved", pointer, name };
|
|
256
|
+
const definition = scope.resolveDefinition(manifest.kind);
|
|
257
|
+
if (!definition)
|
|
258
|
+
return { reason: "no-projection", pointer, kind: manifest.kind };
|
|
259
|
+
return { manifest, definition };
|
|
260
|
+
}
|
|
261
|
+
export function describeProjectionFailure(failure) {
|
|
262
|
+
switch (failure.reason) {
|
|
263
|
+
case "no-ref":
|
|
264
|
+
return `'${failure.pointer}' does not hold a reference, so there is no declaration to project.`;
|
|
265
|
+
case "unresolved":
|
|
266
|
+
return `'${failure.pointer}' references '${failure.name}', which resolves to no resource.`;
|
|
267
|
+
case "ambiguous":
|
|
268
|
+
return (`'${failure.pointer}' references '${failure.name}', which matches more than one resource ` +
|
|
269
|
+
`in scope. Rename one of them so the reference names exactly one declaration.`);
|
|
270
|
+
case "no-projection":
|
|
271
|
+
return (`'${failure.pointer}' references a resource of kind '${failure.kind}', which declares no ` +
|
|
272
|
+
`'x-telo-schema-projection' — so there is nothing for this slot to be typed from.`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Replace every `x-telo-schema-projection-from` node with the projection of the
|
|
277
|
+
* declaration it points at.
|
|
278
|
+
*
|
|
279
|
+
* Structural: returns a new schema and never mutates the one handed in. A node
|
|
280
|
+
* that cannot be projected is left exactly as it was — degrading to the slot's
|
|
281
|
+
* own schema rather than to a wrong one — and the reason is pushed to
|
|
282
|
+
* `failures`, because degrading SILENTLY is the failure this whole mechanism
|
|
283
|
+
* exists to move earlier: the consumer's contract quietly reopens and a
|
|
284
|
+
* misspelled field passes `telo check` exactly as it did before.
|
|
285
|
+
*/
|
|
286
|
+
export function resolveSchemaProjections(schema, manifest, scope, failures) {
|
|
287
|
+
if (Array.isArray(schema)) {
|
|
288
|
+
return schema.map((item) => resolveSchemaProjections(item, manifest, scope, failures));
|
|
289
|
+
}
|
|
290
|
+
if (!isObject(schema))
|
|
291
|
+
return schema;
|
|
292
|
+
const pointer = readProjectionFrom(schema);
|
|
293
|
+
if (pointer && manifest) {
|
|
294
|
+
const target = refTarget(navigate(manifest, pointer), scope, pointer);
|
|
295
|
+
if ("reason" in target) {
|
|
296
|
+
failures?.push(target);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
const projection = readSchemaProjection(target.definition);
|
|
300
|
+
const map = projection && projectionKeyMap(target.definition.schema, projection);
|
|
301
|
+
const projected = projection && map ? projectEntries(target.manifest, projection, map) : undefined;
|
|
302
|
+
if (projected) {
|
|
303
|
+
const { ["x-telo-schema-projection-from"]: _dropped, ...rest } = schema;
|
|
304
|
+
return { ...rest, ...projected };
|
|
305
|
+
}
|
|
306
|
+
failures?.push({
|
|
307
|
+
reason: "no-projection",
|
|
308
|
+
pointer,
|
|
309
|
+
kind: String(target.manifest.kind ?? "<unknown>"),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return Object.fromEntries(Object.entries(schema).map(([key, value]) => [
|
|
314
|
+
key,
|
|
315
|
+
key.startsWith("x-telo-") ? value : resolveSchemaProjections(value, manifest, scope, failures),
|
|
316
|
+
]));
|
|
317
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Does this array hold steps, and where does a step keep what the analyzer
|
|
3
|
+
* reads?" — one accessor, the `ref-slot.ts` / `zone-slot.ts` precedent.
|
|
4
|
+
*
|
|
5
|
+
* TWO SPELLINGS, one answer. A kind declares a step body by pointing its items
|
|
6
|
+
* at the shared grammar (`$ref: "telo://manifest#/$defs/Step"`), which fragment
|
|
7
|
+
* expansion stamps `x-telo-fragment: Step`; the field names are then constants
|
|
8
|
+
* of that grammar and nothing states them. Before the fragment existed the shape
|
|
9
|
+
* was each kind's own, so the kind had to say where its dispatch ref, output
|
|
10
|
+
* type and pure-value fields lived — `x-telo-step-context`. That annotation
|
|
11
|
+
* stays READ, permanently: published artifacts carry it and no migration entry
|
|
12
|
+
* can synthesize a `$ref` (the patch vocabulary writes scalars), so it has the
|
|
13
|
+
* standing of the legacy `x-telo-ref` string form.
|
|
14
|
+
*
|
|
15
|
+
* The annotation WINS where both are present. A kind that spells its own shape
|
|
16
|
+
* is describing its own manifest, and a stamp says only which grammar the items
|
|
17
|
+
* point at.
|
|
18
|
+
*
|
|
19
|
+
* Browser-safe: no Node built-ins.
|
|
20
|
+
*/
|
|
21
|
+
/** The name a step-bearing array's items carry once expanded. */
|
|
22
|
+
export declare const STEP_FRAGMENT = "Step";
|
|
23
|
+
/** Where a step keeps what the analyzer reads. */
|
|
24
|
+
export interface StepSlot {
|
|
25
|
+
/** Field on a step naming the resource to dispatch. */
|
|
26
|
+
invoke: string;
|
|
27
|
+
/** Field on the INVOKED resource's manifest that narrows its result type.
|
|
28
|
+
* Absent for a composer that allows no per-instance narrowing. */
|
|
29
|
+
outputType?: string;
|
|
30
|
+
/** Field that produces a result without dispatching. Only a grammar with pure
|
|
31
|
+
* steps has one. */
|
|
32
|
+
value?: string;
|
|
33
|
+
}
|
|
34
|
+
/** The step slot an array property declares, or undefined when it holds no
|
|
35
|
+
* steps. `fieldSchema` is the ARRAY's schema — the stamp sits on its `items`. */
|
|
36
|
+
export declare function readStepSlot(fieldSchema: unknown): StepSlot | undefined;
|
|
37
|
+
/** True when the property holds a step body. */
|
|
38
|
+
export declare function isStepSlot(fieldSchema: unknown): boolean;
|
|
39
|
+
//# sourceMappingURL=step-slot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-slot.d.ts","sourceRoot":"","sources":["../src/step-slot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,iEAAiE;AACjE,eAAO,MAAM,aAAa,SAAS,CAAC;AAEpC,kDAAkD;AAClD,MAAM,WAAW,QAAQ;IACvB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf;uEACmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;yBACqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;kFACkF;AAClF,wBAAgB,YAAY,CAAC,WAAW,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAcvE;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAExD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Does this array hold steps, and where does a step keep what the analyzer
|
|
3
|
+
* reads?" — one accessor, the `ref-slot.ts` / `zone-slot.ts` precedent.
|
|
4
|
+
*
|
|
5
|
+
* TWO SPELLINGS, one answer. A kind declares a step body by pointing its items
|
|
6
|
+
* at the shared grammar (`$ref: "telo://manifest#/$defs/Step"`), which fragment
|
|
7
|
+
* expansion stamps `x-telo-fragment: Step`; the field names are then constants
|
|
8
|
+
* of that grammar and nothing states them. Before the fragment existed the shape
|
|
9
|
+
* was each kind's own, so the kind had to say where its dispatch ref, output
|
|
10
|
+
* type and pure-value fields lived — `x-telo-step-context`. That annotation
|
|
11
|
+
* stays READ, permanently: published artifacts carry it and no migration entry
|
|
12
|
+
* can synthesize a `$ref` (the patch vocabulary writes scalars), so it has the
|
|
13
|
+
* standing of the legacy `x-telo-ref` string form.
|
|
14
|
+
*
|
|
15
|
+
* The annotation WINS where both are present. A kind that spells its own shape
|
|
16
|
+
* is describing its own manifest, and a stamp says only which grammar the items
|
|
17
|
+
* point at.
|
|
18
|
+
*
|
|
19
|
+
* Browser-safe: no Node built-ins.
|
|
20
|
+
*/
|
|
21
|
+
import { manifestFragmentOf } from "./manifest-schemas.js";
|
|
22
|
+
/** The name a step-bearing array's items carry once expanded. */
|
|
23
|
+
export const STEP_FRAGMENT = "Step";
|
|
24
|
+
/** The shared grammar's own field names — constants, since the shape is no
|
|
25
|
+
* longer the kind's to choose. */
|
|
26
|
+
const STEP_FRAGMENT_SLOT = { invoke: "invoke", outputType: "outputType", value: "value" };
|
|
27
|
+
/** The step slot an array property declares, or undefined when it holds no
|
|
28
|
+
* steps. `fieldSchema` is the ARRAY's schema — the stamp sits on its `items`. */
|
|
29
|
+
export function readStepSlot(fieldSchema) {
|
|
30
|
+
if (!fieldSchema || typeof fieldSchema !== "object")
|
|
31
|
+
return undefined;
|
|
32
|
+
const declared = fieldSchema["x-telo-step-context"];
|
|
33
|
+
if (declared && typeof declared === "object") {
|
|
34
|
+
const { invoke, outputType, value } = declared;
|
|
35
|
+
if (typeof invoke !== "string" || invoke.length === 0)
|
|
36
|
+
return undefined;
|
|
37
|
+
return {
|
|
38
|
+
invoke,
|
|
39
|
+
...(typeof outputType === "string" ? { outputType } : {}),
|
|
40
|
+
...(typeof value === "string" ? { value } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const items = fieldSchema.items;
|
|
44
|
+
return manifestFragmentOf(items) === STEP_FRAGMENT ? STEP_FRAGMENT_SLOT : undefined;
|
|
45
|
+
}
|
|
46
|
+
/** True when the property holds a step body. */
|
|
47
|
+
export function isStepSlot(fieldSchema) {
|
|
48
|
+
return readStepSlot(fieldSchema) !== undefined;
|
|
49
|
+
}
|
package/dist/telo-version.d.ts
CHANGED
package/dist/telo-version.js
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// its release identity, this is the scale a module's `requires.telo` range is
|
|
6
6
|
// written against, and every kernel in every language reports the same scale.
|
|
7
7
|
/** The surface generation this analyzer implements. */
|
|
8
|
-
export const TELO_SURFACE_VERSION = "0.
|
|
8
|
+
export const TELO_SURFACE_VERSION = "0.79.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAWtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AA4CD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACnC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GACxC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA0EjC;AAyID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAyKrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,SAAM,GACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC,CAGvD;AAYD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,SAAM,GAAG,MAAM,EAAE,CAqBxF"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { extractAccessChains, validateChainAgainstSchema } from "@telorun/templating";
|
|
2
2
|
import { elementSchemaOf, isLiveSlot, mergeTypeSchemas, parseCanonicalTypeSchemaId, } from "@telorun/sdk";
|
|
3
3
|
import { KERNEL_BUILTINS } from "./builtins.js";
|
|
4
|
+
import { isStepSlot } from "./step-slot.js";
|
|
5
|
+
import { withRefSlotsAsReadings } from "./ref-slot-reading.js";
|
|
4
6
|
/**
|
|
5
7
|
* Resolve a type field value (string name, inline type, or raw schema) to a JSON Schema.
|
|
6
8
|
* - String: look up the named type in allManifests (Type.JsonSchema resources)
|
|
@@ -367,7 +369,17 @@ export function resolveContextAnnotations(schema, manifestItem, opts) {
|
|
|
367
369
|
// so the variable is typed by the CONTRACT rather than by the wrapper
|
|
368
370
|
// around it. A raw JSON Schema resolves to itself, and a plain property
|
|
369
371
|
// map (a transport scope) resolves to nothing and is used verbatim.
|
|
370
|
-
|
|
372
|
+
const resolved = resolveTypeFieldToSchema(navigated, allManifests ?? []) ?? navigated;
|
|
373
|
+
// A ref slot inside it holds a live instance at runtime, which the
|
|
374
|
+
// kernel substitutes with that resource's published reading before
|
|
375
|
+
// evaluating (`celSelfView`). Type it the same way, or `self.<ref>` is
|
|
376
|
+
// the annotation node — a shape nothing can be read off.
|
|
377
|
+
return defs
|
|
378
|
+
? withRefSlotsAsReadings(resolved, {
|
|
379
|
+
resolve: (kind) => defs.resolve(kind),
|
|
380
|
+
resolveKind: (kind) => aliases?.resolveKind(kind),
|
|
381
|
+
})
|
|
382
|
+
: resolved;
|
|
371
383
|
}
|
|
372
384
|
}
|
|
373
385
|
if (defs) {
|
|
@@ -485,7 +497,9 @@ export function extractContextsFromSchema(schema, path = "$") {
|
|
|
485
497
|
return all.sort((a, b) => b.scope.length - a.scope.length);
|
|
486
498
|
}
|
|
487
499
|
/** Schema keys that declare a CEL-bearing region: a field carrying any of these
|
|
488
|
-
* is evaluated at runtime, so a `!cel` inside it (or a descendant) is live.
|
|
500
|
+
* is evaluated at runtime, so a `!cel` inside it (or a descendant) is live. A
|
|
501
|
+
* STEP BODY is one too, and says so through the grammar its items point at
|
|
502
|
+
* rather than through a key — {@link isStepSlot} reads either spelling. */
|
|
489
503
|
const CEL_REGION_KEYS = [
|
|
490
504
|
"x-telo-context",
|
|
491
505
|
"x-telo-step-context",
|
|
@@ -503,7 +517,7 @@ export function extractCelRegionScopes(schema, path = "$") {
|
|
|
503
517
|
if (!schema || typeof schema !== "object")
|
|
504
518
|
return [];
|
|
505
519
|
const out = [];
|
|
506
|
-
if (CEL_REGION_KEYS.some((k) => schema[k]))
|
|
520
|
+
if (CEL_REGION_KEYS.some((k) => schema[k]) || isStepSlot(schema))
|
|
507
521
|
out.push(path);
|
|
508
522
|
if (schema.properties) {
|
|
509
523
|
for (const [key, value] of Object.entries(schema.properties)) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CallGraph } from "./call-graph.js";
|
|
2
|
+
import { type AnalysisDiagnostic } from "./types.js";
|
|
3
|
+
import { type DefinitionLookup, type ZoneRegion } from "./resolve-zone-containment.js";
|
|
4
|
+
export interface DurableRegionArgs {
|
|
5
|
+
graph: CallGraph;
|
|
6
|
+
resolveDef: DefinitionLookup;
|
|
7
|
+
/** Only report against modules the entry owns — a published dependency's body
|
|
8
|
+
* is not the consumer's to fix, the `X_TELO_REF_UNRESOLVED` precedent. */
|
|
9
|
+
reportModules: ReadonlySet<string>;
|
|
10
|
+
}
|
|
11
|
+
/** Every durable-region diagnostic, over one graph. */
|
|
12
|
+
export declare function validateDurableRegions(args: DurableRegionArgs): AnalysisDiagnostic[];
|
|
13
|
+
/** Re-exported so a consumer can name a region without importing two files. */
|
|
14
|
+
export type { ZoneRegion };
|
|
15
|
+
//# sourceMappingURL=validate-durable-regions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-durable-regions.d.ts","sourceRoot":"","sources":["../src/validate-durable-regions.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,SAAS,EAAgC,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAChB,MAAM,+BAA+B,CAAC;AAsVvC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;IAC7B;+EAC2E;IAC3E,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAED,uDAAuD;AACvD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,iBAAiB,GAAG,kBAAkB,EAAE,CAkBpF;AAED,+EAA+E;AAC/E,YAAY,EAAE,UAAU,EAAE,CAAC"}
|