@telorun/kernel 0.81.0 → 0.82.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.
- package/dist/controllers/logging/console-sink-controller.d.ts.map +1 -1
- package/dist/controllers/logging/console-sink-controller.js +19 -9
- package/dist/controllers/logging/console-sink-controller.js.map +1 -1
- package/dist/controllers/logging/file-sink-controller.d.ts.map +1 -1
- package/dist/controllers/logging/file-sink-controller.js +17 -6
- package/dist/controllers/logging/file-sink-controller.js.map +1 -1
- package/dist/controllers/module/import-controller.d.ts.map +1 -1
- package/dist/controllers/module/import-controller.js +36 -15
- package/dist/controllers/module/import-controller.js.map +1 -1
- package/dist/controllers/resource-definition/resource-template-controller.d.ts.map +1 -1
- package/dist/controllers/resource-definition/resource-template-controller.js +7 -5
- package/dist/controllers/resource-definition/resource-template-controller.js.map +1 -1
- package/dist/effect-scope.d.ts +132 -0
- package/dist/effect-scope.d.ts.map +1 -0
- package/dist/effect-scope.js +253 -0
- package/dist/effect-scope.js.map +1 -0
- package/dist/evaluation-context.d.ts +26 -1
- package/dist/evaluation-context.d.ts.map +1 -1
- package/dist/evaluation-context.js +156 -12
- package/dist/evaluation-context.js.map +1 -1
- package/dist/init-failure-diagnostics.d.ts +10 -0
- package/dist/init-failure-diagnostics.d.ts.map +1 -1
- package/dist/init-failure-diagnostics.js +7 -1
- package/dist/init-failure-diagnostics.js.map +1 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +17 -12
- package/dist/kernel.js.map +1 -1
- package/dist/manifest-schemas.d.ts +4 -1
- package/dist/manifest-schemas.d.ts.map +1 -1
- package/dist/manifest-schemas.js +4 -10
- package/dist/manifest-schemas.js.map +1 -1
- package/dist/module-context.d.ts +10 -0
- package/dist/module-context.d.ts.map +1 -1
- package/dist/module-context.js +15 -0
- package/dist/module-context.js.map +1 -1
- package/dist/observed-state.d.ts.map +1 -1
- package/dist/observed-state.js +2 -4
- package/dist/observed-state.js.map +1 -1
- package/dist/resource-context.d.ts +18 -1
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-context.js +27 -0
- package/dist/resource-context.js.map +1 -1
- package/dist/schema-compiled-values.d.ts +2 -1
- package/dist/schema-compiled-values.d.ts.map +1 -1
- package/dist/schema-compiled-values.js +23 -20
- package/dist/schema-compiled-values.js.map +1 -1
- package/package.json +4 -4
- package/src/controllers/logging/console-sink-controller.ts +19 -9
- package/src/controllers/logging/file-sink-controller.ts +17 -6
- package/src/controllers/module/import-controller.ts +50 -27
- package/src/controllers/resource-definition/resource-template-controller.ts +14 -11
- package/src/effect-scope.ts +340 -0
- package/src/evaluation-context.ts +165 -10
- package/src/init-failure-diagnostics.ts +8 -1
- package/src/kernel.ts +20 -11
- package/src/manifest-schemas.ts +4 -9
- package/src/module-context.ts +16 -0
- package/src/observed-state.ts +2 -4
- package/src/resource-context.ts +32 -0
- package/src/schema-compiled-values.ts +35 -21
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { RuntimeError } from "@telorun/sdk";
|
|
2
|
+
/** One line for an inverse's refusal, quoted into a recovery aggregate. */
|
|
3
|
+
function errorText(err) {
|
|
4
|
+
return err instanceof Error ? err.message : String(err);
|
|
5
|
+
}
|
|
6
|
+
function isAsyncGenerator(value) {
|
|
7
|
+
return (typeof value === "object" &&
|
|
8
|
+
value !== null &&
|
|
9
|
+
typeof value.next === "function" &&
|
|
10
|
+
Symbol.asyncIterator in value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A lazy chain of steps against one scope.
|
|
14
|
+
*
|
|
15
|
+
* Immutable and value-like, so `chain.effect(...)` in a branch or a loop builds
|
|
16
|
+
* a new description rather than mutating a shared one. Deliberately NOT a
|
|
17
|
+
* thenable: an `async init()` would unwrap it and hand the kernel its last
|
|
18
|
+
* result instead of the chain.
|
|
19
|
+
*/
|
|
20
|
+
class Chain {
|
|
21
|
+
scope;
|
|
22
|
+
steps;
|
|
23
|
+
constructor(scope, steps) {
|
|
24
|
+
this.scope = scope;
|
|
25
|
+
this.steps = steps;
|
|
26
|
+
}
|
|
27
|
+
effect(reason, body) {
|
|
28
|
+
return new Chain(this.scope, [
|
|
29
|
+
...this.steps,
|
|
30
|
+
{ reason, body: body },
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
perform() {
|
|
34
|
+
return this.scope.execute(this.steps);
|
|
35
|
+
}
|
|
36
|
+
/** The steps, for the kernel executing a chain a controller returned. */
|
|
37
|
+
plan() {
|
|
38
|
+
return this.steps;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** A chain produced by this kernel, as opposed to any other object a controller
|
|
42
|
+
* might return from `init()`. */
|
|
43
|
+
export function isEffectChain(value) {
|
|
44
|
+
return value instanceof Chain;
|
|
45
|
+
}
|
|
46
|
+
export class EffectScope {
|
|
47
|
+
label;
|
|
48
|
+
/** Innermost last. `create` is opened with the scope, so there is always a
|
|
49
|
+
* frame to register onto. */
|
|
50
|
+
frames = [];
|
|
51
|
+
/** Set while this scope is unwinding. A generator body checks it at each step
|
|
52
|
+
* boundary and stops rather than allocating into a scope that is going away —
|
|
53
|
+
* the mid-boot SIGINT case. */
|
|
54
|
+
unwinding = false;
|
|
55
|
+
/** Terminal: set by {@link unwindAll}. A closed scope has no frames and takes
|
|
56
|
+
* no new effects. */
|
|
57
|
+
closed = false;
|
|
58
|
+
constructor(label) {
|
|
59
|
+
this.label = label;
|
|
60
|
+
this.openFrame("create");
|
|
61
|
+
}
|
|
62
|
+
/** Refuse work against a scope that has already unwound. Raised BEFORE a
|
|
63
|
+
* forward body runs, so a late effect cannot allocate and then find it has
|
|
64
|
+
* nowhere to record the inverse. */
|
|
65
|
+
assertOpen(what) {
|
|
66
|
+
if (!this.closed)
|
|
67
|
+
return;
|
|
68
|
+
throw new RuntimeError("ERR_EFFECT_SCOPE_CLOSED", `${this.label}: '${what}' cannot run — this resource has been torn down. ` +
|
|
69
|
+
`An effect registered now would record an inverse nothing will ever run.`);
|
|
70
|
+
}
|
|
71
|
+
openFrame(label) {
|
|
72
|
+
this.frames.push({ label, entries: [] });
|
|
73
|
+
}
|
|
74
|
+
/** Start a chain. Nothing runs until it is executed. */
|
|
75
|
+
chain(reason, body) {
|
|
76
|
+
return new Chain(this, []).effect(reason, body);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Register an inverse for work performed elsewhere, without a forward body.
|
|
80
|
+
*
|
|
81
|
+
* The synchronous door onto the same accumulator, for a primitive that
|
|
82
|
+
* already returns its own inverse: `acquireHold` hands back a release
|
|
83
|
+
* closure, and its public signature is synchronous, so it cannot go through a
|
|
84
|
+
* chain.
|
|
85
|
+
*/
|
|
86
|
+
register(reason, inverse) {
|
|
87
|
+
this.assertOpen(reason);
|
|
88
|
+
const entry = { reason, inverse, disposed: false };
|
|
89
|
+
this.current().entries.push(entry);
|
|
90
|
+
return () => this.disposeEntries([entry]);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Run a chain's steps in order against the frame open NOW, threading each
|
|
94
|
+
* step's result into the next.
|
|
95
|
+
*
|
|
96
|
+
* A step that throws leaves every inverse produced so far on the frame — this
|
|
97
|
+
* does not unwind, because whether a partial `init()` is recovered-and-retried
|
|
98
|
+
* or torn down is the caller's decision, not this function's.
|
|
99
|
+
*/
|
|
100
|
+
async execute(steps) {
|
|
101
|
+
const registered = [];
|
|
102
|
+
const push = (reason, inverse) => {
|
|
103
|
+
const entry = { reason, inverse, disposed: false };
|
|
104
|
+
this.current().entries.push(entry);
|
|
105
|
+
registered.push(entry);
|
|
106
|
+
};
|
|
107
|
+
let value = undefined;
|
|
108
|
+
for (const step of steps) {
|
|
109
|
+
this.assertOpen(step.reason);
|
|
110
|
+
if (this.unwinding) {
|
|
111
|
+
throw new RuntimeError("ERR_EFFECT_SCOPE_CLOSING", `${this.label}: '${step.reason}' not started because the resource is being torn down`);
|
|
112
|
+
}
|
|
113
|
+
const produced = step.body(value);
|
|
114
|
+
if (isAsyncGenerator(produced)) {
|
|
115
|
+
const iterator = produced;
|
|
116
|
+
for (;;) {
|
|
117
|
+
if (this.unwinding) {
|
|
118
|
+
// Stop at the step boundary and let the generator run its own
|
|
119
|
+
// `finally`. What already yielded stays on the frame and is
|
|
120
|
+
// recovered by the unwind in progress.
|
|
121
|
+
await iterator.return?.(undefined);
|
|
122
|
+
throw new RuntimeError("ERR_EFFECT_SCOPE_CLOSING", `${this.label}: '${step.reason}' stopped because the resource is being torn down`);
|
|
123
|
+
}
|
|
124
|
+
const next = await iterator.next();
|
|
125
|
+
if (next.done) {
|
|
126
|
+
value = next.value;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
push(step.reason, next.value);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const outcome = (await produced);
|
|
134
|
+
// No inverse means the step allocated nothing that outlives a failure —
|
|
135
|
+
// a chain is the sequencing structure for lifecycle work as well as the
|
|
136
|
+
// record of what to undo, so a step with nothing to undo registers
|
|
137
|
+
// nothing rather than a no-op that would read as an oversight.
|
|
138
|
+
if (outcome.inverse)
|
|
139
|
+
push(step.reason, outcome.inverse);
|
|
140
|
+
value = outcome.result;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { result: value, dispose: () => this.disposeEntries(registered) };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Unwind the innermost frame and close it, returning what refused.
|
|
147
|
+
*
|
|
148
|
+
* Failures are returned rather than thrown: the caller decides what a refusal
|
|
149
|
+
* means. Pre-retry recovery withholds the resource (retrying from a state that
|
|
150
|
+
* could not be rolled back is worse than not retrying); teardown aggregates
|
|
151
|
+
* and keeps going, so one throwing resource cannot strand the log sinks pinned
|
|
152
|
+
* to outlive it.
|
|
153
|
+
*/
|
|
154
|
+
async unwindFrame() {
|
|
155
|
+
const frame = this.frames.pop();
|
|
156
|
+
if (!frame)
|
|
157
|
+
return [];
|
|
158
|
+
return this.runInverses(frame.entries);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Unwind every open frame, innermost first, and CLOSE the scope.
|
|
162
|
+
*
|
|
163
|
+
* Terminal, because both callers are: teardown, and the discard of a resource
|
|
164
|
+
* whose `init()` failed (its replacement is built with a fresh context, so a
|
|
165
|
+
* fresh scope). A closed scope refuses new effects rather than accepting them
|
|
166
|
+
* onto a frame nothing will ever unwind — recording an inverse that will never
|
|
167
|
+
* run is the silent leak this whole mechanism exists to remove, and it is
|
|
168
|
+
* exactly the shape a detached task still settling after teardown produces.
|
|
169
|
+
*/
|
|
170
|
+
async unwindAll() {
|
|
171
|
+
const failures = [];
|
|
172
|
+
while (this.frames.length > 0)
|
|
173
|
+
failures.push(...(await this.unwindFrame()));
|
|
174
|
+
this.closed = true;
|
|
175
|
+
return failures;
|
|
176
|
+
}
|
|
177
|
+
async runInverses(entries) {
|
|
178
|
+
const wasUnwinding = this.unwinding;
|
|
179
|
+
this.unwinding = true;
|
|
180
|
+
const failures = [];
|
|
181
|
+
for (const entry of [...entries].reverse()) {
|
|
182
|
+
if (entry.disposed)
|
|
183
|
+
continue;
|
|
184
|
+
entry.disposed = true;
|
|
185
|
+
try {
|
|
186
|
+
await entry.inverse();
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
failures.push({ reason: entry.reason, error });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
this.unwinding = wasUnwinding;
|
|
193
|
+
return failures;
|
|
194
|
+
}
|
|
195
|
+
async disposeEntries(entries) {
|
|
196
|
+
const failures = await this.runInverses(entries);
|
|
197
|
+
if (failures.length === 0)
|
|
198
|
+
return;
|
|
199
|
+
// An explicit dispose HAS a caller, unlike an unwind — so it throws rather
|
|
200
|
+
// than being collected into someone else's aggregate. Every refusal travels,
|
|
201
|
+
// matching the init and teardown paths: a dispose covers a chain, so
|
|
202
|
+
// reporting the first and dropping the rest would hide the others exactly
|
|
203
|
+
// where more than one thing failed to roll back.
|
|
204
|
+
throw new RuntimeError("ERR_EFFECT_RECOVERY_FAILED", `${this.label}: ${failures.length} inverse(s) refused: ` +
|
|
205
|
+
failures.map((f) => `'${f.reason}' (${errorText(f.error)})`).join(", "), failures.map((f) => ({
|
|
206
|
+
severity: "error",
|
|
207
|
+
message: `inverse '${f.reason}' refused: ${errorText(f.error)}`,
|
|
208
|
+
})));
|
|
209
|
+
}
|
|
210
|
+
current() {
|
|
211
|
+
this.assertOpen("effect");
|
|
212
|
+
const frame = this.frames[this.frames.length - 1];
|
|
213
|
+
if (!frame)
|
|
214
|
+
throw new RuntimeError("ERR_EFFECT_NO_FRAME", `${this.label}: no open effect frame`);
|
|
215
|
+
return frame;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Execute whatever a lifecycle method returned.
|
|
220
|
+
*
|
|
221
|
+
* A controller that allocates nothing returns nothing, so a non-chain return is
|
|
222
|
+
* not an error — but it is also not an inverse, which is why `init()` returning
|
|
223
|
+
* a chain it forgot to hand back fails loudly and immediately: nothing was
|
|
224
|
+
* allocated at all.
|
|
225
|
+
*/
|
|
226
|
+
export async function executeReturnedChain(returned, scope) {
|
|
227
|
+
if (!isEffectChain(returned))
|
|
228
|
+
return;
|
|
229
|
+
if (!scope) {
|
|
230
|
+
throw new RuntimeError("ERR_EFFECT_NO_SCOPE", "a controller returned an effect chain from a resource with no effect scope");
|
|
231
|
+
}
|
|
232
|
+
await scope.execute(returned.plan());
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* An instance's effect owner, recorded at the kernel's single
|
|
236
|
+
* instance-production site — the anchor that already carries handle minting and
|
|
237
|
+
* contract binding, so an instance is never observable without one.
|
|
238
|
+
*
|
|
239
|
+
* A WeakMap rather than a field on the resource-instances map: teardown holds
|
|
240
|
+
* the instance and nothing else, and every reader of that map would otherwise
|
|
241
|
+
* have to learn about effects. First bind wins, mirroring handle minting: a
|
|
242
|
+
* `base:` child IS the parent instance returned verbatim, and re-binding would
|
|
243
|
+
* give one object two accumulators.
|
|
244
|
+
*/
|
|
245
|
+
const owners = new WeakMap();
|
|
246
|
+
export function bindEffectOwner(instance, owner) {
|
|
247
|
+
if (!owners.has(instance))
|
|
248
|
+
owners.set(instance, owner);
|
|
249
|
+
}
|
|
250
|
+
export function effectOwnerOf(instance) {
|
|
251
|
+
return owners.get(instance);
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=effect-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect-scope.js","sourceRoot":"","sources":["../src/effect-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,12 +50,27 @@ export declare class EvaluationContext implements IEvaluationContext {
|
|
|
50
50
|
resource: ResourceManifest;
|
|
51
51
|
instance: ResourceInstance;
|
|
52
52
|
}>;
|
|
53
|
-
/** Resources that have been created but not yet initialized (between phases).
|
|
53
|
+
/** Resources that have been created but not yet initialized (between phases).
|
|
54
|
+
* `source` is the manifest as REGISTERED, kept so a discarded instance is
|
|
55
|
+
* rebuilt from what the author wrote rather than from the create-time
|
|
56
|
+
* expansion of it. */
|
|
54
57
|
protected readonly createdInstances: Map<string, {
|
|
55
58
|
resource: ResourceManifest;
|
|
56
59
|
instance: ResourceInstance;
|
|
57
60
|
ctx: any;
|
|
61
|
+
source: ResourceManifest;
|
|
58
62
|
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Resources whose failed `init()` could not be rolled back.
|
|
65
|
+
*
|
|
66
|
+
* Withheld rather than retried: re-running `init()` from a state an inverse
|
|
67
|
+
* refused to restore is worse than not retrying, so the loop skips them and
|
|
68
|
+
* they are reported with both the init error and the refusing inverse.
|
|
69
|
+
*/
|
|
70
|
+
private readonly withheldResources;
|
|
71
|
+
/** Resources discarded after a failed `init()` and re-queued for creation.
|
|
72
|
+
* Their re-creation is not progress — see the create sub-phase. */
|
|
73
|
+
private readonly recreatedResources;
|
|
59
74
|
/** Resources queued for initialization on this context node. */
|
|
60
75
|
private pendingResources;
|
|
61
76
|
/**
|
|
@@ -178,6 +193,16 @@ export declare class EvaluationContext implements IEvaluationContext {
|
|
|
178
193
|
* Attach a child context to this node. The child's parent is set to this
|
|
179
194
|
* context and the child is registered under the given name.
|
|
180
195
|
*/
|
|
196
|
+
/**
|
|
197
|
+
* Detach a child from this node — the inverse of {@link spawnChild}.
|
|
198
|
+
*
|
|
199
|
+
* A child is normally torn down in place, so this exists for the one case
|
|
200
|
+
* that discards it instead: an import whose instance is dropped and rebuilt
|
|
201
|
+
* must not leave its child in `children`, or this context's teardown cascades
|
|
202
|
+
* into a context whose replacement is already live. Here rather than in the
|
|
203
|
+
* controller, because how children are tracked is this class's own fact.
|
|
204
|
+
*/
|
|
205
|
+
detachChild(child: IEvaluationContext): void;
|
|
181
206
|
spawnChild<T extends IEvaluationContext>(child: T): T;
|
|
182
207
|
/** Spawn a fresh child context attached to this node — the isolated scope a
|
|
183
208
|
* templated definition registers its `resources:` into. Rooting it on the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,WAAW,EAEX,KAAK,iBAAiB,IAAI,kBAAkB,EAC5C,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAGlB,KAAK,WAAW,EAChB,KAAK,MAAM,EACZ,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"evaluation-context.d.ts","sourceRoot":"","sources":["../src/evaluation-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,8 @@ import { formatSpanCounter } from "./logging/span-id.js";
|
|
|
3
3
|
import { deriveContext, getRefIdentity, isCompiledValue, isInvokeError, isCancellationError, isSuspension, resourceKey, UNCANCELLABLE_CONTEXT, } from "@telorun/sdk";
|
|
4
4
|
import { RuntimeError } from "@telorun/sdk";
|
|
5
5
|
import { evalPathCovers } from "@telorun/analyzer";
|
|
6
|
-
import {
|
|
6
|
+
import { effectOwnerOf, executeReturnedChain } from "./effect-scope.js";
|
|
7
|
+
import { classifyInitFailures, isDeferral, renderInitFailureText, summarizeInitFailures, } from "./init-failure-diagnostics.js";
|
|
7
8
|
import { acceptReportedStatus, buildPublishedProps, diagnoseObservedStateAccess, } from "./observed-state.js";
|
|
8
9
|
export { resourceKey };
|
|
9
10
|
/** A pure resolved reference is a `{ kind, name, alias? }` object and nothing
|
|
@@ -398,8 +399,22 @@ export class EvaluationContext {
|
|
|
398
399
|
state = "Pending";
|
|
399
400
|
/** Resource instances owned by this context node, keyed by resourceKey(). */
|
|
400
401
|
resourceInstances = new Map();
|
|
401
|
-
/** Resources that have been created but not yet initialized (between phases).
|
|
402
|
+
/** Resources that have been created but not yet initialized (between phases).
|
|
403
|
+
* `source` is the manifest as REGISTERED, kept so a discarded instance is
|
|
404
|
+
* rebuilt from what the author wrote rather than from the create-time
|
|
405
|
+
* expansion of it. */
|
|
402
406
|
createdInstances = new Map();
|
|
407
|
+
/**
|
|
408
|
+
* Resources whose failed `init()` could not be rolled back.
|
|
409
|
+
*
|
|
410
|
+
* Withheld rather than retried: re-running `init()` from a state an inverse
|
|
411
|
+
* refused to restore is worse than not retrying, so the loop skips them and
|
|
412
|
+
* they are reported with both the init error and the refusing inverse.
|
|
413
|
+
*/
|
|
414
|
+
withheldResources = new Set();
|
|
415
|
+
/** Resources discarded after a failed `init()` and re-queued for creation.
|
|
416
|
+
* Their re-creation is not progress — see the create sub-phase. */
|
|
417
|
+
recreatedResources = new Set();
|
|
403
418
|
/** Resources queued for initialization on this context node. */
|
|
404
419
|
pendingResources = [];
|
|
405
420
|
/**
|
|
@@ -614,6 +629,20 @@ export class EvaluationContext {
|
|
|
614
629
|
* Attach a child context to this node. The child's parent is set to this
|
|
615
630
|
* context and the child is registered under the given name.
|
|
616
631
|
*/
|
|
632
|
+
/**
|
|
633
|
+
* Detach a child from this node — the inverse of {@link spawnChild}.
|
|
634
|
+
*
|
|
635
|
+
* A child is normally torn down in place, so this exists for the one case
|
|
636
|
+
* that discards it instead: an import whose instance is dropped and rebuilt
|
|
637
|
+
* must not leave its child in `children`, or this context's teardown cascades
|
|
638
|
+
* into a context whose replacement is already live. Here rather than in the
|
|
639
|
+
* controller, because how children are tracked is this class's own fact.
|
|
640
|
+
*/
|
|
641
|
+
detachChild(child) {
|
|
642
|
+
const index = this.children.indexOf(child);
|
|
643
|
+
if (index >= 0)
|
|
644
|
+
this.children.splice(index, 1);
|
|
645
|
+
}
|
|
617
646
|
spawnChild(child) {
|
|
618
647
|
child.parent = this;
|
|
619
648
|
this.children.push(child);
|
|
@@ -698,12 +727,20 @@ export class EvaluationContext {
|
|
|
698
727
|
resource: created.resource,
|
|
699
728
|
instance: created.instance,
|
|
700
729
|
ctx: created.ctx,
|
|
730
|
+
source: resource,
|
|
701
731
|
});
|
|
702
732
|
const idx = this.pendingResources.findIndex((m) => m.metadata.name === name);
|
|
703
733
|
if (idx >= 0)
|
|
704
734
|
this.pendingResources.splice(idx, 1);
|
|
705
735
|
errors.delete(name);
|
|
706
|
-
progress
|
|
736
|
+
// A FIRST creation is progress; RE-creating a resource whose init
|
|
737
|
+
// failed is not. Counting it would keep the loop alive on a
|
|
738
|
+
// permanently failing resource for every remaining pass, re-running
|
|
739
|
+
// `create()` each time — ten module loads for a broken import, ten
|
|
740
|
+
// pools for an unreachable database. Its retry still happens; what
|
|
741
|
+
// it no longer does is claim the environment moved.
|
|
742
|
+
if (!this.recreatedResources.has(name))
|
|
743
|
+
progress = true;
|
|
707
744
|
const createdRes = created.resource;
|
|
708
745
|
const refs = collectResourceRefs(createdRes);
|
|
709
746
|
this.resourceDependencies.set(name, localDependencyNames(refs));
|
|
@@ -738,8 +775,8 @@ export class EvaluationContext {
|
|
|
738
775
|
}
|
|
739
776
|
}
|
|
740
777
|
// Init sub-phase
|
|
741
|
-
for (const [name, { resource, instance, ctx }] of [...this.createdInstances]) {
|
|
742
|
-
if (this.resourceInstances.has(name))
|
|
778
|
+
for (const [name, { resource, instance, ctx, source }] of [...this.createdInstances]) {
|
|
779
|
+
if (this.resourceInstances.has(name) || this.withheldResources.has(name))
|
|
743
780
|
continue;
|
|
744
781
|
try {
|
|
745
782
|
if (this.preInitHook) {
|
|
@@ -747,8 +784,67 @@ export class EvaluationContext {
|
|
|
747
784
|
? this.resolveImportedInstance(alias, n)
|
|
748
785
|
: this.resourceInstances.get(n)?.instance, (n) => this.hasManifest(n) && !this.resourceInstances.has(n), this);
|
|
749
786
|
}
|
|
750
|
-
|
|
751
|
-
|
|
787
|
+
const scope = effectOwnerOf(instance)?.effects;
|
|
788
|
+
scope?.openFrame("init");
|
|
789
|
+
try {
|
|
790
|
+
// What `init()` RETURNS is what undoes it. A controller that
|
|
791
|
+
// allocates nothing returns nothing; there is no `teardown()`, so an
|
|
792
|
+
// allocation outside the chain is one nothing will reclaim.
|
|
793
|
+
if (instance.init)
|
|
794
|
+
await executeReturnedChain(await instance.init(ctx), scope);
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
// Recover before the next pass: the loop retries a failed init, and
|
|
798
|
+
// an init that registered a listener before it failed to connect
|
|
799
|
+
// would otherwise register that listener again on every pass.
|
|
800
|
+
//
|
|
801
|
+
// HOW MUCH unwinds depends on whether the instance survives. A
|
|
802
|
+
// deferral keeps it, so only the init frame goes — unwinding the
|
|
803
|
+
// create frame there would destroy what construction built (a
|
|
804
|
+
// connection's pool) and then re-init the same instance against it.
|
|
805
|
+
// A real failure discards the instance, so everything `create()`
|
|
806
|
+
// allocated on its behalf goes with it or nothing reclaims it.
|
|
807
|
+
const deferred = isDeferral(error);
|
|
808
|
+
const refused = (await (deferred ? scope?.unwindFrame() : scope?.unwindAll())) ?? [];
|
|
809
|
+
if (refused.length > 0) {
|
|
810
|
+
// Retrying from a state that could not be rolled back is worse
|
|
811
|
+
// than not retrying, so the resource is withheld with a cause
|
|
812
|
+
// naming both the init error and the inverse that refused. The
|
|
813
|
+
// entry stays in `createdInstances` so it is still reported as a
|
|
814
|
+
// failure; the loop skips it from here on.
|
|
815
|
+
this.withheldResources.add(name);
|
|
816
|
+
throw new RuntimeError("ERR_EFFECT_RECOVERY_FAILED", `${resource.kind} '${name}' failed to initialize and could not be rolled back: ` +
|
|
817
|
+
`${refused.map((f) => `'${f.reason}' (${errorText(f.error)})`).join(", ")}`, [
|
|
818
|
+
{ severity: "error", message: errorText(error), resource: name },
|
|
819
|
+
...refused.map((f) => ({
|
|
820
|
+
severity: "error",
|
|
821
|
+
message: `inverse '${f.reason}' refused: ${errorText(f.error)}`,
|
|
822
|
+
resource: name,
|
|
823
|
+
})),
|
|
824
|
+
]);
|
|
825
|
+
}
|
|
826
|
+
// A DEFERRAL is not a failure: it is the loop's own "your turn has
|
|
827
|
+
// not come" signal, raised when a ref names a resource that has not
|
|
828
|
+
// initialized yet. The instance stays — re-creating it would re-run
|
|
829
|
+
// `create()`, which for an import re-registers its alias and reloads
|
|
830
|
+
// its module, and for a template re-registers its children. Its init
|
|
831
|
+
// frame alone was unwound above, so the next pass re-inits a
|
|
832
|
+
// constructed resource rather than a dismantled one.
|
|
833
|
+
if (deferred)
|
|
834
|
+
throw error;
|
|
835
|
+
// The inverses restored what init() touched OUTSIDE the instance;
|
|
836
|
+
// the instance's own half-built fields are beyond their reach, so
|
|
837
|
+
// the object goes too and the next pass builds a fresh one. That is
|
|
838
|
+
// what makes "retry from a clean state" literal rather than a
|
|
839
|
+
// convention each controller has to honour.
|
|
840
|
+
this.createdInstances.delete(name);
|
|
841
|
+
this.recreatedResources.add(name);
|
|
842
|
+
// Re-queued as REGISTERED, not as created: the create-time manifest
|
|
843
|
+
// has already been through compile-field expansion, and expanding
|
|
844
|
+
// it a second time would evaluate an author's expression twice.
|
|
845
|
+
this.pendingResources.push(source);
|
|
846
|
+
throw error;
|
|
847
|
+
}
|
|
752
848
|
// Publish BEFORE registering: publication can fail (a kind returning
|
|
753
849
|
// the reserved `status` key without declaring it, a report that does
|
|
754
850
|
// not match `status:`), and a resource that failed must not be left
|
|
@@ -956,9 +1052,26 @@ export class EvaluationContext {
|
|
|
956
1052
|
reportedStatus.delete(instance);
|
|
957
1053
|
startedInstances.delete(instance);
|
|
958
1054
|
completedInstances.delete(instance);
|
|
1055
|
+
// Tearing a resource down IS unwinding its effects — every frame, newest
|
|
1056
|
+
// first, LIFO within each. There is no `teardown()` to call: what undoes a
|
|
1057
|
+
// resource is what its `init()` and `run()` returned.
|
|
1058
|
+
const owner = effectOwnerOf(instance);
|
|
1059
|
+
const refused = (await owner?.effects.unwindAll()) ?? [];
|
|
1060
|
+
if (refused.length > 0) {
|
|
1061
|
+
// Aggregate and continue: one refusing inverse must not strand the log
|
|
1062
|
+
// sinks pinned last to outlive everything that might log on the way
|
|
1063
|
+
// down.
|
|
1064
|
+
failures.push({
|
|
1065
|
+
resource: label,
|
|
1066
|
+
error: new RuntimeError("ERR_EFFECT_RECOVERY_FAILED", `${refused.length} inverse(s) refused: ` +
|
|
1067
|
+
refused.map((f) => `'${f.reason}' (${errorText(f.error)})`).join(", ")),
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
// A drain waits for in-flight background work under a bound and then
|
|
1071
|
+
// abandons it — not an inverse, so it is not on a frame; it runs here,
|
|
1072
|
+
// once the resource's own effects are undone.
|
|
959
1073
|
try {
|
|
960
|
-
|
|
961
|
-
await instance.teardown();
|
|
1074
|
+
await owner?.drainDetached();
|
|
962
1075
|
}
|
|
963
1076
|
catch (err) {
|
|
964
1077
|
// Aggregate rather than abort. A single throwing resource used to
|
|
@@ -1357,12 +1470,26 @@ export class EvaluationContext {
|
|
|
1357
1470
|
// the whole point — `ctx.setStatus()` is an error until the resource counts
|
|
1358
1471
|
// as started, so this has to happen first.
|
|
1359
1472
|
await this.markStarted(name, instance);
|
|
1473
|
+
// A frame of its own: what `run()` allocates (a listening socket, a kernel
|
|
1474
|
+
// hold) is undone when the run's frame unwinds, without disturbing what
|
|
1475
|
+
// `init()` built underneath it.
|
|
1476
|
+
const effects = effectOwnerOf(instance)?.effects;
|
|
1477
|
+
effects?.openFrame("run");
|
|
1360
1478
|
try {
|
|
1361
1479
|
// Runnable: run inside the ALS scope so nested invokes inherit the token and
|
|
1362
1480
|
// trace id (skip the redundant `run` when the token is already ambient).
|
|
1363
1481
|
// Service: call directly with the explicit context and NO ambient scope, so
|
|
1364
1482
|
// its long-lived async work does not capture this scope.
|
|
1365
|
-
|
|
1483
|
+
// What `run()` returns is what undoes it — the socket it opened, the hold
|
|
1484
|
+
// it took. The chain executes INSIDE the same ambient scope as the call
|
|
1485
|
+
// that produced it: its bodies are the work `run()` would otherwise have
|
|
1486
|
+
// done inline, so running them outside would silently strip the
|
|
1487
|
+
// cancellation token and trace parent from exactly the code that moved
|
|
1488
|
+
// into a chain.
|
|
1489
|
+
const call = async () => {
|
|
1490
|
+
const returned = await instance.run(invokeCtx);
|
|
1491
|
+
await executeReturnedChain(returned, effects);
|
|
1492
|
+
};
|
|
1366
1493
|
await (isService || invokeCtx === ambient ? call() : cancellationStore.run(invokeCtx, call));
|
|
1367
1494
|
// A one-shot Runnable that discovered something during run() publishes it
|
|
1368
1495
|
// without an explicit call; a Service never reaches this until teardown.
|
|
@@ -1371,15 +1498,28 @@ export class EvaluationContext {
|
|
|
1371
1498
|
await this.emit(`${name}.Run`, span("end", "ok", {}));
|
|
1372
1499
|
}
|
|
1373
1500
|
catch (err) {
|
|
1501
|
+
// A run that did not complete leaves nothing of its own behind: its frame
|
|
1502
|
+
// unwinds here, so a `listen()` that threw releases the hold it took a
|
|
1503
|
+
// line earlier instead of holding the process open for a server that
|
|
1504
|
+
// never came up.
|
|
1505
|
+
const refused = (await effects?.unwindFrame()) ?? [];
|
|
1506
|
+
const recovery = refused.length > 0
|
|
1507
|
+
? {
|
|
1508
|
+
recoveryFailures: refused.map((f) => ({
|
|
1509
|
+
reason: f.reason,
|
|
1510
|
+
message: errorText(f.error),
|
|
1511
|
+
})),
|
|
1512
|
+
}
|
|
1513
|
+
: {};
|
|
1374
1514
|
if (isCancellationError(err)) {
|
|
1375
1515
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1376
|
-
await this.emit(`${name}.RunCancelled`, span("end", "cancelled", { reason }));
|
|
1516
|
+
await this.emit(`${name}.RunCancelled`, span("end", "cancelled", { reason, ...recovery }));
|
|
1377
1517
|
throw err;
|
|
1378
1518
|
}
|
|
1379
1519
|
const detail = err instanceof Error
|
|
1380
1520
|
? { name: err.name, message: err.message }
|
|
1381
1521
|
: { name: "UnknownError", message: String(err) };
|
|
1382
|
-
await this.emit(`${name}.RunFailed`, span("end", "failed", detail));
|
|
1522
|
+
await this.emit(`${name}.RunFailed`, span("end", "failed", { ...detail, ...recovery }));
|
|
1383
1523
|
throw err;
|
|
1384
1524
|
}
|
|
1385
1525
|
}
|
|
@@ -1714,6 +1854,10 @@ function locateFailedAccess(source, ctx, msg) {
|
|
|
1714
1854
|
}
|
|
1715
1855
|
return null;
|
|
1716
1856
|
}
|
|
1857
|
+
/** One line for an inverse's refusal, quoted into a recovery aggregate. */
|
|
1858
|
+
function errorText(err) {
|
|
1859
|
+
return err instanceof Error ? err.message : String(err);
|
|
1860
|
+
}
|
|
1717
1861
|
function describeMissingAccess(value, key) {
|
|
1718
1862
|
if (value === null)
|
|
1719
1863
|
return `cannot read '${key}' — value is null`;
|