@telorun/kernel 0.66.0 → 0.68.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/application-env.d.ts +25 -0
- package/dist/application-env.d.ts.map +1 -1
- package/dist/application-env.js +79 -3
- package/dist/application-env.js.map +1 -1
- package/dist/controller-loader.d.ts +7 -5
- package/dist/controller-loader.d.ts.map +1 -1
- package/dist/controller-loader.js +7 -5
- package/dist/controller-loader.js.map +1 -1
- package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
- package/dist/controllers/resource-definition/resource-definition-controller.js +29 -8
- package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
- package/dist/evaluation-context.d.ts +6 -0
- package/dist/evaluation-context.d.ts.map +1 -1
- package/dist/evaluation-context.js +20 -8
- package/dist/evaluation-context.js.map +1 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +32 -3
- package/dist/kernel.js.map +1 -1
- package/dist/manifest-schemas.d.ts +1 -1
- package/dist/manifest-schemas.d.ts.map +1 -1
- package/dist/manifest-schemas.js +6 -0
- package/dist/manifest-schemas.js.map +1 -1
- package/dist/module-context.d.ts.map +1 -1
- package/dist/module-context.js +3 -4
- package/dist/module-context.js.map +1 -1
- package/dist/resource-context.d.ts +21 -20
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-context.js +71 -51
- package/dist/resource-context.js.map +1 -1
- package/dist/resource-handle.d.ts +11 -0
- package/dist/resource-handle.d.ts.map +1 -0
- package/dist/resource-handle.js +32 -0
- package/dist/resource-handle.js.map +1 -0
- package/dist/runtime-seam.d.ts.map +1 -1
- package/dist/runtime-seam.js +8 -2
- package/dist/runtime-seam.js.map +1 -1
- package/dist/schema-compiled-values.d.ts.map +1 -1
- package/dist/schema-compiled-values.js +39 -4
- package/dist/schema-compiled-values.js.map +1 -1
- package/dist/schema-validator.d.ts +21 -1
- package/dist/schema-validator.d.ts.map +1 -1
- package/dist/schema-validator.js +88 -6
- package/dist/schema-validator.js.map +1 -1
- package/dist/type-field-schema.d.ts +21 -0
- package/dist/type-field-schema.d.ts.map +1 -0
- package/dist/type-field-schema.js +54 -0
- package/dist/type-field-schema.js.map +1 -0
- package/dist/zone-context.d.ts +94 -0
- package/dist/zone-context.d.ts.map +1 -0
- package/dist/zone-context.js +272 -0
- package/dist/zone-context.js.map +1 -0
- package/package.json +4 -4
- package/src/application-env.ts +93 -4
- package/src/controller-loader.ts +7 -5
- package/src/controllers/resource-definition/resource-definition-controller.ts +31 -13
- package/src/evaluation-context.ts +21 -7
- package/src/kernel.ts +38 -1
- package/src/manifest-schemas.ts +6 -0
- package/src/module-context.ts +3 -4
- package/src/resource-context.ts +111 -50
- package/src/resource-handle.ts +35 -0
- package/src/runtime-seam.ts +9 -1
- package/src/schema-compiled-values.ts +36 -4
- package/src/schema-validator.ts +87 -5
- package/src/type-field-schema.ts +77 -0
- package/src/zone-context.ts +337 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kernel half of execution zones — `kernel/specs/execution-zones.md`.
|
|
3
|
+
*
|
|
4
|
+
* Everything a `ResourceContext` needs to open a zone declared by one of its
|
|
5
|
+
* own slots, and to assert it is inside one: annotation resolution in the
|
|
6
|
+
* kind's *declaring* scope, correlation-pointer resolution over the resource's
|
|
7
|
+
* own (Phase-5-injected) manifest, `extends`-aware kind matching, and the
|
|
8
|
+
* ambient-stack search.
|
|
9
|
+
*
|
|
10
|
+
* It lives beside `resource-handle.ts` rather than inside `resource-context.ts`
|
|
11
|
+
* because it is a subsystem with its own state, not a few accessors: the
|
|
12
|
+
* context delegates and keeps its own file about the resource lifecycle.
|
|
13
|
+
*
|
|
14
|
+
* **Resolution is memoized per annotated field.** Everything a match needs —
|
|
15
|
+
* the resolved zone kind, the accepted-kind set, and the correlation handle —
|
|
16
|
+
* is a function of `(kind, field)` and the injected manifest, all fixed once
|
|
17
|
+
* `create()` has returned. A statement resolves them on *every* dispatch
|
|
18
|
+
* otherwise, and this sits on the SQL hot path.
|
|
19
|
+
*/
|
|
20
|
+
import { InvokeError, RuntimeError, UNCANCELLABLE_CONTEXT, deriveContext, getRefIdentity, sameResource, } from "@telorun/sdk";
|
|
21
|
+
import { readProvidesZone, readRequiresZone } from "@telorun/analyzer";
|
|
22
|
+
import { isRefSentinel } from "@telorun/templating";
|
|
23
|
+
import { ambientInvokeContext, runWithAmbientContext } from "./evaluation-context.js";
|
|
24
|
+
import { handleOfInstance } from "./resource-handle.js";
|
|
25
|
+
export class ZoneContext {
|
|
26
|
+
#host;
|
|
27
|
+
/** Field → resolved requirement. See the file docstring. */
|
|
28
|
+
#requirements = new Map();
|
|
29
|
+
/** Slot → resolved correlation handle for a providing slot. */
|
|
30
|
+
#providers = new Map();
|
|
31
|
+
constructor(host) {
|
|
32
|
+
this.#host = host;
|
|
33
|
+
}
|
|
34
|
+
async withZone(slot, fn, base) {
|
|
35
|
+
const provider = this.resolveProvider(slot);
|
|
36
|
+
// Every field derived: kind = the declaring kind, provider = this resource,
|
|
37
|
+
// key = the annotation's pointer over this resource's own manifest — which
|
|
38
|
+
// is the runtime half of why a schema cannot claim another module's zone.
|
|
39
|
+
const entry = Object.freeze({
|
|
40
|
+
kind: this.#host.resolvedKind,
|
|
41
|
+
provider: this.#host.self,
|
|
42
|
+
...(provider.key ? { key: provider.key } : {}),
|
|
43
|
+
});
|
|
44
|
+
const from = base ?? ambientInvokeContext() ?? UNCANCELLABLE_CONTEXT;
|
|
45
|
+
const derived = deriveContext(from, { zones: [...(from.zones ?? []), entry] });
|
|
46
|
+
return runWithAmbientContext(derived, () => fn(derived, entry));
|
|
47
|
+
}
|
|
48
|
+
requireZone(field, ctx) {
|
|
49
|
+
const resolved = this.resolveRequirement(field);
|
|
50
|
+
const entry = this.match(resolved, ctx);
|
|
51
|
+
if (entry)
|
|
52
|
+
return entry;
|
|
53
|
+
const where = resolved.keyHandle
|
|
54
|
+
? ` on ${resolved.keyHandle.ref.kind} '${resolved.keyHandle.ref.name}'`
|
|
55
|
+
: "";
|
|
56
|
+
const reason = resolved.requirement.reason ? `: ${resolved.requirement.reason}` : "";
|
|
57
|
+
throw new InvokeError("ERR_ZONE_REQUIRED", `${this.#host.resolvedKind} '${this.#host.resourceName}': no ${resolved.requiredKind} zone open${where}${reason}`);
|
|
58
|
+
}
|
|
59
|
+
findZone(field, ctx) {
|
|
60
|
+
return this.match(this.resolveRequirement(field), ctx);
|
|
61
|
+
}
|
|
62
|
+
zonesFor(instance, ctx) {
|
|
63
|
+
const zones = (ctx ?? ambientInvokeContext())?.zones;
|
|
64
|
+
if (!zones || zones.length === 0)
|
|
65
|
+
return [];
|
|
66
|
+
const handle = handleOfInstance(instance);
|
|
67
|
+
if (!handle)
|
|
68
|
+
return [];
|
|
69
|
+
const out = [];
|
|
70
|
+
for (let i = zones.length - 1; i >= 0; i--) {
|
|
71
|
+
const entry = zones[i];
|
|
72
|
+
if (entry.key && sameResource(entry.key, handle))
|
|
73
|
+
out.push(entry);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
// ── resolution ────────────────────────────────────────────────────────────
|
|
78
|
+
resolveProvider(slot) {
|
|
79
|
+
const cached = this.#providers.get(slot);
|
|
80
|
+
if (cached)
|
|
81
|
+
return cached;
|
|
82
|
+
const provides = readProvidesZone(this.slotSchema(slot));
|
|
83
|
+
if (!provides) {
|
|
84
|
+
throw new RuntimeError("ERR_ZONE_ANNOTATION_MISSING", `[${this.#host.resourceName}] withZone('${slot}'): schema.properties.${slot} of ` +
|
|
85
|
+
`${this.#host.resolvedKind} carries no x-telo-provides-zone — the controller and its ` +
|
|
86
|
+
`schema disagree`);
|
|
87
|
+
}
|
|
88
|
+
const key = provides.key ? this.resolveCorrelationHandle([provides.key]) : undefined;
|
|
89
|
+
const resolved = key ? { key } : {};
|
|
90
|
+
this.#providers.set(slot, resolved);
|
|
91
|
+
return resolved;
|
|
92
|
+
}
|
|
93
|
+
resolveRequirement(field) {
|
|
94
|
+
const cached = this.#requirements.get(field);
|
|
95
|
+
if (cached)
|
|
96
|
+
return cached;
|
|
97
|
+
const requirement = readRequiresZone(this.slotSchema(field));
|
|
98
|
+
if (!requirement) {
|
|
99
|
+
throw new RuntimeError("ERR_ZONE_ANNOTATION_MISSING", `[${this.#host.resourceName}] requireZone('${field}'): schema.properties.${field} of ` +
|
|
100
|
+
`${this.#host.resolvedKind} carries no x-telo-requires-zone — the controller and its ` +
|
|
101
|
+
`schema disagree`);
|
|
102
|
+
}
|
|
103
|
+
const requiredKind = this.resolveZoneKind(requirement.zone);
|
|
104
|
+
const resolved = {
|
|
105
|
+
requirement,
|
|
106
|
+
requiredKind,
|
|
107
|
+
accepts: this.acceptancePredicate(requiredKind),
|
|
108
|
+
// When no pointer resolves, the requirement discharges uncorrelated — any
|
|
109
|
+
// zone of the right type. Inventing a correlation the manifest does not
|
|
110
|
+
// state would manufacture failures from a guess.
|
|
111
|
+
keyHandle: requirement.key.length > 0 ? this.resolveCorrelationHandle(requirement.key) : undefined,
|
|
112
|
+
};
|
|
113
|
+
this.#requirements.set(field, resolved);
|
|
114
|
+
return resolved;
|
|
115
|
+
}
|
|
116
|
+
/** Innermost first — the order a nested zone must win in. */
|
|
117
|
+
match(resolved, ctx) {
|
|
118
|
+
const zones = (ctx ?? ambientInvokeContext())?.zones ?? [];
|
|
119
|
+
for (let i = zones.length - 1; i >= 0; i--) {
|
|
120
|
+
const entry = zones[i];
|
|
121
|
+
if (!resolved.accepts(entry.kind))
|
|
122
|
+
continue;
|
|
123
|
+
if (resolved.keyHandle && !(entry.key && sameResource(entry.key, resolved.keyHandle))) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
return entry;
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
/** The schema node for one of this kind's own top-level fields — the site the
|
|
131
|
+
* zone annotations live on. */
|
|
132
|
+
slotSchema(field) {
|
|
133
|
+
const def = this.ownDefinition();
|
|
134
|
+
return def.schema
|
|
135
|
+
?.properties?.[field];
|
|
136
|
+
}
|
|
137
|
+
ownDefinition() {
|
|
138
|
+
const def = this.#host.resolveDefinition(this.#host.resolvedKind);
|
|
139
|
+
if (!def) {
|
|
140
|
+
throw new RuntimeError("ERR_ZONE_ANNOTATION_MISSING", `[${this.#host.resourceName}] no registered definition for kind '${this.#host.resolvedKind}'`);
|
|
141
|
+
}
|
|
142
|
+
return def;
|
|
143
|
+
}
|
|
144
|
+
/** Resolve the annotation's zone kind to canonical `<module>.<Kind>`.
|
|
145
|
+
* `resolveSchemaRefKinds` already rewrote it in the DECLARING module's scope
|
|
146
|
+
* during analysis; the scoped lookup is the fallback for an un-analyzed load. */
|
|
147
|
+
resolveZoneKind(zone) {
|
|
148
|
+
const def = this.#host.resolveDefinitionIn(zone, this.ownDefinition().metadata.module);
|
|
149
|
+
if (!def) {
|
|
150
|
+
throw new RuntimeError("ERR_ZONE_UNRESOLVED", `[${this.#host.resourceName}] x-telo-requires-zone names '${zone}', which resolves to no ` +
|
|
151
|
+
`registered kind`);
|
|
152
|
+
}
|
|
153
|
+
return `${def.metadata.module}.${def.metadata.name}`;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Acceptance for one requirement: the required kind, or anything that
|
|
157
|
+
* transitively `extends` it.
|
|
158
|
+
*
|
|
159
|
+
* A predicate rather than a precomputed set because the kernel's registry has
|
|
160
|
+
* no `extendedBy` index — only the upward `extends` link — so the set of
|
|
161
|
+
* acceptable kinds is not enumerable from the requirement alone. Each
|
|
162
|
+
* candidate walks UP instead, and only ever for a kind actually seen on the
|
|
163
|
+
* stack. The verdict is cached per entry kind, so a repeated dispatch through
|
|
164
|
+
* the same shape costs one map lookup.
|
|
165
|
+
*/
|
|
166
|
+
acceptancePredicate(requiredKind) {
|
|
167
|
+
const verdicts = new Map([[requiredKind, true]]);
|
|
168
|
+
return (entryKind) => {
|
|
169
|
+
const known = verdicts.get(entryKind);
|
|
170
|
+
if (known !== undefined)
|
|
171
|
+
return known;
|
|
172
|
+
const verdict = this.extendsChainReaches(entryKind, requiredKind);
|
|
173
|
+
verdicts.set(entryKind, verdict);
|
|
174
|
+
return verdict;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
extendsChainReaches(entryKind, requiredKind) {
|
|
178
|
+
let current = entryKind;
|
|
179
|
+
const seen = new Set();
|
|
180
|
+
while (current !== undefined && !seen.has(current)) {
|
|
181
|
+
if (current === requiredKind)
|
|
182
|
+
return true;
|
|
183
|
+
seen.add(current);
|
|
184
|
+
const def = this.#host.resolveDefinition(current);
|
|
185
|
+
if (typeof def?.extends !== "string")
|
|
186
|
+
return false;
|
|
187
|
+
const parent = this.#host.resolveDefinitionIn(def.extends, def.metadata.module);
|
|
188
|
+
current = parent ? `${parent.metadata.module}.${parent.metadata.name}` : undefined;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
/** First pointer that resolves to a live instance wins — the manifest-level
|
|
193
|
+
* transcription of a controller's own `a ?? b` derivation. */
|
|
194
|
+
resolveCorrelationHandle(pointers) {
|
|
195
|
+
for (const pointer of pointers) {
|
|
196
|
+
const handle = this.resolveKeyPointer(pointer);
|
|
197
|
+
if (handle)
|
|
198
|
+
return handle;
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
resolveKeyPointer(pointer) {
|
|
203
|
+
const segments = pointer.split("/").filter((s) => s.length > 0);
|
|
204
|
+
if (segments.length === 0)
|
|
205
|
+
return undefined;
|
|
206
|
+
let manifest = this.#host.manifest;
|
|
207
|
+
let value;
|
|
208
|
+
for (let i = 0; i < segments.length; i++) {
|
|
209
|
+
if (i > 0) {
|
|
210
|
+
// A pointer may traverse a `!ref` into the referenced resource's own
|
|
211
|
+
// manifest: read field → resolve reference → read field. Purely
|
|
212
|
+
// mechanical — no kind is named here.
|
|
213
|
+
const name = this.referencedName(value);
|
|
214
|
+
manifest = name ? this.#host.resolveLocalManifest(name) : undefined;
|
|
215
|
+
if (!manifest)
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
value = manifest[segments[i]];
|
|
219
|
+
if (value === undefined || value === null)
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
return this.handleOfValue(value);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The LOCAL name a reference value points at, in any of its three shapes.
|
|
226
|
+
*
|
|
227
|
+
* A cross-module `!ref Alias.name` yields undefined: it names an instance in
|
|
228
|
+
* another module's scope, and this resolver is scope-local, so taking the
|
|
229
|
+
* bare name would bind to whatever local resource happened to share it.
|
|
230
|
+
* Leaving it uncorrelated is the under-approximating direction. The analyzer's
|
|
231
|
+
* `refName` holds the identical rule, so the two halves agree about what a
|
|
232
|
+
* traversing pointer means.
|
|
233
|
+
*/
|
|
234
|
+
referencedName(value) {
|
|
235
|
+
if (value === null || typeof value !== "object")
|
|
236
|
+
return undefined;
|
|
237
|
+
const id = getRefIdentity(value) ?? handleOfInstance(value)?.ref;
|
|
238
|
+
if (id)
|
|
239
|
+
return id.name;
|
|
240
|
+
if (isRefSentinel(value)) {
|
|
241
|
+
const source = value.source;
|
|
242
|
+
const dot = source.indexOf(".");
|
|
243
|
+
if (dot <= 0)
|
|
244
|
+
return source;
|
|
245
|
+
return source.slice(0, dot) === "Self" ? source.slice(dot + 1) : undefined;
|
|
246
|
+
}
|
|
247
|
+
const v = value;
|
|
248
|
+
const pure = typeof v.kind === "string" &&
|
|
249
|
+
typeof v.name === "string" &&
|
|
250
|
+
Object.keys(v).every((k) => k === "kind" || k === "name" || k === "alias");
|
|
251
|
+
if (!pure)
|
|
252
|
+
return undefined;
|
|
253
|
+
if (typeof v.alias === "string" && v.alias !== "Self")
|
|
254
|
+
return undefined;
|
|
255
|
+
return v.name;
|
|
256
|
+
}
|
|
257
|
+
/** The handle a pointer's terminal value identifies: a live instance's own
|
|
258
|
+
* handle, or the handle of the instance a reference resolves to. */
|
|
259
|
+
handleOfValue(value) {
|
|
260
|
+
if (value === null || typeof value !== "object")
|
|
261
|
+
return undefined;
|
|
262
|
+
const direct = handleOfInstance(value);
|
|
263
|
+
if (direct)
|
|
264
|
+
return direct;
|
|
265
|
+
const name = this.referencedName(value);
|
|
266
|
+
if (!name)
|
|
267
|
+
return undefined;
|
|
268
|
+
const instance = this.#host.resolveLocalInstance(name);
|
|
269
|
+
return instance ? handleOfInstance(instance) : undefined;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=zone-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-context.js","sourceRoot":"","sources":["../src/zone-context.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/kernel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.0",
|
|
4
4
|
"description": "Telo Runtime - A lightweight, polyglot execution host.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@marcbachmann/cel-js": "^7.6.1",
|
|
63
63
|
"@sinclair/typebox": "^0.34.48",
|
|
64
|
-
"@telorun/analyzer": "0.
|
|
64
|
+
"@telorun/analyzer": "0.55.0",
|
|
65
65
|
"@telorun/glob": "0.2.0",
|
|
66
|
-
"@telorun/templating": "0.
|
|
66
|
+
"@telorun/templating": "0.12.0",
|
|
67
67
|
"ajv": "^8.17.1",
|
|
68
68
|
"ajv-formats": "^3.0.1",
|
|
69
69
|
"packageurl-js": "^2.0.1",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@types/tar-stream": "^3.1.3",
|
|
76
76
|
"typescript": "^5.0.0",
|
|
77
77
|
"vitest": "^2.1.8",
|
|
78
|
-
"@telorun/sdk": "0.
|
|
78
|
+
"@telorun/sdk": "0.68.0"
|
|
79
79
|
},
|
|
80
80
|
"optionalDependencies": {
|
|
81
81
|
"esbuild": "^0.25.12"
|
package/src/application-env.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type DefResolver,
|
|
3
|
+
effectiveAuthorSchema,
|
|
4
|
+
residualEntrySchema,
|
|
5
|
+
withStreamPropertiesSkipped,
|
|
6
|
+
} from "@telorun/analyzer";
|
|
7
|
+
import type {
|
|
8
|
+
ResourceContext,
|
|
9
|
+
ResourceDefinition,
|
|
10
|
+
ResourceManifest,
|
|
11
|
+
TypeRule,
|
|
12
|
+
} from "@telorun/sdk";
|
|
3
13
|
import { RuntimeError } from "@telorun/sdk";
|
|
14
|
+
import { create as createJsonSchemaType } from "./controllers/type/json-schema-controller.js";
|
|
4
15
|
import { SchemaValidator } from "./schema-validator.js";
|
|
16
|
+
import { resolveTypeFieldSchema } from "./type-field-schema.js";
|
|
5
17
|
|
|
6
18
|
type EntryType = "string" | "integer" | "number" | "boolean" | "object" | "array";
|
|
7
19
|
|
|
@@ -144,6 +156,63 @@ export function precompileApplicationEnvSchemas(
|
|
|
144
156
|
}
|
|
145
157
|
}
|
|
146
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Register every `Telo.JsonSchema` resource's resolved schema into `validator`,
|
|
161
|
+
* ahead of the contract warm below.
|
|
162
|
+
*
|
|
163
|
+
* A contract declared as `{kind: Telo.JsonSchema, schema: {$ref: "telo:m/X"}}`
|
|
164
|
+
* — what `oauth-client` and `vector-store` write — is compiled at runtime from
|
|
165
|
+
* the schema registered under that id, reached by following the alias. With no
|
|
166
|
+
* types registered the warm follows it to the `$ref` wrapper itself, AJV refuses
|
|
167
|
+
* the unresolvable reference, and the entry is silently not baked while the
|
|
168
|
+
* runtime goes on compiling something else: a guaranteed miss for exactly the
|
|
169
|
+
* modules that declare their shapes once and reference them.
|
|
170
|
+
*
|
|
171
|
+
* Runs the REAL type controller rather than re-deriving registration here. The
|
|
172
|
+
* three names a type registers under, the canonical `telo:` id and the `extends`
|
|
173
|
+
* merge are its rules; a second implementation would drift into baking schemas
|
|
174
|
+
* under keys the runtime never asks for — the failure this whole pass exists to
|
|
175
|
+
* remove. The loop mirrors the kernel's multi-pass init (`create` returns null
|
|
176
|
+
* while a parent type is unregistered) and stops as soon as a pass registers
|
|
177
|
+
* nothing new, so an unresolvable parent ends it instead of spinning.
|
|
178
|
+
*
|
|
179
|
+
* The deprecated `Type.JsonSchema` is not warmed: it is a module kind with its
|
|
180
|
+
* own controller, and reaching for this one would be assuming the two stayed
|
|
181
|
+
* identical.
|
|
182
|
+
*/
|
|
183
|
+
export async function precompileTypeSchemas(
|
|
184
|
+
manifests: Array<Record<string, any>>,
|
|
185
|
+
validator: SchemaValidator,
|
|
186
|
+
): Promise<void> {
|
|
187
|
+
const ctx = {
|
|
188
|
+
lookupSchema: (name: string) => validator.getSchema(name),
|
|
189
|
+
registerSchema: (name: string, schema: object) => validator.addSchema(name, schema),
|
|
190
|
+
registerTypeRules: (name: string, rules: TypeRule[]) => validator.addTypeRules(name, rules),
|
|
191
|
+
} as unknown as ResourceContext;
|
|
192
|
+
|
|
193
|
+
let pending = manifests.filter(
|
|
194
|
+
(m) =>
|
|
195
|
+
m?.kind === "Telo.JsonSchema" &&
|
|
196
|
+
m.schema &&
|
|
197
|
+
typeof m.metadata?.name === "string" &&
|
|
198
|
+
typeof m.metadata?.module === "string",
|
|
199
|
+
);
|
|
200
|
+
while (pending.length > 0) {
|
|
201
|
+
const deferred: Array<Record<string, any>> = [];
|
|
202
|
+
for (const m of pending) {
|
|
203
|
+
try {
|
|
204
|
+
if ((await createJsonSchemaType(m as unknown as ResourceManifest, ctx)) === null) {
|
|
205
|
+
deferred.push(m);
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
// A broken type surfaces through analysis / runtime, not the warm pass.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (deferred.length === pending.length) break;
|
|
212
|
+
pending = deferred;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
147
216
|
/**
|
|
148
217
|
* Build-time cache warm for resource-config validators. The runtime
|
|
149
218
|
* `_createInstance` compiles the declaring `Telo.Definition`'s `schema` to
|
|
@@ -188,11 +257,31 @@ export function precompileDefinitionSchemas(
|
|
|
188
257
|
// Broken schemas are reported by analysis / runtime, not the warm pass.
|
|
189
258
|
}
|
|
190
259
|
};
|
|
260
|
+
const lookup = (name: string) => validator.getSchema(name);
|
|
261
|
+
// A contract validator is compiled from the RESOLVED schema with its
|
|
262
|
+
// `x-telo-stream` properties stripped, never from the declaration — see
|
|
263
|
+
// `resolveBoundContract`. Baking the declaration instead bakes a validator for
|
|
264
|
+
// `{kind, schema}`, which no dispatch asks for. A declaration that needs the
|
|
265
|
+
// runtime type registry (a bare name, a `{kind, name}` ref) resolves to
|
|
266
|
+
// nothing here — named types register when their resources initialize, long
|
|
267
|
+
// after the warm — so it is skipped rather than baked wrong.
|
|
268
|
+
const compileContract = (declared: unknown): void => {
|
|
269
|
+
if (declared === undefined || declared === null) return;
|
|
270
|
+
try {
|
|
271
|
+
const schema = resolveTypeFieldSchema(declared, lookup);
|
|
272
|
+
if (!schema) return;
|
|
273
|
+
compile(withStreamPropertiesSkipped(schema, (ref) => lookup(ref) as any));
|
|
274
|
+
} catch {
|
|
275
|
+
// An unresolvable contract is a dispatch-time error, not a warm failure.
|
|
276
|
+
}
|
|
277
|
+
};
|
|
191
278
|
for (const m of manifests) {
|
|
279
|
+
// A per-instance contract overrides the kind's, so a resource declaring its
|
|
280
|
+
// own narrowing is what the runtime compiles for that instance.
|
|
281
|
+
compileContract(m?.inputType);
|
|
282
|
+
compileContract(m?.outputType);
|
|
192
283
|
if (m?.kind !== "Telo.Definition") continue;
|
|
193
284
|
compile(m.schema);
|
|
194
|
-
compile(m.inputType);
|
|
195
|
-
compile(m.outputType);
|
|
196
285
|
if (resolverFor && m.extends) {
|
|
197
286
|
// Mirrors the runtime stamp in `resource-definition-controller`; sharing
|
|
198
287
|
// `effectiveAuthorSchema` is what keeps the two keys identical.
|
package/src/controller-loader.ts
CHANGED
|
@@ -183,14 +183,16 @@ export class ControllerLoader {
|
|
|
183
183
|
* Resolve a controller without importing it: pick the first candidate this
|
|
184
184
|
* environment can host (same ordering + env-missing fallback as {@link load}),
|
|
185
185
|
* verify it's present, and return a {@link ResolvedController} whose
|
|
186
|
-
* `importInstance` defers the actual import/eval.
|
|
187
|
-
*
|
|
188
|
-
*
|
|
186
|
+
* `importInstance` defers the actual import/eval. Lazy controller loading
|
|
187
|
+
* calls this from the kind's first instantiation — never at boot — so a
|
|
188
|
+
* definition whose candidate list nothing in this environment can host
|
|
189
|
+
* registers fine and errors only when a resource of it is declared (matching
|
|
190
|
+
* the Rust kernel's deferral).
|
|
189
191
|
*
|
|
190
192
|
* Silent by design — no lifecycle events here; the caller emits
|
|
191
193
|
* ControllerLoading/Loaded around `importInstance` so the events fire when the
|
|
192
|
-
* load actually happens. A total resolution failure throws
|
|
193
|
-
*
|
|
194
|
+
* load actually happens. A total resolution failure throws, mirroring
|
|
195
|
+
* {@link load}'s aggregated error.
|
|
194
196
|
*/
|
|
195
197
|
async resolve(
|
|
196
198
|
purlCandidates: string[],
|
|
@@ -161,32 +161,39 @@ class ResourceDefinition implements ResourceInstance {
|
|
|
161
161
|
cacheRoot: host.getCacheRoot?.(),
|
|
162
162
|
log: ctx.log,
|
|
163
163
|
});
|
|
164
|
-
// Eager resolve — verify the controller is hostable now (so a broken
|
|
165
|
-
// `controllers:` candidate fails fast at boot), but defer the expensive
|
|
166
|
-
// import/eval and the controller's `register()` to the kind's first
|
|
167
|
-
// instantiation. Definitions whose kind is never instantiated never import.
|
|
168
164
|
// The artifact of the module that DECLARED this kind — a bundled controller
|
|
169
165
|
// ships in its own module's payload, not the consumer's. It owns the pinned
|
|
170
166
|
// ref and the verified layer index, so the loader picks a candidate and asks
|
|
171
167
|
// it for that selector's directory rather than fetching anything itself.
|
|
172
168
|
const artifact = host.getModuleArtifact?.(this.resource.metadata.source);
|
|
173
|
-
const resolved = await loader.resolve(
|
|
174
|
-
this.resource.controllers,
|
|
175
|
-
this.resource.metadata.source,
|
|
176
|
-
ctx.getControllerPolicy(),
|
|
177
|
-
artifact,
|
|
178
|
-
);
|
|
179
169
|
ctx.registerDefinition(this.resource);
|
|
180
170
|
|
|
181
171
|
const moduleName = this.resource.metadata.module;
|
|
182
172
|
const kindName = this.resource.metadata.name;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
173
|
+
const controllers = this.resource.controllers;
|
|
174
|
+
const source = this.resource.metadata.source;
|
|
175
|
+
const policy = ctx.getControllerPolicy();
|
|
176
|
+
// Resolution AND import are deferred to the kind's first instantiation,
|
|
177
|
+
// matching the Rust kernel: a definition whose candidate list nothing in
|
|
178
|
+
// this environment can host registers fine and errors — naming the kind —
|
|
179
|
+
// only when a resource of it is declared. That is what lets both kernels
|
|
180
|
+
// load a partially-covered module (e.g. console's stream kinds with no Rust
|
|
181
|
+
// controller) instead of rejecting it over kinds nobody uses.
|
|
182
|
+
// Lifecycle events are emitted here (not in the loader) so ControllerLoading
|
|
183
|
+
// / ControllerLoaded / ControllerLoadFailed — and the import duration —
|
|
184
|
+
// surface when the load actually happens, with the resolved PURL + source.
|
|
186
185
|
host.registerLazyController(
|
|
187
186
|
moduleName,
|
|
188
187
|
kindName,
|
|
189
188
|
async () => {
|
|
189
|
+
const resolved = await loader
|
|
190
|
+
.resolve(controllers, source, policy, artifact)
|
|
191
|
+
.catch((err) => {
|
|
192
|
+
if (err instanceof RuntimeError) {
|
|
193
|
+
throw new RuntimeError(err.code, `kind '${moduleName}.${kindName}': ${err.message}`);
|
|
194
|
+
}
|
|
195
|
+
throw err;
|
|
196
|
+
});
|
|
190
197
|
await ctx.emit("ControllerLoading", { purl: resolved.purl });
|
|
191
198
|
const startedAt = Date.now();
|
|
192
199
|
const instance = await resolved.importInstance().catch(async (err) => {
|
|
@@ -241,6 +248,17 @@ export function register(ctx: ControllerContext): void {
|
|
|
241
248
|
}
|
|
242
249
|
|
|
243
250
|
export async function create(resource: any, ctx: ResourceContext): Promise<ResourceDefinition> {
|
|
251
|
+
// Named preflight for the one capability the schema rejects by omission: the
|
|
252
|
+
// AJV oneOf failure it produces never says WHY, and this is the mistake an
|
|
253
|
+
// author migrating a multi-kind slot is most likely to make.
|
|
254
|
+
if (resource?.capability === "Telo.Executable") {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Invalid ResourceDefinition "${resource.metadata?.name}": capability 'Telo.Executable' ` +
|
|
257
|
+
`is an x-telo-ref slot constraint (the parent Telo.Invocable and Telo.Runnable ` +
|
|
258
|
+
`extend), not a declarable lifecycle role. Declare 'Telo.Invocable' (invoke) or ` +
|
|
259
|
+
`'Telo.Runnable' (run) instead.`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
244
262
|
// Validate incoming resource definition against schema
|
|
245
263
|
if (!validateResourceDefinition(resource)) {
|
|
246
264
|
throw new Error(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { formatSpanCounter } from "./logging/span-id.js";
|
|
3
3
|
import {
|
|
4
|
+
deriveContext,
|
|
4
5
|
getRefIdentity,
|
|
5
6
|
isCompiledValue,
|
|
6
7
|
isInvokeError,
|
|
@@ -263,6 +264,15 @@ export function ambientInvokeContext(): InvokeContext | undefined {
|
|
|
263
264
|
return cancellationStore.getStore();
|
|
264
265
|
}
|
|
265
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Establish `ctx` as the ambient invocation context for the duration of `fn`.
|
|
269
|
+
* The seam `withZone` uses to make an opened zone ambient without owning the
|
|
270
|
+
* store — the store itself stays kernel-internal, never on the SDK surface.
|
|
271
|
+
*/
|
|
272
|
+
export function runWithAmbientContext<T>(ctx: InvokeContext, fn: () => T): T {
|
|
273
|
+
return cancellationStore.run(ctx, fn);
|
|
274
|
+
}
|
|
275
|
+
|
|
266
276
|
/** Marks a scope built by {@link EvaluationContext.bindScope}, whose properties
|
|
267
277
|
* are getters. `expandWith` merges such a scope by descriptor rather than by
|
|
268
278
|
* value — reading the value here is what a lazy binding must not do. */
|
|
@@ -1189,11 +1199,13 @@ export class EvaluationContext implements IEvaluationContext {
|
|
|
1189
1199
|
outcome,
|
|
1190
1200
|
phase === "end" && rootScope ? { ...detail, context: rootScope } : detail,
|
|
1191
1201
|
);
|
|
1192
|
-
// When tracing, a
|
|
1202
|
+
// When tracing, a derived context carries the new id down the tree so nested
|
|
1193
1203
|
// invokes read it as their parent; it is never `=== ambient`, so the call
|
|
1194
|
-
// always (re)establishes the ALS scope.
|
|
1204
|
+
// always (re)establishes the ALS scope. Derived, never a fresh literal: a
|
|
1205
|
+
// literal drops every field it does not restate (`zones`), making
|
|
1206
|
+
// propagation differ between tracing on and off.
|
|
1195
1207
|
const invokeCtx: InvokeContext = tracing
|
|
1196
|
-
? {
|
|
1208
|
+
? deriveContext(baseCtx, { invocationId, parentInvocationId, traceId })
|
|
1197
1209
|
: baseCtx;
|
|
1198
1210
|
|
|
1199
1211
|
// Pre-dispatch gate: a sub-invoke reached after the tree was cancelled is
|
|
@@ -1391,12 +1403,14 @@ export class EvaluationContext implements IEvaluationContext {
|
|
|
1391
1403
|
);
|
|
1392
1404
|
|
|
1393
1405
|
await this.emit(`${opts.ref.name}.Requesting`, payload("start", undefined));
|
|
1394
|
-
|
|
1395
|
-
|
|
1406
|
+
// Derived so the span context keeps whatever `base` carries beyond the span
|
|
1407
|
+
// ids — with tracing off this method returns `base` unchanged, and tracing
|
|
1408
|
+
// must not change what propagates.
|
|
1409
|
+
const context: InvokeContext = deriveContext(ctx, {
|
|
1396
1410
|
invocationId: spanId,
|
|
1397
1411
|
parentInvocationId: parentSpanId,
|
|
1398
1412
|
traceId,
|
|
1399
|
-
};
|
|
1413
|
+
});
|
|
1400
1414
|
let settled = false;
|
|
1401
1415
|
return {
|
|
1402
1416
|
context,
|
|
@@ -1460,7 +1474,7 @@ export class EvaluationContext implements IEvaluationContext {
|
|
|
1460
1474
|
phase === "end" && rootScope ? { ...detail, context: rootScope } : detail,
|
|
1461
1475
|
);
|
|
1462
1476
|
const invokeCtx: InvokeContext = tracing
|
|
1463
|
-
? {
|
|
1477
|
+
? deriveContext(baseCtx, { invocationId, parentInvocationId, traceId })
|
|
1464
1478
|
: baseCtx;
|
|
1465
1479
|
|
|
1466
1480
|
// Refuse a target reached after the boot run was cancelled.
|