@telorun/sdk 0.77.0 → 0.80.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/cancellation.d.ts +50 -1
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/contract-errors.d.ts +8 -1
- package/dist/contract-errors.d.ts.map +1 -1
- package/dist/contract-errors.js +8 -0
- package/dist/durable-run.d.ts +310 -0
- package/dist/durable-run.d.ts.map +1 -0
- package/dist/durable-run.js +223 -0
- package/dist/durable-suspension.d.ts +143 -0
- package/dist/durable-suspension.d.ts.map +1 -0
- package/dist/durable-suspension.js +153 -0
- package/dist/durable-target-encoding.d.ts +49 -0
- package/dist/durable-target-encoding.d.ts.map +1 -0
- package/dist/durable-target-encoding.js +121 -0
- package/dist/duration.d.ts +1 -1
- package/dist/duration.js +5 -5
- package/dist/evaluation-context.d.ts +16 -0
- package/dist/evaluation-context.d.ts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/invoke-step.d.ts +86 -1
- package/dist/invoke-step.d.ts.map +1 -1
- package/dist/invoke-step.js +261 -18
- package/dist/resource-context.d.ts +37 -0
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-instance.d.ts +21 -1
- package/dist/resource-instance.d.ts.map +1 -1
- package/dist/resource-instance.js +6 -2
- package/dist/step-engine.d.ts +170 -0
- package/dist/step-engine.d.ts.map +1 -0
- package/dist/step-engine.js +365 -0
- package/dist/zone-attribute.d.ts +101 -0
- package/dist/zone-attribute.d.ts.map +1 -0
- package/dist/zone-attribute.js +130 -0
- package/dist/zone-attributes/entries/atomic.json +7 -0
- package/dist/zone-attributes/entries/idempotent.json +6 -0
- package/dist/zone-attributes/entries/index.d.ts +3 -0
- package/dist/zone-attributes/entries/index.d.ts.map +1 -0
- package/dist/zone-attributes/entries/index.js +13 -0
- package/dist/zone-attributes/entries/no-suspend.json +6 -0
- package/dist/zone-attributes/entries/replayed.json +6 -0
- package/package.json +1 -1
- package/src/cancellation.ts +50 -1
- package/src/contract-errors.ts +9 -0
- package/src/durable-run.ts +450 -0
- package/src/durable-suspension.ts +188 -0
- package/src/durable-target-encoding.ts +181 -0
- package/src/duration.ts +5 -5
- package/src/evaluation-context.ts +17 -0
- package/src/index.ts +5 -1
- package/src/invoke-step.ts +378 -24
- package/src/resource-context.ts +37 -0
- package/src/resource-instance.ts +32 -2
- package/src/step-engine.ts +627 -0
- package/src/zone-attribute.ts +208 -0
- package/src/zone-attributes/entries/atomic.json +7 -0
- package/src/zone-attributes/entries/idempotent.json +6 -0
- package/src/zone-attributes/entries/index.ts +14 -0
- package/src/zone-attributes/entries/no-suspend.json +6 -0
- package/src/zone-attributes/entries/replayed.json +6 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step grammar and its execution: `invoke` / `value` / `if` / `while` /
|
|
3
|
+
* `switch` / `try` / `throw`, the `steps.<name>.result` accumulator, and the
|
|
4
|
+
* nested-scope walk that resolves an inline `invoke:` into a named resource.
|
|
5
|
+
*
|
|
6
|
+
* WHY THE SDK OWNS THIS. The leaf ({@link executeInvokeStep}) has always lived
|
|
7
|
+
* here; everything above it lived in `modules/run` for no reason anyone chose,
|
|
8
|
+
* and that is what made a step body something only `run`'s own kinds could have.
|
|
9
|
+
* `@telorun/sdk` is the single name in the bundle loader's `REALM_COLLAPSE_NAMES`
|
|
10
|
+
* — symlinked onto the KERNEL's own copy rather than inlined — so it is one
|
|
11
|
+
* version per process whatever anyone pins, and it is reachable from a controller
|
|
12
|
+
* bundle and from the kernel's own boot runner alike. A module library
|
|
13
|
+
* (`exports.code:`) is no longer copied per consumer, but it is still one scope
|
|
14
|
+
* per pinned version, it is outside the seam entirely for an npm-delivered
|
|
15
|
+
* controller, and the kernel cannot reach one at all. For a component whose
|
|
16
|
+
* contract is determinism across a durable run, one implementation is the whole
|
|
17
|
+
* premise.
|
|
18
|
+
*
|
|
19
|
+
* The context is STRUCTURAL ({@link StepEngineContext}), the property the leaf
|
|
20
|
+
* already proved: `ResourceContext` satisfies it, and so does a kernel-side
|
|
21
|
+
* adapter. Nothing here imports the kernel or `run`.
|
|
22
|
+
*/
|
|
23
|
+
import type { InvokeContext } from "./cancellation.js";
|
|
24
|
+
import { type InvokeStep, type InvokeStepContext } from "./invoke-step.js";
|
|
25
|
+
import type { KindRef, ScopeContext } from "./ref.js";
|
|
26
|
+
/**
|
|
27
|
+
* What the engine needs beyond the leaf's own contract: turning an inline
|
|
28
|
+
* `invoke: { kind, … }` into a named reference.
|
|
29
|
+
*
|
|
30
|
+
* Widened from {@link InvokeStepContext} rather than replaced, so one interface
|
|
31
|
+
* describes a step site whether or not control flow is involved. Satisfied
|
|
32
|
+
* structurally by `ResourceContext`; a host that composes steps in code supplies
|
|
33
|
+
* its own.
|
|
34
|
+
*/
|
|
35
|
+
export interface StepEngineContext extends InvokeStepContext {
|
|
36
|
+
ensureKindRef(value: any, resourceName?: string): KindRef;
|
|
37
|
+
}
|
|
38
|
+
export interface IfStep {
|
|
39
|
+
name: string;
|
|
40
|
+
if: string;
|
|
41
|
+
then: Step[];
|
|
42
|
+
elseif?: Array<{
|
|
43
|
+
if: string;
|
|
44
|
+
then: Step[];
|
|
45
|
+
}>;
|
|
46
|
+
else?: Step[];
|
|
47
|
+
}
|
|
48
|
+
export interface WhileStep {
|
|
49
|
+
name: string;
|
|
50
|
+
while: string;
|
|
51
|
+
do: Step[];
|
|
52
|
+
}
|
|
53
|
+
export interface SwitchStep {
|
|
54
|
+
name: string;
|
|
55
|
+
switch: string;
|
|
56
|
+
cases: Record<string, Step[]>;
|
|
57
|
+
default?: Step[];
|
|
58
|
+
}
|
|
59
|
+
export interface TryStep {
|
|
60
|
+
name: string;
|
|
61
|
+
when?: string;
|
|
62
|
+
try: Step[];
|
|
63
|
+
catch?: Step[];
|
|
64
|
+
finally?: Step[];
|
|
65
|
+
}
|
|
66
|
+
export interface ThrowStep {
|
|
67
|
+
name: string;
|
|
68
|
+
throw: {
|
|
69
|
+
code: string;
|
|
70
|
+
message?: string;
|
|
71
|
+
data?: unknown;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export interface ValueStep {
|
|
75
|
+
name: string;
|
|
76
|
+
value: unknown;
|
|
77
|
+
}
|
|
78
|
+
export type Step = InvokeStep | IfStep | WhileStep | SwitchStep | TryStep | ThrowStep | ValueStep;
|
|
79
|
+
/** Code assigned to any caught failure that is not a structured `InvokeError`.
|
|
80
|
+
* Guarantees `error.code` is always a non-empty string inside a `catch`, so a
|
|
81
|
+
* `throw: { code: "${{ error.code }}" }` rethrow can never resolve to null.
|
|
82
|
+
* The analyzer's throws resolver mirrors this constant. */
|
|
83
|
+
export declare const PLAIN_ERROR_CODE = "INTERNAL_ERROR";
|
|
84
|
+
/** The `error` variable a `catch:` / `catches:` branch sees. */
|
|
85
|
+
export interface SequenceError {
|
|
86
|
+
message: string;
|
|
87
|
+
code: string;
|
|
88
|
+
data?: unknown;
|
|
89
|
+
step: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Who is running this body — the two facts the generated name of an inline
|
|
93
|
+
* `invoke:` is built from.
|
|
94
|
+
*
|
|
95
|
+
* Taken as identity rather than as a finished prefix because that name is
|
|
96
|
+
* MANIFEST-VISIBLE topology: it is what `steps.<name>.result`, a trace span and
|
|
97
|
+
* an `ERR_RESOURCE_NOT_FOUND` all print. Every caller used to spell the recipe
|
|
98
|
+
* itself (`` `Iteration${pascalCase(name)}` ``), which is the half of a
|
|
99
|
+
* must-not-fork component that forked anyway — the fifth composer would get the
|
|
100
|
+
* casing subtly wrong, or collide with the fourth.
|
|
101
|
+
*/
|
|
102
|
+
export interface StepBodyOwner {
|
|
103
|
+
/** The owning kind's suffix (`Sequence`, `Iteration`, `Transaction`). */
|
|
104
|
+
kind: string;
|
|
105
|
+
/** The owning resource's `metadata.name`. */
|
|
106
|
+
resourceName: string;
|
|
107
|
+
}
|
|
108
|
+
/** Runs a step list against an `extraCtx` CEL scope, owning the full grammar —
|
|
109
|
+
* `invoke` / `value` / `if` / `while` / `switch` / `try` / `throw`. A composing
|
|
110
|
+
* kind injects its own scope variables (`item`, `index`, `iteration`,
|
|
111
|
+
* `previous`, …) through `extraCtx`; the engine knows none of them. */
|
|
112
|
+
export declare class StepEngine {
|
|
113
|
+
private readonly ctx;
|
|
114
|
+
/** Prefix for generated inline-invoke resource names; unique per host resource
|
|
115
|
+
* (`SequenceMySeq`, `LoopPollUntilReady`). */
|
|
116
|
+
private readonly namePrefix;
|
|
117
|
+
constructor(ctx: StepEngineContext, owner: StepBodyOwner);
|
|
118
|
+
resolveInvokes(stepList: Step[], path?: string[]): void;
|
|
119
|
+
private inlineInvokeResourceName;
|
|
120
|
+
/**
|
|
121
|
+
* @param path Journal key prefix for this list — see {@link stepPath}. A
|
|
122
|
+
* composer that repeats a body (an iteration element, a loop turn) qualifies
|
|
123
|
+
* it with the index, which is what makes each repetition an independently
|
|
124
|
+
* resumable subtree. Omitted, it is derived from the ambient step path, so a
|
|
125
|
+
* NESTED body nests its keys instead of restarting at the root — see
|
|
126
|
+
* {@link baseStepPath}.
|
|
127
|
+
*/
|
|
128
|
+
executeSteps(stepList: Step[], steps: Record<string, unknown>, scope: ScopeContext | undefined, extraCtx: Record<string, unknown>, invokeCtx?: InvokeContext, path?: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* The journal key of one step.
|
|
131
|
+
*
|
|
132
|
+
* Composed from the WRITTEN structure — the enclosing list's path plus this
|
|
133
|
+
* step's own name — never from execution order. A per-run call ordinal would
|
|
134
|
+
* be simpler and is wrong: two branches of a concurrent fan-out interleave
|
|
135
|
+
* their dispatches, so an ordinal numbers them differently on every run while
|
|
136
|
+
* these paths stay fixed.
|
|
137
|
+
*/
|
|
138
|
+
private pathOf;
|
|
139
|
+
/** The run handle to journal through, or undefined when this body is not
|
|
140
|
+
* inside a durable run — in which case the engine behaves exactly as it did
|
|
141
|
+
* before durability existed, and pays nothing for it. */
|
|
142
|
+
private handle;
|
|
143
|
+
/**
|
|
144
|
+
* Evaluate a control-flow decision, journaling it when a run is durable.
|
|
145
|
+
*
|
|
146
|
+
* EVERY decision goes through here, which is the closure property the whole
|
|
147
|
+
* design rests on: a predicate, a loop condition and a switch key are all read
|
|
148
|
+
* from a CEL scope carrying live readings, so re-deriving one in a fresh
|
|
149
|
+
* process can send the replay down a different branch than the run took —
|
|
150
|
+
* silently, because the journal would then hand back a recorded result under a
|
|
151
|
+
* key the run reached for a different reason.
|
|
152
|
+
*/
|
|
153
|
+
private decide;
|
|
154
|
+
private executeStep;
|
|
155
|
+
private executeIfStep;
|
|
156
|
+
private executeWhileStep;
|
|
157
|
+
private executeSwitchStep;
|
|
158
|
+
/** A pure step: expand the expression in the step scope and publish it as
|
|
159
|
+
* `steps.<name>.result`, the same shape an invoke step records — so a
|
|
160
|
+
* downstream step cannot tell how the value was produced. Nothing is
|
|
161
|
+
* dispatched, so there is no span and no topology edge. */
|
|
162
|
+
private executeValueStep;
|
|
163
|
+
private executeThrowStep;
|
|
164
|
+
private executeTryStep;
|
|
165
|
+
}
|
|
166
|
+
/** Normalize any caught failure to the `error` shape a `catch:` branch reads.
|
|
167
|
+
* Shared with the composers' whole-operation `catches:`, so one caught failure
|
|
168
|
+
* has one shape wherever it is read. */
|
|
169
|
+
export declare function toSequenceError(err: unknown, stepName: string): SequenceError;
|
|
170
|
+
//# sourceMappingURL=step-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-engine.d.ts","sourceRoot":"","sources":["../src/step-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step grammar and its execution: `invoke` / `value` / `if` / `while` /
|
|
3
|
+
* `switch` / `try` / `throw`, the `steps.<name>.result` accumulator, and the
|
|
4
|
+
* nested-scope walk that resolves an inline `invoke:` into a named resource.
|
|
5
|
+
*
|
|
6
|
+
* WHY THE SDK OWNS THIS. The leaf ({@link executeInvokeStep}) has always lived
|
|
7
|
+
* here; everything above it lived in `modules/run` for no reason anyone chose,
|
|
8
|
+
* and that is what made a step body something only `run`'s own kinds could have.
|
|
9
|
+
* `@telorun/sdk` is the single name in the bundle loader's `REALM_COLLAPSE_NAMES`
|
|
10
|
+
* — symlinked onto the KERNEL's own copy rather than inlined — so it is one
|
|
11
|
+
* version per process whatever anyone pins, and it is reachable from a controller
|
|
12
|
+
* bundle and from the kernel's own boot runner alike. A module library
|
|
13
|
+
* (`exports.code:`) is no longer copied per consumer, but it is still one scope
|
|
14
|
+
* per pinned version, it is outside the seam entirely for an npm-delivered
|
|
15
|
+
* controller, and the kernel cannot reach one at all. For a component whose
|
|
16
|
+
* contract is determinism across a durable run, one implementation is the whole
|
|
17
|
+
* premise.
|
|
18
|
+
*
|
|
19
|
+
* The context is STRUCTURAL ({@link StepEngineContext}), the property the leaf
|
|
20
|
+
* already proved: `ResourceContext` satisfies it, and so does a kernel-side
|
|
21
|
+
* adapter. Nothing here imports the kernel or `run`.
|
|
22
|
+
*/
|
|
23
|
+
import { durableHandleOf, journalingSuppressed, stepPath, } from "./durable-run.js";
|
|
24
|
+
import { isSuspension } from "./durable-suspension.js";
|
|
25
|
+
import { InvokeError, isInvokeError } from "./invoke-error.js";
|
|
26
|
+
import { executeInvokeStep } from "./invoke-step.js";
|
|
27
|
+
/** Code assigned to any caught failure that is not a structured `InvokeError`.
|
|
28
|
+
* Guarantees `error.code` is always a non-empty string inside a `catch`, so a
|
|
29
|
+
* `throw: { code: "${{ error.code }}" }` rethrow can never resolve to null.
|
|
30
|
+
* The analyzer's throws resolver mirrors this constant. */
|
|
31
|
+
export const PLAIN_ERROR_CODE = "INTERNAL_ERROR";
|
|
32
|
+
function isInvokeStep(step) {
|
|
33
|
+
return "invoke" in step;
|
|
34
|
+
}
|
|
35
|
+
function isIfStep(step) {
|
|
36
|
+
return "if" in step;
|
|
37
|
+
}
|
|
38
|
+
function isWhileStep(step) {
|
|
39
|
+
return "while" in step;
|
|
40
|
+
}
|
|
41
|
+
function isSwitchStep(step) {
|
|
42
|
+
return "switch" in step;
|
|
43
|
+
}
|
|
44
|
+
function isTryStep(step) {
|
|
45
|
+
return "try" in step;
|
|
46
|
+
}
|
|
47
|
+
function isThrowStep(step) {
|
|
48
|
+
return "throw" in step;
|
|
49
|
+
}
|
|
50
|
+
function isValueStep(step) {
|
|
51
|
+
return "value" in step;
|
|
52
|
+
}
|
|
53
|
+
/** Runs a step list against an `extraCtx` CEL scope, owning the full grammar —
|
|
54
|
+
* `invoke` / `value` / `if` / `while` / `switch` / `try` / `throw`. A composing
|
|
55
|
+
* kind injects its own scope variables (`item`, `index`, `iteration`,
|
|
56
|
+
* `previous`, …) through `extraCtx`; the engine knows none of them. */
|
|
57
|
+
export class StepEngine {
|
|
58
|
+
ctx;
|
|
59
|
+
/** Prefix for generated inline-invoke resource names; unique per host resource
|
|
60
|
+
* (`SequenceMySeq`, `LoopPollUntilReady`). */
|
|
61
|
+
namePrefix;
|
|
62
|
+
constructor(ctx, owner) {
|
|
63
|
+
this.ctx = ctx;
|
|
64
|
+
this.namePrefix = `${pascalCase(owner.kind)}${pascalCase(owner.resourceName)}`;
|
|
65
|
+
}
|
|
66
|
+
resolveInvokes(stepList, path = ["steps"]) {
|
|
67
|
+
for (const [index, step] of stepList.entries()) {
|
|
68
|
+
const stepPath = [...path, String(index)];
|
|
69
|
+
if (isInvokeStep(step)) {
|
|
70
|
+
const raw = step.invoke;
|
|
71
|
+
if (!raw || typeof raw.invoke !== "function") {
|
|
72
|
+
step.invoke = this.ctx.ensureKindRef(raw, this.inlineInvokeResourceName(step.name, stepPath));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (isIfStep(step)) {
|
|
76
|
+
this.resolveInvokes(step.then, [...stepPath, "then"]);
|
|
77
|
+
if (step.elseif) {
|
|
78
|
+
for (const [elseifIndex, branch] of step.elseif.entries()) {
|
|
79
|
+
this.resolveInvokes(branch.then, [...stepPath, "elseif", String(elseifIndex), "then"]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (step.else)
|
|
83
|
+
this.resolveInvokes(step.else, [...stepPath, "else"]);
|
|
84
|
+
}
|
|
85
|
+
if (isWhileStep(step))
|
|
86
|
+
this.resolveInvokes(step.do, [...stepPath, "do"]);
|
|
87
|
+
if (isSwitchStep(step)) {
|
|
88
|
+
for (const [caseName, branch] of Object.entries(step.cases)) {
|
|
89
|
+
this.resolveInvokes(branch, [...stepPath, "cases", caseName]);
|
|
90
|
+
}
|
|
91
|
+
if (step.default)
|
|
92
|
+
this.resolveInvokes(step.default, [...stepPath, "default"]);
|
|
93
|
+
}
|
|
94
|
+
if (isTryStep(step)) {
|
|
95
|
+
this.resolveInvokes(step.try, [...stepPath, "try"]);
|
|
96
|
+
if (step.catch)
|
|
97
|
+
this.resolveInvokes(step.catch, [...stepPath, "catch"]);
|
|
98
|
+
if (step.finally)
|
|
99
|
+
this.resolveInvokes(step.finally, [...stepPath, "finally"]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
inlineInvokeResourceName(stepName, stepPath) {
|
|
104
|
+
const path = stepPath.map(pascalCase).join("");
|
|
105
|
+
const step = pascalCase(stepName);
|
|
106
|
+
return `${this.namePrefix}${path}${step}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* @param path Journal key prefix for this list — see {@link stepPath}. A
|
|
110
|
+
* composer that repeats a body (an iteration element, a loop turn) qualifies
|
|
111
|
+
* it with the index, which is what makes each repetition an independently
|
|
112
|
+
* resumable subtree. Omitted, it is derived from the ambient step path, so a
|
|
113
|
+
* NESTED body nests its keys instead of restarting at the root — see
|
|
114
|
+
* {@link baseStepPath}.
|
|
115
|
+
*/
|
|
116
|
+
async executeSteps(stepList, steps, scope, extraCtx, invokeCtx, path) {
|
|
117
|
+
const base = path ?? baseStepPath(invokeCtx);
|
|
118
|
+
for (const step of stepList) {
|
|
119
|
+
await this.executeStep(step, steps, scope, extraCtx, invokeCtx, base);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The journal key of one step.
|
|
124
|
+
*
|
|
125
|
+
* Composed from the WRITTEN structure — the enclosing list's path plus this
|
|
126
|
+
* step's own name — never from execution order. A per-run call ordinal would
|
|
127
|
+
* be simpler and is wrong: two branches of a concurrent fan-out interleave
|
|
128
|
+
* their dispatches, so an ordinal numbers them differently on every run while
|
|
129
|
+
* these paths stay fixed.
|
|
130
|
+
*/
|
|
131
|
+
pathOf(path, step) {
|
|
132
|
+
// A missing name is refused rather than defaulted. The shared `Step` schema
|
|
133
|
+
// declares `name` required, so a manifest cannot reach this — but a caller
|
|
134
|
+
// assembling steps in code can, and an empty segment would give two such
|
|
135
|
+
// steps ONE journal key, where first-writer-wins hands the second the
|
|
136
|
+
// first's result. Silent, and indistinguishable from a correct replay.
|
|
137
|
+
if (!step.name) {
|
|
138
|
+
throw new InvokeError("ERR_STEP_NAME_REQUIRED", `A step at '${path}' has no name. A name is what identifies the step in the run's ` +
|
|
139
|
+
`record, so two unnamed steps would share one key and the second would be handed ` +
|
|
140
|
+
`the first's result.`, { path });
|
|
141
|
+
}
|
|
142
|
+
return stepPath(path, step.name);
|
|
143
|
+
}
|
|
144
|
+
/** The run handle to journal through, or undefined when this body is not
|
|
145
|
+
* inside a durable run — in which case the engine behaves exactly as it did
|
|
146
|
+
* before durability existed, and pays nothing for it. */
|
|
147
|
+
handle(invokeCtx) {
|
|
148
|
+
return durableHandleOf(invokeCtx);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Evaluate a control-flow decision, journaling it when a run is durable.
|
|
152
|
+
*
|
|
153
|
+
* EVERY decision goes through here, which is the closure property the whole
|
|
154
|
+
* design rests on: a predicate, a loop condition and a switch key are all read
|
|
155
|
+
* from a CEL scope carrying live readings, so re-deriving one in a fresh
|
|
156
|
+
* process can send the replay down a different branch than the run took —
|
|
157
|
+
* silently, because the journal would then hand back a recorded result under a
|
|
158
|
+
* key the run reached for a different reason.
|
|
159
|
+
*/
|
|
160
|
+
async decide(invokeCtx, path, kind, compute) {
|
|
161
|
+
const handle = this.handle(invokeCtx);
|
|
162
|
+
if (!handle || journalingSuppressed(this.ctx, invokeCtx, handle))
|
|
163
|
+
return compute();
|
|
164
|
+
return handle.decide(path, kind, compute);
|
|
165
|
+
}
|
|
166
|
+
async executeStep(step, steps, scope, extraCtx, invokeCtx, path = "steps") {
|
|
167
|
+
const here = this.pathOf(path, step);
|
|
168
|
+
if (isInvokeStep(step))
|
|
169
|
+
await executeInvokeStep(step, this.ctx, {
|
|
170
|
+
steps,
|
|
171
|
+
scope,
|
|
172
|
+
cel: extraCtx,
|
|
173
|
+
invokeCtx,
|
|
174
|
+
journalPath: here,
|
|
175
|
+
});
|
|
176
|
+
else if (isIfStep(step))
|
|
177
|
+
await this.executeIfStep(step, steps, scope, extraCtx, invokeCtx, here);
|
|
178
|
+
else if (isWhileStep(step))
|
|
179
|
+
await this.executeWhileStep(step, steps, scope, extraCtx, invokeCtx, here);
|
|
180
|
+
else if (isSwitchStep(step))
|
|
181
|
+
await this.executeSwitchStep(step, steps, scope, extraCtx, invokeCtx, here);
|
|
182
|
+
else if (isTryStep(step))
|
|
183
|
+
await this.executeTryStep(step, steps, scope, extraCtx, invokeCtx, here);
|
|
184
|
+
else if (isThrowStep(step))
|
|
185
|
+
this.executeThrowStep(step, steps, extraCtx);
|
|
186
|
+
else if (isValueStep(step))
|
|
187
|
+
await this.executeValueStep(step, steps, extraCtx, invokeCtx, here);
|
|
188
|
+
else
|
|
189
|
+
throw new Error(`Step "${step.name}" has no recognized type key`);
|
|
190
|
+
}
|
|
191
|
+
async executeIfStep(step, steps, scope, extraCtx, invokeCtx, path = "steps") {
|
|
192
|
+
// Each predicate is journaled under its own key, so replay takes the branch
|
|
193
|
+
// the RUN took rather than the branch the predicate would evaluate to now.
|
|
194
|
+
if (await this.decide(invokeCtx, stepPath(path, "if"), "predicate", () => this.ctx.expandValue(step.if, { steps, ...extraCtx }))) {
|
|
195
|
+
await this.executeSteps(step.then, steps, scope, extraCtx, invokeCtx, stepPath(path, "then"));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (step.elseif) {
|
|
199
|
+
for (const [index, branch] of step.elseif.entries()) {
|
|
200
|
+
if (await this.decide(invokeCtx, stepPath(path, "elseif", index), "predicate", () => this.ctx.expandValue(branch.if, { steps, ...extraCtx }))) {
|
|
201
|
+
await this.executeSteps(branch.then, steps, scope, extraCtx, invokeCtx, stepPath(path, "elseif", index, "then"));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (step.else) {
|
|
207
|
+
await this.executeSteps(step.else, steps, scope, extraCtx, invokeCtx, stepPath(path, "else"));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async executeWhileStep(step, steps, scope, extraCtx, invokeCtx, path = "steps") {
|
|
211
|
+
// The turn index qualifies both the condition's key and the body's, so each
|
|
212
|
+
// turn is an independently resumable subtree and a resume re-enters the turn
|
|
213
|
+
// it stopped in rather than restarting the loop.
|
|
214
|
+
for (let turn = 0;; turn++) {
|
|
215
|
+
const go = await this.decide(invokeCtx, stepPath(path, "while", turn), "condition", () => this.ctx.expandValue(step.while, { steps, ...extraCtx }));
|
|
216
|
+
if (!go)
|
|
217
|
+
return;
|
|
218
|
+
await this.executeSteps(step.do, steps, scope, extraCtx, invokeCtx, stepPath(path, "do", turn));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async executeSwitchStep(step, steps, scope, extraCtx, invokeCtx, path = "steps") {
|
|
222
|
+
const key = String(await this.decide(invokeCtx, stepPath(path, "switch"), "switch", () => this.ctx.expandValue(step.switch, { steps, ...extraCtx })));
|
|
223
|
+
if (Object.prototype.hasOwnProperty.call(step.cases, key)) {
|
|
224
|
+
await this.executeSteps(step.cases[key], steps, scope, extraCtx, invokeCtx, stepPath(path, "cases", key));
|
|
225
|
+
}
|
|
226
|
+
else if (step.default) {
|
|
227
|
+
await this.executeSteps(step.default, steps, scope, extraCtx, invokeCtx, stepPath(path, "default"));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
throw new Error(`Switch step "${step.name}": no matching case for "${key}" and no default`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** A pure step: expand the expression in the step scope and publish it as
|
|
234
|
+
* `steps.<name>.result`, the same shape an invoke step records — so a
|
|
235
|
+
* downstream step cannot tell how the value was produced. Nothing is
|
|
236
|
+
* dispatched, so there is no span and no topology edge. */
|
|
237
|
+
async executeValueStep(step, steps, extraCtx, invokeCtx, path = "steps") {
|
|
238
|
+
try {
|
|
239
|
+
// Journaled like any other decision: a pure step's expression may be
|
|
240
|
+
// impure (`now()`, `uuid()`), and its value becomes `steps.<name>.result`
|
|
241
|
+
// that later steps read — so re-deriving it on replay would change the
|
|
242
|
+
// run's state without any dispatch having differed. This is also what lets
|
|
243
|
+
// a `Durable.Value` work INSIDE a collapsed region: collapse suppresses
|
|
244
|
+
// per-step entries, never a direct decision.
|
|
245
|
+
const result = await this.decide(invokeCtx, path, "value", () => this.ctx.expandValue(step.value, { steps, ...extraCtx }));
|
|
246
|
+
steps[step.name] = { result };
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
// A suspension is not this step's failure — it is the run leaving —
|
|
250
|
+
// so it passes through unattributed rather than being rewritten into an
|
|
251
|
+
// InvokeError a `catches:` list could name.
|
|
252
|
+
if (isSuspension(err))
|
|
253
|
+
throw err;
|
|
254
|
+
// Attribute the failure the way every other step branch does — a bare
|
|
255
|
+
// expression error names no step, no resource and no line, which is the
|
|
256
|
+
// one thing a `catch:` and a stack trace both need.
|
|
257
|
+
const failure = toSequenceError(err, step.name);
|
|
258
|
+
throw new InvokeError(failure.code, `Step "${step.name}": ${failure.message}`, {
|
|
259
|
+
step: step.name,
|
|
260
|
+
data: failure.data,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
executeThrowStep(step, steps, extraCtx) {
|
|
265
|
+
const cel = { steps, ...extraCtx };
|
|
266
|
+
const expanded = this.ctx.expandValue(step.throw, cel);
|
|
267
|
+
const code = expanded?.code;
|
|
268
|
+
if (typeof code !== "string" || code.length === 0) {
|
|
269
|
+
// Structured error (not plain Error) so the failure stays in the InvokeError
|
|
270
|
+
// channel and a route's `catches:` list can still map it. The alternative —
|
|
271
|
+
// a plain Error — would skip catches: entirely and fall through to a 500.
|
|
272
|
+
throw new InvokeError("INVALID_THROW_STEP", `throw.code is required and must resolve to a non-empty string (step "${step.name}")`, { step: step.name, code });
|
|
273
|
+
}
|
|
274
|
+
const message = typeof expanded.message === "string" ? expanded.message : code;
|
|
275
|
+
throw new InvokeError(code, message, expanded.data);
|
|
276
|
+
}
|
|
277
|
+
async executeTryStep(step, steps, scope, extraCtx, invokeCtx, path = "steps") {
|
|
278
|
+
if (step.when !== undefined &&
|
|
279
|
+
!(await this.decide(invokeCtx, stepPath(path, "when"), "predicate", () => this.ctx.expandValue(step.when, { steps, ...extraCtx })))) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
let tryFailed = false;
|
|
283
|
+
let tryError;
|
|
284
|
+
try {
|
|
285
|
+
await this.executeSteps(step.try, steps, scope, extraCtx, invokeCtx, stepPath(path, "try"));
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
// `try:` must NOT catch a suspension. The signal unwinds to the workflow
|
|
289
|
+
// that owns the run; absorbing it here would run the `catch:` branch and
|
|
290
|
+
// then continue, converting a park into a completed step and duplicating
|
|
291
|
+
// every effect after it. The latch would catch that at the boundary, but
|
|
292
|
+
// a hard error is a worse answer than simply not swallowing it.
|
|
293
|
+
if (isSuspension(err))
|
|
294
|
+
throw err;
|
|
295
|
+
tryFailed = true;
|
|
296
|
+
tryError = err;
|
|
297
|
+
}
|
|
298
|
+
if (tryFailed) {
|
|
299
|
+
if (step.catch) {
|
|
300
|
+
const seqErr = toSequenceError(tryError, step.name);
|
|
301
|
+
try {
|
|
302
|
+
await this.executeSteps(step.catch, steps, scope, { ...extraCtx, error: seqErr }, invokeCtx, stepPath(path, "catch"));
|
|
303
|
+
}
|
|
304
|
+
catch (catchErr) {
|
|
305
|
+
if (step.finally) {
|
|
306
|
+
await this.executeSteps(step.finally, steps, scope, { ...extraCtx, error: toSequenceError(catchErr, step.name) }, invokeCtx, stepPath(path, "finally"));
|
|
307
|
+
}
|
|
308
|
+
throw catchErr;
|
|
309
|
+
}
|
|
310
|
+
if (step.finally) {
|
|
311
|
+
await this.executeSteps(step.finally, steps, scope, { ...extraCtx, error: null }, invokeCtx, stepPath(path, "finally"));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
if (step.finally) {
|
|
316
|
+
await this.executeSteps(step.finally, steps, scope, { ...extraCtx, error: toSequenceError(tryError, step.name) }, invokeCtx, stepPath(path, "finally"));
|
|
317
|
+
}
|
|
318
|
+
throw tryError;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (step.finally) {
|
|
322
|
+
await this.executeSteps(step.finally, steps, scope, { ...extraCtx, error: null }, invokeCtx, stepPath(path, "finally"));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/** The naming recipe for a generated inline-invoke resource. Module-private: it
|
|
327
|
+
* is the engine's own, and a bare `pascalCase` on the SDK's flat surface is a
|
|
328
|
+
* utility nobody should be reimplementing a name from. */
|
|
329
|
+
function pascalCase(s) {
|
|
330
|
+
return s
|
|
331
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
332
|
+
.filter(Boolean)
|
|
333
|
+
.map((p) => p[0].toUpperCase() + p.slice(1))
|
|
334
|
+
.join("");
|
|
335
|
+
}
|
|
336
|
+
/** Normalize any caught failure to the `error` shape a `catch:` branch reads.
|
|
337
|
+
* Shared with the composers' whole-operation `catches:`, so one caught failure
|
|
338
|
+
* has one shape wherever it is read. */
|
|
339
|
+
export function toSequenceError(err, stepName) {
|
|
340
|
+
if (isInvokeError(err)) {
|
|
341
|
+
// InvokeError.code is not validated non-empty at construction, so fall back
|
|
342
|
+
// to PLAIN_ERROR_CODE; message then falls back to the resolved code. Keeps
|
|
343
|
+
// both fields non-empty (see PLAIN_ERROR_CODE).
|
|
344
|
+
const code = err.code || PLAIN_ERROR_CODE;
|
|
345
|
+
return { message: err.message || code, code, data: err.data, step: stepName };
|
|
346
|
+
}
|
|
347
|
+
const message = (err instanceof Error ? err.message : String(err)) || "Unknown error";
|
|
348
|
+
return { message, code: PLAIN_ERROR_CODE, data: undefined, step: stepName };
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Where a step list's journal keys hang from.
|
|
352
|
+
*
|
|
353
|
+
* At the top of a durable run there is no ambient path and the base is `steps`.
|
|
354
|
+
* Inside one, it is the path of the step that dispatched this body — so a nested
|
|
355
|
+
* sequence's `work` becomes `steps/importAll/work` rather than a second
|
|
356
|
+
* `steps/work`, and two nested bodies can no longer collide.
|
|
357
|
+
*
|
|
358
|
+
* The dispatching step's path is used directly rather than with a `steps`
|
|
359
|
+
* segment appended: the parent path already names one dispatch site, and every
|
|
360
|
+
* other segment the grammar produces (`then`, `do[2]`, `cases/x`) is distinct
|
|
361
|
+
* from a step name, so nothing else can generate the same key.
|
|
362
|
+
*/
|
|
363
|
+
function baseStepPath(invokeCtx) {
|
|
364
|
+
return invokeCtx?.durablePath ?? "steps";
|
|
365
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zone attributes — what an `x-telo-provides-zone` object form declares about
|
|
3
|
+
* the region a body slot establishes, and the single accessor every surface
|
|
4
|
+
* reads that vocabulary through (the `value-type.ts` precedent, itself the
|
|
5
|
+
* `ref-slot.ts` one).
|
|
6
|
+
*
|
|
7
|
+
* A body slot that CONSTRAINS its contents is a body slot that already
|
|
8
|
+
* ESTABLISHES a zone — a transaction, a lease, an idempotency claim, a durable
|
|
9
|
+
* run are all of them — so the constraints are attributes on the annotation
|
|
10
|
+
* rather than a second annotation family that would have to restate the zone's
|
|
11
|
+
* location, its `extends` resolution and its runtime open call.
|
|
12
|
+
*
|
|
13
|
+
* THE VOCABULARY IS DATA; THE MEANING IS THE CONSUMER'S. Entries live at
|
|
14
|
+
* `sdk/zone-attributes/*.json` (see the README there) and are copied in by the
|
|
15
|
+
* root `prepare`. Both kernels read the identical files, because `noSuspend` is
|
|
16
|
+
* what stops a run parking inside a lease wherever that run executes. An entry
|
|
17
|
+
* declares a name, a value schema and its `requires:` dependencies, and no code:
|
|
18
|
+
* there is nothing per entry to implement.
|
|
19
|
+
*
|
|
20
|
+
* THE SET IS CLOSED. `x-telo-ref`'s `use` is a closed set on the same annotation
|
|
21
|
+
* family and nothing has needed to extend it; capabilities and value types are
|
|
22
|
+
* closed. The argument for openness inverts on inspection — `metadata.categories`
|
|
23
|
+
* is open precisely because NOTHING BRANCHES ON IT, while every zone attribute
|
|
24
|
+
* exists to be branched on and every reader is core. And a third-party attribute
|
|
25
|
+
* could only ever be HALF an attribute: a module cannot contribute an analyzer
|
|
26
|
+
* pass, so it would get a runtime reader here and no static check, while the
|
|
27
|
+
* failure directions that justify validating this vocabulary at all — an unread
|
|
28
|
+
* `noSuspend`, an unread `atomic` — are exactly the ones only a static check
|
|
29
|
+
* catches.
|
|
30
|
+
*
|
|
31
|
+
* THE REGISTRY IS IN THE SDK for the reasons the value-type one is: it is
|
|
32
|
+
* dependency-free and Node-built-in-free (so the browser-side analyzer can read
|
|
33
|
+
* it), and it is the only placement a module controller can reach.
|
|
34
|
+
*/
|
|
35
|
+
import type { ZoneEntry } from "./cancellation.js";
|
|
36
|
+
/** One zone attribute, exactly as its entry file declares it. */
|
|
37
|
+
export interface ZoneAttributeEntry {
|
|
38
|
+
/** The bare name an author writes as a key inside the annotation. Bare rather
|
|
39
|
+
* than `Telo.`-qualified because the position already implies the namespace
|
|
40
|
+
* and a closed set has no second namespace to disambiguate against. */
|
|
41
|
+
readonly name: string;
|
|
42
|
+
/** JSON Schema the declared value must satisfy — always the author's REASON,
|
|
43
|
+
* required by being the value itself rather than a sibling of a boolean. That
|
|
44
|
+
* is also what makes a type check possible at all: there is no `true` to
|
|
45
|
+
* accept, so `atomic: true` fails this schema. */
|
|
46
|
+
readonly value: Record<string, unknown>;
|
|
47
|
+
/** Attributes that must be declared alongside this one. Compiled to JSON
|
|
48
|
+
* Schema's `dependentRequired`, so the completeness rule lives in the data
|
|
49
|
+
* beside the thing it constrains rather than as a hardcoded pair of names. */
|
|
50
|
+
readonly requires: readonly string[];
|
|
51
|
+
readonly description: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Read one entry file's parsed data.
|
|
55
|
+
*
|
|
56
|
+
* Reading is STRICT and the vocabulary is closed at every level, for the reason
|
|
57
|
+
* the value-type reader is: a malformed or typo'd entry's only other outcome is
|
|
58
|
+
* an attribute that quietly is not in the vocabulary — which reads to an author
|
|
59
|
+
* as "unknown name", pointing at their manifest instead of at the entry.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseZoneAttributeEntry(file: string, data: unknown): ZoneAttributeEntry;
|
|
62
|
+
/** Every declared zone attribute, keyed by its bare name. */
|
|
63
|
+
export declare const ZONE_ATTRIBUTES: ReadonlyMap<string, ZoneAttributeEntry>;
|
|
64
|
+
/** The declared names, in entry order — what a diagnostic listing the closed
|
|
65
|
+
* vocabulary prints. */
|
|
66
|
+
export declare function zoneAttributeNames(): string[];
|
|
67
|
+
/**
|
|
68
|
+
* The attributes a zone declares, keyed by name, with the author's reason as the
|
|
69
|
+
* value.
|
|
70
|
+
*
|
|
71
|
+
* A typed record rather than a string-keyed bag, which the closed vocabulary is
|
|
72
|
+
* what makes possible. It is a readability gain and NOT a semantic one — the
|
|
73
|
+
* kernel still interprets nothing and branches on no name, exactly as
|
|
74
|
+
* `readRefSlot` hands back `use` without acting on it.
|
|
75
|
+
*/
|
|
76
|
+
export type ZoneAttributes = {
|
|
77
|
+
readonly [K in "atomic" | "idempotent" | "noSuspend" | "replayed"]?: string;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* One open zone, paired with what it declares about everything inside it.
|
|
81
|
+
*
|
|
82
|
+
* The kind is carried so a consumer can name the zone in a diagnostic — "the
|
|
83
|
+
* `Sql.Transaction` you are inside forbids parking" — while the attributes are
|
|
84
|
+
* what it actually branches on.
|
|
85
|
+
*/
|
|
86
|
+
export interface OpenZoneAttributes {
|
|
87
|
+
/** Canonical `<module>.<Kind>` of the providing kind. */
|
|
88
|
+
readonly kind: string;
|
|
89
|
+
/** What this zone declares, with each author's reason as the value. */
|
|
90
|
+
readonly attributes: ZoneAttributes;
|
|
91
|
+
/** The open entry itself, so a consumer that must ASK something about this
|
|
92
|
+
* particular zone has it in hand — a durable journal answering "do my writes
|
|
93
|
+
* land inside your atomicity?" needs the entry, not the kind.
|
|
94
|
+
*
|
|
95
|
+
* This is not the rejected "attributes on the entry" shape inverted: the
|
|
96
|
+
* entry stays three identities and carries nothing new, it merely travels
|
|
97
|
+
* BESIDE the attributes instead of being looked up again by a caller that
|
|
98
|
+
* would have to re-walk the stack to find it. */
|
|
99
|
+
readonly entry: ZoneEntry;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=zone-attribute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-attribute.d.ts","sourceRoot":"","sources":["../src/zone-attribute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC;;4EAEwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;uDAGmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC;;mFAE+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAuBD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,kBAAkB,CAyCvF;AAsCD,6DAA6D;AAC7D,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAmB,CAAC;AAExF;yBACyB;AACzB,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,CAAC,IAAI,QAAQ,GAAG,YAAY,GAAG,WAAW,GAAG,UAAU,CAAC,CAAC,EAAE,MAAM;CAC5E,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC;;;;;;;sDAOkD;IAClD,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B"}
|