@fluidgrids/workflows 2026.1027.0 → 2026.1028.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/README.md +170 -18
- package/dist/activation-BBh1p82z.d.cts +57 -0
- package/dist/activation-DnhG7SmF.d.ts +57 -0
- package/dist/assemble-BLqQ8lSu.d.cts +39 -0
- package/dist/assemble-DWgaXHd-.d.ts +39 -0
- package/dist/chunk-2DP2Y6EU.cjs +832 -0
- package/dist/chunk-2J6TF7YI.cjs +86 -0
- package/dist/chunk-3FOTFQ3J.js +1622 -0
- package/dist/chunk-4LYSEANA.js +199 -0
- package/dist/{chunk-KUTMY2IO.cjs → chunk-57MSZ24T.cjs} +162 -98
- package/dist/{chunk-BWSLWF2E.js → chunk-5HDSBTBX.js} +99 -32
- package/dist/chunk-623FVQSN.cjs +251 -0
- package/dist/{chunk-4DPEGDPW.js → chunk-6FSHT5IL.js} +4 -4
- package/dist/{chunk-REV7ZOA4.js → chunk-7AAZGVKD.js} +1 -35
- package/dist/chunk-7NQ37GZU.js +73 -0
- package/dist/chunk-ABFZWYHT.js +612 -0
- package/dist/{chunk-SS4LT2IP.js → chunk-BD7ZPG53.js} +5 -10
- package/dist/chunk-BO6OVILK.js +79 -0
- package/dist/chunk-C5OLWZ42.js +245 -0
- package/dist/chunk-CBL5JSCC.cjs +75 -0
- package/dist/{chunk-LSKSBVL4.cjs → chunk-CPDK3K5N.cjs} +6 -6
- package/dist/chunk-CSNWNFKU.cjs +1635 -0
- package/dist/chunk-DJYMDLV5.js +227 -0
- package/dist/{chunk-X4HKU3MX.js → chunk-E2VK7J2C.js} +3 -1
- package/dist/chunk-FMSW2HDF.cjs +267 -0
- package/dist/chunk-FQIHX7YI.cjs +80 -0
- package/dist/chunk-FR7KYWRO.cjs +41 -0
- package/dist/{chunk-AU3YW7FZ.cjs → chunk-GBQ33SIZ.cjs} +46 -51
- package/dist/{chunk-RRQ5HOBF.cjs → chunk-GVTZMA5C.cjs} +4 -4
- package/dist/chunk-H7KEVW2M.js +816 -0
- package/dist/chunk-HR3BN4C4.cjs +27 -0
- package/dist/chunk-IISBRUWJ.js +13 -0
- package/dist/chunk-JD2VQIDZ.cjs +15 -0
- package/dist/{chunk-PL73GJR4.js → chunk-JZ4TOOLN.js} +1 -1
- package/dist/chunk-L24YIZY7.cjs +207 -0
- package/dist/chunk-LNUQE6D6.js +39 -0
- package/dist/{chunk-BYJ7QTLY.cjs → chunk-NEIUQASE.cjs} +13 -13
- package/dist/chunk-P6TA5EZT.cjs +634 -0
- package/dist/chunk-PDHLTDZB.js +72 -0
- package/dist/chunk-PMCM2OH4.js +46 -0
- package/dist/chunk-PZCVF76Q.cjs +868 -0
- package/dist/{chunk-GO34UHSY.js → chunk-QAN5MZEM.js} +3 -3
- package/dist/{chunk-27HASETB.cjs → chunk-QKZL3ONM.cjs} +3 -1
- package/dist/chunk-SAENRNBJ.cjs +52 -0
- package/dist/{chunk-LMX35GNQ.js → chunk-SMCANK3F.js} +5 -4
- package/dist/{chunk-IZQNUNSU.cjs → chunk-SX2PNKKT.cjs} +18 -17
- package/dist/{chunk-LRM6JXJA.cjs → chunk-TBDXTV22.cjs} +0 -37
- package/dist/chunk-VOKMAAJP.js +848 -0
- package/dist/chunk-XB53G2DK.js +25 -0
- package/dist/chunk-Z2Y65YOY.cjs +7 -0
- package/dist/codegen/index.cjs +9 -7
- package/dist/codegen/index.d.cts +2 -2
- package/dist/codegen/index.d.ts +2 -2
- package/dist/codegen/index.js +6 -5
- package/dist/file-BOoHZgoQ.d.cts +1557 -0
- package/dist/file-yuPCFU5b.d.ts +1557 -0
- package/dist/history/index.cjs +287 -0
- package/dist/history/index.d.cts +157 -0
- package/dist/history/index.d.ts +157 -0
- package/dist/history/index.js +5 -0
- package/dist/index.cjs +134 -36
- package/dist/index.d.cts +137 -9
- package/dist/index.d.ts +137 -9
- package/dist/index.js +8 -3
- package/dist/ir/index.cjs +161 -165
- package/dist/ir/index.d.cts +32 -12
- package/dist/ir/index.d.ts +32 -12
- package/dist/ir/index.js +54 -61
- package/dist/lower-BuSvAFqi.d.cts +129 -0
- package/dist/lower-C_Qv_34S.d.ts +129 -0
- package/dist/lower-types-nofRXw9c.d.cts +15 -0
- package/dist/lower-types-nofRXw9c.d.ts +15 -0
- package/dist/{manifest-Duf-gHdO.d.cts → manifest-0OLw0i7k.d.cts} +1 -1
- package/dist/{manifest-Bp9ZJlav.d.ts → manifest-C8N6HDYl.d.ts} +1 -1
- package/dist/node-evaluator-CJNP4GHX.js +12 -0
- package/dist/node-evaluator-DMKPKUWA.cjs +22 -0
- package/dist/quickjs/index.cjs +256 -78
- package/dist/quickjs/index.d.cts +48 -7
- package/dist/quickjs/index.d.ts +48 -7
- package/dist/quickjs/index.js +254 -79
- package/dist/roundtrip/index.cjs +404 -129
- package/dist/roundtrip/index.d.cts +43 -8
- package/dist/roundtrip/index.d.ts +43 -8
- package/dist/roundtrip/index.js +260 -19
- package/dist/runtime/index.cjs +90 -0
- package/dist/runtime/index.d.cts +297 -0
- package/dist/runtime/index.d.ts +297 -0
- package/dist/runtime/index.js +8 -0
- package/dist/testing/index.cjs +1057 -0
- package/dist/testing/index.d.cts +305 -0
- package/dist/testing/index.d.ts +305 -0
- package/dist/testing/index.js +1037 -0
- package/dist/{types-Dr2Fn1Ax.d.ts → types-BGoPzgcl.d.cts} +1 -1
- package/dist/{types-Dr2Fn1Ax.d.cts → types-BGoPzgcl.d.ts} +1 -1
- package/dist/types-CpBBjaGX.d.cts +380 -0
- package/dist/types-DV-HX_Gt.d.ts +380 -0
- package/dist/{assemble-DMOuaxRI.d.ts → workflow-Bpr3vnA_.d.ts} +60 -162
- package/dist/workflow-C2a05WUJ.d.ts +444 -0
- package/dist/{assemble-BJo9TECS.d.cts → workflow-FXUkpIBc.d.cts} +60 -162
- package/dist/workflow-J-yYixny.d.cts +444 -0
- package/fixtures/README.md +128 -0
- package/fixtures/histories/activity-failure-fails-run.history.json +163 -0
- package/fixtures/histories/activity-retry-then-success.history.json +147 -0
- package/fixtures/histories/cancel-activity-abandon.history.json +126 -0
- package/fixtures/histories/cancel-activity-compensate.history.json +307 -0
- package/fixtures/histories/cancel-concurrent-non-cancellable.history.json +234 -0
- package/fixtures/histories/cancel-concurrent-scope-timeout.history.json +184 -0
- package/fixtures/histories/cancel-condition-after-cancel.history.json +200 -0
- package/fixtures/histories/cancel-during-timer.history.json +123 -0
- package/fixtures/histories/cancel-saga-compensations.history.json +325 -0
- package/fixtures/histories/cancel-scope-timeout.history.json +148 -0
- package/fixtures/histories/child-abandon-outlives-parent.history.json +191 -0
- package/fixtures/histories/child-cancel-default.history.json +234 -0
- package/fixtures/histories/child-cancel-request.history.json +206 -0
- package/fixtures/histories/child-durable-child-side.history.json +124 -0
- package/fixtures/histories/child-durable-result.history.json +163 -0
- package/fixtures/histories/child-failure-caught.history.json +166 -0
- package/fixtures/histories/child-graph-by-key.history.json +138 -0
- package/fixtures/histories/child-terminated-by-parent-close.history.json +85 -0
- package/fixtures/histories/continue-as-new-first-run.history.json +146 -0
- package/fixtures/histories/continue-as-new-history-limit.history.json +489 -0
- package/fixtures/histories/continue-as-new-last-run.history.json +147 -0
- package/fixtures/histories/continue-as-new-suggested.history.json +352 -0
- package/fixtures/histories/details-and-context.history.json +176 -0
- package/fixtures/histories/deterministic-values.history.json +128 -0
- package/fixtures/histories/engine-builtins.history.json +172 -0
- package/fixtures/histories/gather-activities.history.json +291 -0
- package/fixtures/histories/gather-all-settled.history.json +219 -0
- package/fixtures/histories/gather-mixed.history.json +281 -0
- package/fixtures/histories/input-during-running-task.history.json +111 -0
- package/fixtures/histories/nondeterminism-reordered.history.json +218 -0
- package/fixtures/histories/patched-deprecated.history.json +271 -0
- package/fixtures/histories/patched-new-run.history.json +270 -0
- package/fixtures/histories/patched-old-history.history.json +190 -0
- package/fixtures/histories/race-activity-wins.history.json +152 -0
- package/fixtures/histories/race-signal-beats-child.history.json +161 -0
- package/fixtures/histories/race-timeout-wins.history.json +118 -0
- package/fixtures/histories/side-effect-recorded.history.json +153 -0
- package/fixtures/histories/signal-buffered-at-start.history.json +157 -0
- package/fixtures/histories/signal-default-handler.history.json +187 -0
- package/fixtures/histories/signal-handler.history.json +191 -0
- package/fixtures/histories/signal-parent-child-side.history.json +132 -0
- package/fixtures/histories/signal-parent.history.json +202 -0
- package/fixtures/histories/signal-run-delivered.history.json +128 -0
- package/fixtures/histories/signal-run-undelivered.history.json +134 -0
- package/fixtures/histories/signal-wait.history.json +111 -0
- package/fixtures/histories/task-failure-code-error.history.json +137 -0
- package/fixtures/histories/task-failure-then-transient-attempt.history.json +140 -0
- package/fixtures/histories/timer-condition-met.history.json +201 -0
- package/fixtures/histories/timer-condition-timeout.history.json +118 -0
- package/fixtures/histories/timer-sleep-once.history.json +118 -0
- package/fixtures/histories/timer-sleep-sequence.history.json +210 -0
- package/fixtures/histories/update-accepted-completed.history.json +149 -0
- package/fixtures/histories/update-with-start-rejected-validation.history.json +91 -0
- package/package.json +85 -20
- package/schema/history-file.schema.json +2544 -0
- package/schema/ir-v2.schema.json +16 -0
- package/schema/run-event.schema.json +2374 -0
- package/schema/workflow-command.schema.json +1095 -0
- package/schema/workflow-task.schema.json +3560 -0
- package/dist/chunk-265J2FCH.cjs +0 -33
- package/dist/chunk-LUXCXFOE.cjs +0 -269
- package/dist/chunk-S34NJE3A.js +0 -262
- package/dist/chunk-SRQRLWOS.js +0 -31
- package/dist/lower-DcOY8ux0.d.cts +0 -49
- package/dist/lower-Dw5grNrI.d.ts +0 -49
- package/dist/node-evaluator-2GTUZVPK.cjs +0 -17
- package/dist/node-evaluator-RMYEWCXZ.js +0 -8
|
@@ -0,0 +1,1622 @@
|
|
|
1
|
+
import { ServerFailure, failureToError, CancelledFailure, TimeoutFailure, ApplicationError, TerminatedFailure, DeterminismViolation, HistoryLimitExceeded, PhaseCFeatureError, isCancellation, isRunFailure, errorToFailure, optionalMs, ActivityFailure, toMs, signalName, ChildRunFailure } from './chunk-C5OLWZ42.js';
|
|
2
|
+
import { createRandom, seedOf, takeShimControl, uuidFrom } from './chunk-4LYSEANA.js';
|
|
3
|
+
import { isDurableWorkflow, SDK_VERSION } from './chunk-7NQ37GZU.js';
|
|
4
|
+
import { utf8Bytes, splitActivations, explicitRetryPolicy, CHILD_DEFAULT_CANCELLATION_TYPE, CHILD_DEFAULT_PARENT_CLOSE_POLICY } from './chunk-ABFZWYHT.js';
|
|
5
|
+
import { COMMAND_EVENT_TYPES, DEFAULT_HISTORY_LIMIT, CONTINUE_AS_NEW_SUGGESTED_RATIO, HISTORY_PROTOCOL, HISTORY_LIMIT_EXCEEDED_RATIO, BUILT_IN_QUERIES } from './chunk-DJYMDLV5.js';
|
|
6
|
+
import { isActivityNodeTypeAllowed } from './chunk-BO6OVILK.js';
|
|
7
|
+
|
|
8
|
+
// src/runtime/ids.ts
|
|
9
|
+
var CommandIds = class {
|
|
10
|
+
counters = { a: 0, t: 0, c: 0, s: 0, x: 0, m: 0 };
|
|
11
|
+
/** The next id of a kind: `a1`, `a2`, ... */
|
|
12
|
+
next(kind) {
|
|
13
|
+
this.counters[kind] += 1;
|
|
14
|
+
return `${kind}${this.counters[kind]}`;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function patchMarkerId(patchId) {
|
|
18
|
+
return `patch:${patchId}`;
|
|
19
|
+
}
|
|
20
|
+
function isPatchMarkerId(markerId) {
|
|
21
|
+
return markerId.startsWith("patch:");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/runtime/matcher.ts
|
|
25
|
+
function field(event, key) {
|
|
26
|
+
return event.payload[key];
|
|
27
|
+
}
|
|
28
|
+
function describeCommand(command) {
|
|
29
|
+
switch (command.type) {
|
|
30
|
+
case "SCHEDULE_ACTIVITY":
|
|
31
|
+
return `SCHEDULE_ACTIVITY ${command.activityId} (${command.nodeType} ${command.operation})`;
|
|
32
|
+
case "REQUEST_CANCEL_ACTIVITY":
|
|
33
|
+
return `REQUEST_CANCEL_ACTIVITY ${command.activityId}`;
|
|
34
|
+
case "START_TIMER":
|
|
35
|
+
case "CANCEL_TIMER":
|
|
36
|
+
return `${command.type} ${command.timerId}`;
|
|
37
|
+
case "START_CHILD_RUN":
|
|
38
|
+
return `START_CHILD_RUN ${command.childId} (${command.workflowKey ?? command.workflowFn ?? ""})`;
|
|
39
|
+
case "SIGNAL_RUN":
|
|
40
|
+
return `SIGNAL_RUN ${command.externalId} (${command.name})`;
|
|
41
|
+
case "REQUEST_CANCEL_EXTERNAL_RUN":
|
|
42
|
+
return `REQUEST_CANCEL_EXTERNAL_RUN ${command.externalId}`;
|
|
43
|
+
case "RECORD_SIDE_EFFECT":
|
|
44
|
+
return `RECORD_SIDE_EFFECT ${command.sideEffectId}`;
|
|
45
|
+
case "RECORD_MARKER":
|
|
46
|
+
return `RECORD_MARKER ${command.markerId}`;
|
|
47
|
+
case "RECORD_LOCAL_ACTIVITY":
|
|
48
|
+
return `RECORD_LOCAL_ACTIVITY ${command.localActivityId}`;
|
|
49
|
+
default:
|
|
50
|
+
return command.type;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function describeEvent(event) {
|
|
54
|
+
const id = field(event, "activityId") ?? field(event, "timerId") ?? field(event, "childId") ?? field(event, "externalId") ?? field(event, "sideEffectId") ?? field(event, "markerId") ?? field(event, "localActivityId");
|
|
55
|
+
return `${event.type}${typeof id === "string" ? ` ${id}` : ""} (seq ${event.seq})`;
|
|
56
|
+
}
|
|
57
|
+
function mismatch(command, event) {
|
|
58
|
+
const expected = COMMAND_EVENT_TYPES[command.type];
|
|
59
|
+
const typeOk = command.type === "RECORD_LOCAL_ACTIVITY" ? event.type === "LOCAL_ACTIVITY_COMPLETED" || event.type === "LOCAL_ACTIVITY_FAILED" : event.type === expected;
|
|
60
|
+
const differ = () => `the code issued ${describeCommand(command)} where the history has ${describeEvent(event)}`;
|
|
61
|
+
if (!typeOk) return differ();
|
|
62
|
+
switch (command.type) {
|
|
63
|
+
case "SCHEDULE_ACTIVITY":
|
|
64
|
+
return field(event, "activityId") === command.activityId && field(event, "nodeType") === command.nodeType && field(event, "operation") === command.operation ? void 0 : differ();
|
|
65
|
+
case "REQUEST_CANCEL_ACTIVITY":
|
|
66
|
+
return field(event, "activityId") === command.activityId ? void 0 : differ();
|
|
67
|
+
case "START_TIMER":
|
|
68
|
+
case "CANCEL_TIMER":
|
|
69
|
+
return field(event, "timerId") === command.timerId ? void 0 : differ();
|
|
70
|
+
case "START_CHILD_RUN":
|
|
71
|
+
return field(event, "childId") === command.childId && (field(event, "workflowKey") ?? null) === (command.workflowKey ?? null) && (field(event, "workflowFn") ?? null) === (command.workflowFn ?? null) ? void 0 : differ();
|
|
72
|
+
case "SIGNAL_RUN":
|
|
73
|
+
return field(event, "externalId") === command.externalId && field(event, "name") === command.name ? void 0 : differ();
|
|
74
|
+
case "REQUEST_CANCEL_EXTERNAL_RUN":
|
|
75
|
+
return field(event, "externalId") === command.externalId ? void 0 : differ();
|
|
76
|
+
case "RECORD_SIDE_EFFECT":
|
|
77
|
+
return field(event, "sideEffectId") === command.sideEffectId ? void 0 : differ();
|
|
78
|
+
case "RECORD_MARKER":
|
|
79
|
+
return field(event, "markerId") === command.markerId ? void 0 : differ();
|
|
80
|
+
case "RECORD_LOCAL_ACTIVITY":
|
|
81
|
+
return field(event, "localActivityId") === command.localActivityId ? void 0 : differ();
|
|
82
|
+
default:
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function isSkippablePatch(event) {
|
|
87
|
+
const markerId = field(event, "markerId");
|
|
88
|
+
return event.type === "MARKER_RECORDED" && typeof markerId === "string" && isPatchMarkerId(markerId);
|
|
89
|
+
}
|
|
90
|
+
function matchActivation(commands, recorded) {
|
|
91
|
+
let position = 0;
|
|
92
|
+
for (const command of commands) {
|
|
93
|
+
let event = recorded[position];
|
|
94
|
+
while (event !== void 0 && isSkippablePatch(event) && !(command.type === "RECORD_MARKER" && command.markerId === field(event, "markerId"))) {
|
|
95
|
+
position += 1;
|
|
96
|
+
event = recorded[position];
|
|
97
|
+
}
|
|
98
|
+
if (event === void 0)
|
|
99
|
+
return {
|
|
100
|
+
message: `the code issued ${describeCommand(command)}, which the history does not have`
|
|
101
|
+
};
|
|
102
|
+
const reason = mismatch(command, event);
|
|
103
|
+
if (reason !== void 0) return { message: reason, seq: event.seq };
|
|
104
|
+
position += 1;
|
|
105
|
+
}
|
|
106
|
+
for (; position < recorded.length; position++) {
|
|
107
|
+
const event = recorded[position];
|
|
108
|
+
if (event !== void 0 && !isSkippablePatch(event))
|
|
109
|
+
return {
|
|
110
|
+
message: `the history has ${describeEvent(event)}, which the code did not issue`,
|
|
111
|
+
seq: event.seq
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/runtime/stack.ts
|
|
118
|
+
var RUNTIME_FRAME = /fluidgrids-runtime|\/runtime\/(scheduler|index)\.|WorkflowExecution|RuntimeContext/;
|
|
119
|
+
function captureStack() {
|
|
120
|
+
const stack = new Error("stack").stack ?? "";
|
|
121
|
+
return stack.split("\n").slice(1).filter((line) => line.trim() !== "" && !RUNTIME_FRAME.test(line)).join("\n");
|
|
122
|
+
}
|
|
123
|
+
var V8_FRAME = /^\s*at (?:(.*?) \()?(.*?):(\d+):(\d+)\)?$/;
|
|
124
|
+
var QUICKJS_FRAME = /^\s*at (?:(.*?) )?\(?(.*?)(?::(\d+))?(?::(\d+))?\)?$/;
|
|
125
|
+
function parseStack(stack) {
|
|
126
|
+
const locations = [];
|
|
127
|
+
for (const line of stack.split("\n")) {
|
|
128
|
+
if (line.trim() === "") continue;
|
|
129
|
+
const match = V8_FRAME.exec(line) ?? QUICKJS_FRAME.exec(line);
|
|
130
|
+
if (!match) continue;
|
|
131
|
+
const location = {};
|
|
132
|
+
if (match[1]) location.functionName = match[1];
|
|
133
|
+
if (match[2]) location.filePath = match[2];
|
|
134
|
+
if (match[3]) location.line = Number(match[3]);
|
|
135
|
+
if (match[4]) location.column = Number(match[4]);
|
|
136
|
+
locations.push(location);
|
|
137
|
+
}
|
|
138
|
+
return locations;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/runtime/scheduler.ts
|
|
142
|
+
var SDK_NAME = "@fluidgrids/workflows";
|
|
143
|
+
var ContinueAsNewRequest = class {
|
|
144
|
+
constructor(input, options) {
|
|
145
|
+
this.input = input;
|
|
146
|
+
this.options = options;
|
|
147
|
+
}
|
|
148
|
+
input;
|
|
149
|
+
options;
|
|
150
|
+
};
|
|
151
|
+
var TaskFailure = class {
|
|
152
|
+
constructor(cause, message, stackTrace, seq, errorType) {
|
|
153
|
+
this.cause = cause;
|
|
154
|
+
this.message = message;
|
|
155
|
+
this.stackTrace = stackTrace;
|
|
156
|
+
this.seq = seq;
|
|
157
|
+
this.errorType = errorType;
|
|
158
|
+
}
|
|
159
|
+
cause;
|
|
160
|
+
message;
|
|
161
|
+
stackTrace;
|
|
162
|
+
seq;
|
|
163
|
+
errorType;
|
|
164
|
+
};
|
|
165
|
+
var Scope = class {
|
|
166
|
+
constructor(parent, nonCancellable) {
|
|
167
|
+
this.parent = parent;
|
|
168
|
+
this.nonCancellable = nonCancellable;
|
|
169
|
+
this.cancelRequested = new Promise((_resolve, reject) => {
|
|
170
|
+
this.rejectRequested = reject;
|
|
171
|
+
});
|
|
172
|
+
this.cancelRequested.catch(() => void 0);
|
|
173
|
+
if (parent && !nonCancellable) {
|
|
174
|
+
parent.children.add(this);
|
|
175
|
+
if (parent.consideredCancelled) this.cancel();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
parent;
|
|
179
|
+
nonCancellable;
|
|
180
|
+
consideredCancelled = false;
|
|
181
|
+
cancelRequested;
|
|
182
|
+
rejectRequested = () => void 0;
|
|
183
|
+
handlers = /* @__PURE__ */ new Set();
|
|
184
|
+
children = /* @__PURE__ */ new Set();
|
|
185
|
+
/** Register a reaction to cancellation; returns its removal. */
|
|
186
|
+
onCancel(handler) {
|
|
187
|
+
if (this.consideredCancelled) {
|
|
188
|
+
handler();
|
|
189
|
+
return () => void 0;
|
|
190
|
+
}
|
|
191
|
+
this.handlers.add(handler);
|
|
192
|
+
return () => this.handlers.delete(handler);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Cancel the scope and its children. Every handler and child runs even when
|
|
196
|
+
* one throws; the first error is rethrown once the fan-out is complete.
|
|
197
|
+
*/
|
|
198
|
+
cancel() {
|
|
199
|
+
if (this.consideredCancelled) return;
|
|
200
|
+
this.consideredCancelled = true;
|
|
201
|
+
this.rejectRequested(new CancelledFailure("Cancellation requested"));
|
|
202
|
+
const errors = [];
|
|
203
|
+
const handlers = [...this.handlers];
|
|
204
|
+
this.handlers.clear();
|
|
205
|
+
for (const handler of handlers) {
|
|
206
|
+
try {
|
|
207
|
+
handler();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
errors.push(error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const child of [...this.children]) {
|
|
213
|
+
try {
|
|
214
|
+
child.cancel();
|
|
215
|
+
} catch (error) {
|
|
216
|
+
errors.push(error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (errors.length > 0) throw errors[0];
|
|
220
|
+
}
|
|
221
|
+
detach() {
|
|
222
|
+
this.parent?.children.delete(this);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function toJson(value, what) {
|
|
226
|
+
if (value === void 0) return null;
|
|
227
|
+
if (typeof value === "function" || typeof value === "symbol")
|
|
228
|
+
throw new TypeError(`${what} is not JSON: it is a ${typeof value}`);
|
|
229
|
+
let text;
|
|
230
|
+
try {
|
|
231
|
+
text = JSON.stringify(value);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (!(error instanceof TypeError)) throw error;
|
|
234
|
+
throw new TypeError(`${what} is not JSON: ${error.message}`);
|
|
235
|
+
}
|
|
236
|
+
if (text === void 0) return null;
|
|
237
|
+
const parsed = JSON.parse(text);
|
|
238
|
+
return parsed;
|
|
239
|
+
}
|
|
240
|
+
function randomnessSeedOf(request) {
|
|
241
|
+
const started = request.history[0];
|
|
242
|
+
const seed = started?.type === "WORKFLOW_STARTED" ? started.payload["randomnessSeed"] : void 0;
|
|
243
|
+
return typeof seed === "string" ? seed : request.info.runId;
|
|
244
|
+
}
|
|
245
|
+
function errorName(error) {
|
|
246
|
+
return error instanceof Error ? error.name : "";
|
|
247
|
+
}
|
|
248
|
+
function messageOf(error) {
|
|
249
|
+
return error instanceof Error ? error.message : String(error);
|
|
250
|
+
}
|
|
251
|
+
function stackTextOf(error) {
|
|
252
|
+
return error instanceof Error && typeof error.stack === "string" ? error.stack : void 0;
|
|
253
|
+
}
|
|
254
|
+
var WorkflowExecution = class {
|
|
255
|
+
request;
|
|
256
|
+
definition;
|
|
257
|
+
exportNames;
|
|
258
|
+
sdkVersion;
|
|
259
|
+
activations;
|
|
260
|
+
index = -1;
|
|
261
|
+
activationOpen = false;
|
|
262
|
+
replaying = false;
|
|
263
|
+
clock = 0;
|
|
264
|
+
random;
|
|
265
|
+
ids = new CommandIds();
|
|
266
|
+
commands = [];
|
|
267
|
+
newCommands = [];
|
|
268
|
+
logs = [];
|
|
269
|
+
failure;
|
|
270
|
+
closed = false;
|
|
271
|
+
captureStacks;
|
|
272
|
+
/** History length and inline bytes the current activation reports (see `Activation.historyLength`). */
|
|
273
|
+
historyLength = 0;
|
|
274
|
+
historyBytes = 0;
|
|
275
|
+
/** `bytesUpTo[n]`: UTF-8 bytes of the payloads of events 1..n. */
|
|
276
|
+
bytesUpTo;
|
|
277
|
+
limitEvents;
|
|
278
|
+
limitBytes;
|
|
279
|
+
root = new Scope(void 0, false);
|
|
280
|
+
/**
|
|
281
|
+
* Scopes whose function is running synchronously right now, innermost
|
|
282
|
+
* last. The outer `ctx` uses the innermost one, else the root: a scope is
|
|
283
|
+
* ambient only during the synchronous part of its function, never across
|
|
284
|
+
* an await, so concurrent branches never see each other's scopes.
|
|
285
|
+
*/
|
|
286
|
+
syncScopes = [];
|
|
287
|
+
/** A scope that is never cancelled, for waits that ignore cancellation. */
|
|
288
|
+
detachedScope = new Scope(void 0, true);
|
|
289
|
+
cancelRequested = false;
|
|
290
|
+
activities = /* @__PURE__ */ new Map();
|
|
291
|
+
timers = /* @__PURE__ */ new Map();
|
|
292
|
+
children = /* @__PURE__ */ new Map();
|
|
293
|
+
externals = /* @__PURE__ */ new Map();
|
|
294
|
+
issued = /* @__PURE__ */ new Set();
|
|
295
|
+
recordedSideEffects = /* @__PURE__ */ new Map();
|
|
296
|
+
recordedMarkers = /* @__PURE__ */ new Set();
|
|
297
|
+
patches = /* @__PURE__ */ new Map();
|
|
298
|
+
signalBuffers = /* @__PURE__ */ new Map();
|
|
299
|
+
signalOrder = [];
|
|
300
|
+
signalHandlers = /* @__PURE__ */ new Map();
|
|
301
|
+
signalWaiters = /* @__PURE__ */ new Map();
|
|
302
|
+
defaultSignalHandler;
|
|
303
|
+
runningHandlers = 0;
|
|
304
|
+
conditions = [];
|
|
305
|
+
currentDetails;
|
|
306
|
+
context;
|
|
307
|
+
info;
|
|
308
|
+
constructor(input) {
|
|
309
|
+
this.request = input.request;
|
|
310
|
+
this.definition = input.definition;
|
|
311
|
+
this.exportNames = input.exportNames;
|
|
312
|
+
this.sdkVersion = input.sdkVersion;
|
|
313
|
+
const history = input.request.history;
|
|
314
|
+
this.bytesUpTo = [0];
|
|
315
|
+
let bytes = 0;
|
|
316
|
+
for (const event of history) {
|
|
317
|
+
bytes += utf8Bytes(JSON.stringify(event.payload));
|
|
318
|
+
this.bytesUpTo.push(bytes);
|
|
319
|
+
}
|
|
320
|
+
this.historyLength = history.length;
|
|
321
|
+
this.historyBytes = bytes;
|
|
322
|
+
const limit = input.request.info.historyLimit ?? DEFAULT_HISTORY_LIMIT;
|
|
323
|
+
this.limitEvents = limit.events;
|
|
324
|
+
this.limitBytes = limit.bytes;
|
|
325
|
+
this.captureStacks = input.request.kind === "query";
|
|
326
|
+
this.random = createRandom(seedOf(randomnessSeedOf(input.request)));
|
|
327
|
+
for (const event of history) {
|
|
328
|
+
if (event.type === "SIDE_EFFECT_RECORDED") {
|
|
329
|
+
const id = event.payload["sideEffectId"];
|
|
330
|
+
if (typeof id === "string")
|
|
331
|
+
this.recordedSideEffects.set(id, event.payload["value"] ?? null);
|
|
332
|
+
} else if (event.type === "MARKER_RECORDED") {
|
|
333
|
+
const id = event.payload["markerId"];
|
|
334
|
+
if (typeof id === "string") this.recordedMarkers.add(id);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
this.activations = splitActivations(
|
|
338
|
+
history,
|
|
339
|
+
input.request.nowMs,
|
|
340
|
+
input.request.kind !== "query"
|
|
341
|
+
);
|
|
342
|
+
const execution = this;
|
|
343
|
+
this.info = {
|
|
344
|
+
...input.request.info,
|
|
345
|
+
workflowFn: input.request.workflowFn,
|
|
346
|
+
get historyLength() {
|
|
347
|
+
return execution.historyLength;
|
|
348
|
+
},
|
|
349
|
+
get historyBytes() {
|
|
350
|
+
return execution.historyBytes;
|
|
351
|
+
},
|
|
352
|
+
get continueAsNewSuggested() {
|
|
353
|
+
return execution.continueAsNewSuggested();
|
|
354
|
+
},
|
|
355
|
+
get isReplaying() {
|
|
356
|
+
return execution.replaying;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
this.context = this.contextFor(void 0);
|
|
360
|
+
}
|
|
361
|
+
continueAsNewSuggested() {
|
|
362
|
+
return this.historyLength >= this.limitEvents * CONTINUE_AS_NEW_SUGGESTED_RATIO || this.historyBytes >= this.limitBytes * CONTINUE_AS_NEW_SUGGESTED_RATIO;
|
|
363
|
+
}
|
|
364
|
+
/** The workflow clock. */
|
|
365
|
+
now() {
|
|
366
|
+
return this.clock;
|
|
367
|
+
}
|
|
368
|
+
/** The next deterministic random number. */
|
|
369
|
+
nextRandom() {
|
|
370
|
+
return this.random();
|
|
371
|
+
}
|
|
372
|
+
/** Begin or finish an activation; `done` when there is nothing left to do. */
|
|
373
|
+
step() {
|
|
374
|
+
if (this.failure) return "done";
|
|
375
|
+
if (this.activationOpen) {
|
|
376
|
+
if (this.checkConditions()) return "more";
|
|
377
|
+
this.finishActivation();
|
|
378
|
+
if (this.failure) return "done";
|
|
379
|
+
}
|
|
380
|
+
const next = this.activations[this.index + 1];
|
|
381
|
+
if (!next) return "done";
|
|
382
|
+
this.index += 1;
|
|
383
|
+
this.beginActivation(next);
|
|
384
|
+
return this.failure ? "done" : "more";
|
|
385
|
+
}
|
|
386
|
+
/** The task's result. Call after `step()` returned `done` and jobs were drained. */
|
|
387
|
+
complete() {
|
|
388
|
+
if (!this.failure && this.activationOpen) {
|
|
389
|
+
this.checkConditions();
|
|
390
|
+
this.finishActivation();
|
|
391
|
+
}
|
|
392
|
+
const result = {
|
|
393
|
+
protocol: HISTORY_PROTOCOL,
|
|
394
|
+
outcome: this.failure ? "failed" : "commands",
|
|
395
|
+
commands: this.failure ? [] : this.newCommands,
|
|
396
|
+
logs: this.logs
|
|
397
|
+
};
|
|
398
|
+
if (this.failure) {
|
|
399
|
+
result.failure = { cause: this.failure.cause, message: this.failure.message };
|
|
400
|
+
if (this.failure.stackTrace !== void 0)
|
|
401
|
+
result.failure.stackTrace = this.failure.stackTrace;
|
|
402
|
+
if (this.failure.seq !== void 0) result.failure.seq = this.failure.seq;
|
|
403
|
+
if (this.failure.errorType !== void 0) result.failure.errorType = this.failure.errorType;
|
|
404
|
+
}
|
|
405
|
+
if (this.request.kind === "query") result.queryResults = this.answerQueries();
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
/** Fail the task (the runtime's own failures and code errors). */
|
|
409
|
+
failTask(cause, message, stackTrace, seq, errorType) {
|
|
410
|
+
if (!this.failure) this.failure = new TaskFailure(cause, message, stackTrace, seq, errorType);
|
|
411
|
+
}
|
|
412
|
+
beginActivation(activation) {
|
|
413
|
+
this.activationOpen = true;
|
|
414
|
+
this.replaying = activation.recorded !== void 0;
|
|
415
|
+
this.clock = activation.timeMs;
|
|
416
|
+
this.historyLength = activation.historyLength;
|
|
417
|
+
this.historyBytes = this.bytesUpTo[Math.min(activation.historyLength, this.bytesUpTo.length - 1)] ?? 0;
|
|
418
|
+
this.commands = [];
|
|
419
|
+
for (const event of activation.inputs) {
|
|
420
|
+
if (this.failure) return;
|
|
421
|
+
this.deliver(event);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
finishActivation() {
|
|
425
|
+
this.activationOpen = false;
|
|
426
|
+
const activation = this.activations[this.index];
|
|
427
|
+
if (!activation) return;
|
|
428
|
+
if (activation.recorded) {
|
|
429
|
+
const found = matchActivation(this.commands, activation.recorded);
|
|
430
|
+
if (found)
|
|
431
|
+
this.failTask(
|
|
432
|
+
"NON_DETERMINISM",
|
|
433
|
+
`Replay of workflow task ${activation.taskId ?? this.index + 1} diverged: ${found.message}`,
|
|
434
|
+
void 0,
|
|
435
|
+
found.seq
|
|
436
|
+
);
|
|
437
|
+
} else {
|
|
438
|
+
this.newCommands = this.commands;
|
|
439
|
+
}
|
|
440
|
+
this.commands = [];
|
|
441
|
+
}
|
|
442
|
+
// ---- inputs ---------------------------------------------------------------
|
|
443
|
+
deliver(event) {
|
|
444
|
+
const p = event.payload;
|
|
445
|
+
const id = (key) => typeof p[key] === "string" ? p[key] : "";
|
|
446
|
+
switch (event.type) {
|
|
447
|
+
case "WORKFLOW_STARTED":
|
|
448
|
+
this.startFunction(p["input"] ?? null);
|
|
449
|
+
return;
|
|
450
|
+
case "ACTIVITY_COMPLETED":
|
|
451
|
+
case "ACTIVITY_FAILED":
|
|
452
|
+
case "ACTIVITY_TIMED_OUT":
|
|
453
|
+
case "ACTIVITY_CANCELLED":
|
|
454
|
+
this.settleActivity(event, id("activityId"));
|
|
455
|
+
return;
|
|
456
|
+
case "TIMER_FIRED": {
|
|
457
|
+
const timerId = id("timerId");
|
|
458
|
+
this.expectIssued(`timer:${timerId}`, event);
|
|
459
|
+
const pending = this.timers.get(timerId);
|
|
460
|
+
if (pending) {
|
|
461
|
+
this.timers.delete(timerId);
|
|
462
|
+
pending.release?.();
|
|
463
|
+
pending.resolve(void 0);
|
|
464
|
+
}
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
case "SIGNAL_RECEIVED":
|
|
468
|
+
this.dispatchSignal(id("name"), p["payload"] ?? null);
|
|
469
|
+
return;
|
|
470
|
+
case "CHILD_RUN_STARTED":
|
|
471
|
+
case "CHILD_RUN_COMPLETED":
|
|
472
|
+
case "CHILD_RUN_FAILED":
|
|
473
|
+
case "CHILD_RUN_CANCELLED":
|
|
474
|
+
case "CHILD_RUN_TERMINATED":
|
|
475
|
+
case "CHILD_RUN_TIMED_OUT":
|
|
476
|
+
this.settleChild(event, id("childId"));
|
|
477
|
+
return;
|
|
478
|
+
case "EXTERNAL_RUN_SIGNALED":
|
|
479
|
+
case "EXTERNAL_RUN_CANCEL_RESOLVED": {
|
|
480
|
+
const externalId = id("externalId");
|
|
481
|
+
this.expectIssued(`external:${externalId}`, event);
|
|
482
|
+
const pending = this.externals.get(externalId);
|
|
483
|
+
if (!pending) return;
|
|
484
|
+
this.externals.delete(externalId);
|
|
485
|
+
if (p["ok"] === true) pending.resolve(void 0);
|
|
486
|
+
else {
|
|
487
|
+
const failure = p["failure"];
|
|
488
|
+
pending.reject(
|
|
489
|
+
typeof failure === "object" && failure !== null && !Array.isArray(failure) ? failureToError(failure) : new ServerFailure(`${pending.description} was not delivered`)
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
case "WORKFLOW_CANCEL_REQUESTED":
|
|
495
|
+
this.cancelRequested = true;
|
|
496
|
+
this.root.cancel();
|
|
497
|
+
return;
|
|
498
|
+
default:
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
expectIssued(key, event) {
|
|
503
|
+
if (!this.issued.has(key))
|
|
504
|
+
this.failTask(
|
|
505
|
+
"NON_DETERMINISM",
|
|
506
|
+
`The history has ${event.type} (seq ${event.seq}) for ${key}, which the code never issued`,
|
|
507
|
+
void 0,
|
|
508
|
+
event.seq
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
settleActivity(event, activityId) {
|
|
512
|
+
this.expectIssued(`activity:${activityId}`, event);
|
|
513
|
+
const pending = this.activities.get(activityId);
|
|
514
|
+
if (!pending) return;
|
|
515
|
+
this.activities.delete(activityId);
|
|
516
|
+
pending.release?.();
|
|
517
|
+
const p = event.payload;
|
|
518
|
+
const failure = typeof p["failure"] === "object" && p["failure"] !== null && !Array.isArray(p["failure"]) ? p["failure"] : void 0;
|
|
519
|
+
const wrap = (cause) => new ActivityFailure(
|
|
520
|
+
`Activity ${activityId} (${pending.nodeType} ${pending.operation}) failed: ${cause.message}`,
|
|
521
|
+
activityId,
|
|
522
|
+
pending.nodeType,
|
|
523
|
+
pending.operation,
|
|
524
|
+
cause
|
|
525
|
+
);
|
|
526
|
+
switch (event.type) {
|
|
527
|
+
case "ACTIVITY_COMPLETED":
|
|
528
|
+
pending.resolve(p["result"] ?? null);
|
|
529
|
+
return;
|
|
530
|
+
case "ACTIVITY_FAILED":
|
|
531
|
+
pending.reject(
|
|
532
|
+
wrap(
|
|
533
|
+
failure ? failureToError(failure) : new ApplicationError("Activity failed", { errorType: "UNKNOWN" })
|
|
534
|
+
)
|
|
535
|
+
);
|
|
536
|
+
return;
|
|
537
|
+
case "ACTIVITY_TIMED_OUT": {
|
|
538
|
+
const timeoutType = typeof p["timeoutType"] === "string" ? p["timeoutType"] : "UNKNOWN";
|
|
539
|
+
pending.reject(
|
|
540
|
+
wrap(new TimeoutFailure(`Activity timed out (${timeoutType})`, timeoutType))
|
|
541
|
+
);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
default:
|
|
545
|
+
pending.reject(wrap(new CancelledFailure("Activity cancelled")));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
settleChild(event, childId) {
|
|
549
|
+
this.expectIssued(`child:${childId}`, event);
|
|
550
|
+
const child = this.children.get(childId);
|
|
551
|
+
if (!child) return;
|
|
552
|
+
const p = event.payload;
|
|
553
|
+
if (typeof p["childRunId"] === "string") child.runId = p["childRunId"];
|
|
554
|
+
if (event.type === "CHILD_RUN_STARTED") {
|
|
555
|
+
const started2 = child.started;
|
|
556
|
+
child.started = void 0;
|
|
557
|
+
started2?.resolve(child.runId);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
let outcome;
|
|
561
|
+
const failureValue = p["failure"];
|
|
562
|
+
const failure = typeof failureValue === "object" && failureValue !== null && !Array.isArray(failureValue) ? failureValue : void 0;
|
|
563
|
+
const wrap = (cause) => new ChildRunFailure(
|
|
564
|
+
`Child run ${childId} ${event.type.slice("CHILD_RUN_".length).toLowerCase().replace("_", " ")}: ${cause.message}`,
|
|
565
|
+
childId,
|
|
566
|
+
child.runId,
|
|
567
|
+
cause
|
|
568
|
+
);
|
|
569
|
+
switch (event.type) {
|
|
570
|
+
case "CHILD_RUN_COMPLETED":
|
|
571
|
+
outcome = { ok: true, value: p["result"] ?? null };
|
|
572
|
+
break;
|
|
573
|
+
case "CHILD_RUN_FAILED":
|
|
574
|
+
outcome = {
|
|
575
|
+
ok: false,
|
|
576
|
+
error: wrap(
|
|
577
|
+
failure ? failureToError(failure) : new ApplicationError("Child failed", { errorType: "UNKNOWN" })
|
|
578
|
+
)
|
|
579
|
+
};
|
|
580
|
+
break;
|
|
581
|
+
case "CHILD_RUN_CANCELLED":
|
|
582
|
+
outcome = { ok: false, error: wrap(new CancelledFailure("Child run cancelled")) };
|
|
583
|
+
break;
|
|
584
|
+
case "CHILD_RUN_TERMINATED":
|
|
585
|
+
outcome = {
|
|
586
|
+
ok: false,
|
|
587
|
+
error: wrap(
|
|
588
|
+
new TerminatedFailure(typeof p["reason"] === "string" ? p["reason"] : "Terminated")
|
|
589
|
+
)
|
|
590
|
+
};
|
|
591
|
+
break;
|
|
592
|
+
default: {
|
|
593
|
+
const timeoutType = typeof p["timeoutType"] === "string" ? p["timeoutType"] : "WORKFLOW_RUN";
|
|
594
|
+
outcome = {
|
|
595
|
+
ok: false,
|
|
596
|
+
error: wrap(new TimeoutFailure(`Child run timed out (${timeoutType})`, timeoutType))
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
child.settled = true;
|
|
601
|
+
child.outcome = outcome;
|
|
602
|
+
const started = child.started;
|
|
603
|
+
child.started = void 0;
|
|
604
|
+
if (started) {
|
|
605
|
+
if (outcome.ok) started.resolve(child.runId);
|
|
606
|
+
else started.reject(outcome.error);
|
|
607
|
+
}
|
|
608
|
+
const result = child.result;
|
|
609
|
+
child.result = void 0;
|
|
610
|
+
if (result) {
|
|
611
|
+
result.release?.();
|
|
612
|
+
if (outcome.ok) result.resolve(outcome.value);
|
|
613
|
+
else result.reject(outcome.error);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
dispatchSignal(name, payload) {
|
|
617
|
+
const handler = this.signalHandlers.get(name);
|
|
618
|
+
if (handler) {
|
|
619
|
+
this.runHandler(() => handler(payload));
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const waiters = this.signalWaiters.get(name);
|
|
623
|
+
const waiter = waiters?.shift();
|
|
624
|
+
if (waiter) {
|
|
625
|
+
waiter.release?.();
|
|
626
|
+
waiter.resolve(payload);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (this.defaultSignalHandler) {
|
|
630
|
+
const fallback = this.defaultSignalHandler;
|
|
631
|
+
this.runHandler(() => fallback(name, payload));
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const buffer = this.signalBuffers.get(name) ?? [];
|
|
635
|
+
buffer.push(payload);
|
|
636
|
+
this.signalBuffers.set(name, buffer);
|
|
637
|
+
this.signalOrder.push({ name, payload });
|
|
638
|
+
}
|
|
639
|
+
takeBuffered(name) {
|
|
640
|
+
const buffer = this.signalBuffers.get(name);
|
|
641
|
+
if (!buffer || buffer.length === 0) return void 0;
|
|
642
|
+
const payload = buffer.shift();
|
|
643
|
+
const position = this.signalOrder.findIndex((entry) => entry.name === name);
|
|
644
|
+
if (position >= 0) this.signalOrder.splice(position, 1);
|
|
645
|
+
return payload;
|
|
646
|
+
}
|
|
647
|
+
runHandler(call) {
|
|
648
|
+
let result;
|
|
649
|
+
try {
|
|
650
|
+
result = call();
|
|
651
|
+
} catch (error) {
|
|
652
|
+
this.onWorkflowError(error);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (result instanceof Promise) {
|
|
656
|
+
this.runningHandlers += 1;
|
|
657
|
+
result.then(
|
|
658
|
+
() => {
|
|
659
|
+
this.runningHandlers -= 1;
|
|
660
|
+
},
|
|
661
|
+
(error) => {
|
|
662
|
+
this.runningHandlers -= 1;
|
|
663
|
+
this.onWorkflowError(error);
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// ---- the workflow function ------------------------------------------------
|
|
669
|
+
startFunction(input) {
|
|
670
|
+
let promise;
|
|
671
|
+
try {
|
|
672
|
+
promise = Promise.resolve(this.definition.fn(this.context, input));
|
|
673
|
+
} catch (error) {
|
|
674
|
+
promise = Promise.reject(error);
|
|
675
|
+
}
|
|
676
|
+
promise.then(
|
|
677
|
+
(value) => this.onReturn(value),
|
|
678
|
+
(error) => this.onWorkflowError(error)
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
onReturn(value) {
|
|
682
|
+
if (this.closed || this.failure) return;
|
|
683
|
+
let output;
|
|
684
|
+
try {
|
|
685
|
+
output = toJson(value, "The workflow output");
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (error instanceof TypeError)
|
|
688
|
+
this.failTask("WORKFLOW_CODE_ERROR", messageOf(error), stackTextOf(error));
|
|
689
|
+
else this.onWorkflowError(error);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
this.pushTerminal({ type: "COMPLETE_WORKFLOW", output });
|
|
693
|
+
}
|
|
694
|
+
isFailureType(error) {
|
|
695
|
+
const listed = this.definition.options.failureErrorTypes ?? [];
|
|
696
|
+
return listed.some((type) => error instanceof type);
|
|
697
|
+
}
|
|
698
|
+
/** What an error thrown out of workflow code does (11 §2.25, §2.37). */
|
|
699
|
+
onWorkflowError(error) {
|
|
700
|
+
if (this.closed || this.failure) return;
|
|
701
|
+
if (error instanceof ContinueAsNewRequest) {
|
|
702
|
+
this.pushTerminal({ type: "CONTINUE_AS_NEW", input: error.input, options: error.options });
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const name = errorName(error);
|
|
706
|
+
if (error instanceof DeterminismViolation || name === "DeterminismViolation") {
|
|
707
|
+
this.failTask("NON_DETERMINISM", messageOf(error), stackTextOf(error), void 0, name);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (error instanceof HistoryLimitExceeded) {
|
|
711
|
+
this.failTask("HISTORY_LIMIT", messageOf(error), stackTextOf(error));
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (name === "InternalError" && /out of memory/i.test(messageOf(error))) {
|
|
715
|
+
this.failTask("ISOLATE_MEMORY", messageOf(error), stackTextOf(error), void 0, name);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (error instanceof PhaseCFeatureError) {
|
|
719
|
+
this.failTask("UNSUPPORTED_COMMAND", messageOf(error), stackTextOf(error));
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
if (this.cancelRequested && isCancellation(error)) {
|
|
723
|
+
this.pushTerminal({ type: "CANCEL_WORKFLOW" });
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
if (isRunFailure(error) || this.isFailureType(error)) {
|
|
727
|
+
this.pushTerminal({ type: "FAIL_WORKFLOW", failure: errorToFailure(error) });
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
this.failTask(
|
|
731
|
+
"WORKFLOW_CODE_ERROR",
|
|
732
|
+
`${name || "Error"}: ${messageOf(error)}`,
|
|
733
|
+
stackTextOf(error),
|
|
734
|
+
void 0,
|
|
735
|
+
name || void 0
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
// ---- commands -------------------------------------------------------------
|
|
739
|
+
currentScope() {
|
|
740
|
+
return this.syncScopes[this.syncScopes.length - 1] ?? this.root;
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Throw `HistoryLimitExceeded` from 95 % of the cap. It runs on replay too:
|
|
744
|
+
* the history length is the activation's cutoff and the command count is
|
|
745
|
+
* the activation's own, so replay sees what the first execution saw.
|
|
746
|
+
*/
|
|
747
|
+
checkLimit() {
|
|
748
|
+
const events = this.historyLength + this.commands.length;
|
|
749
|
+
if (events >= this.limitEvents * HISTORY_LIMIT_EXCEEDED_RATIO || this.historyBytes >= this.limitBytes * HISTORY_LIMIT_EXCEEDED_RATIO)
|
|
750
|
+
throw new HistoryLimitExceeded(
|
|
751
|
+
`The history has ${events} events and ${this.historyBytes} bytes, past 95 % of its limit (${this.limitEvents} events, ${this.limitBytes} bytes); continue as new`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
/** Issue a command; false when the run is already closed (the command is dropped). */
|
|
755
|
+
push(command) {
|
|
756
|
+
if (this.closed || this.failure) return false;
|
|
757
|
+
this.checkLimit();
|
|
758
|
+
this.commands.push(command);
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
pushTerminal(command) {
|
|
762
|
+
if (this.closed || this.failure) return;
|
|
763
|
+
this.commands.push(command);
|
|
764
|
+
this.closed = true;
|
|
765
|
+
}
|
|
766
|
+
stack() {
|
|
767
|
+
return this.captureStacks ? captureStack() : void 0;
|
|
768
|
+
}
|
|
769
|
+
never() {
|
|
770
|
+
return new Promise(() => void 0);
|
|
771
|
+
}
|
|
772
|
+
scheduleActivity(scope, operation, options) {
|
|
773
|
+
if (typeof operation !== "object" || operation === null || typeof operation.nodeType !== "string")
|
|
774
|
+
throw new TypeError("ctx.run needs a node operation (from nodes.* or the generated clients)");
|
|
775
|
+
if (operation.role === "trigger" || !isActivityNodeTypeAllowed(operation.nodeType))
|
|
776
|
+
throw new ApplicationError(
|
|
777
|
+
`${operation.nodeType} cannot run as a durable activity: only store and registry nodes and internal:http, internal:datatable, internal:set and internal:code can`,
|
|
778
|
+
{ type: "durable.activityTypeNotAllowed", errorType: "VALIDATION", nonRetryable: true }
|
|
779
|
+
);
|
|
780
|
+
const activityId = this.ids.next("a");
|
|
781
|
+
const operationName = options.operation ?? operation.operation ?? "execute";
|
|
782
|
+
const nodeVersion = options.nodeVersion ?? operation.version;
|
|
783
|
+
const cancellationType = options.cancellationType ?? "WAIT_CANCELLATION_COMPLETED";
|
|
784
|
+
const command = {
|
|
785
|
+
type: "SCHEDULE_ACTIVITY",
|
|
786
|
+
activityId,
|
|
787
|
+
kind: "node",
|
|
788
|
+
nodeType: operation.nodeType,
|
|
789
|
+
operation: operationName,
|
|
790
|
+
args: toJson(options.args, `The args of ${operation.nodeType}`),
|
|
791
|
+
retry: explicitRetryPolicy(options.retry)
|
|
792
|
+
};
|
|
793
|
+
if (nodeVersion !== void 0) command.nodeVersion = nodeVersion;
|
|
794
|
+
if (options.connection !== void 0) command.connection = options.connection;
|
|
795
|
+
const startToClose = optionalMs(options.startToCloseTimeout, "startToCloseTimeout");
|
|
796
|
+
const scheduleToClose = optionalMs(options.scheduleToCloseTimeout, "scheduleToCloseTimeout");
|
|
797
|
+
const scheduleToStart = optionalMs(options.scheduleToStartTimeout, "scheduleToStartTimeout");
|
|
798
|
+
const heartbeat = optionalMs(options.heartbeatTimeout, "heartbeatTimeout");
|
|
799
|
+
if (startToClose !== void 0) command.startToCloseTimeout = startToClose;
|
|
800
|
+
if (scheduleToClose !== void 0) command.scheduleToCloseTimeout = scheduleToClose;
|
|
801
|
+
if (scheduleToStart !== void 0) command.scheduleToStartTimeout = scheduleToStart;
|
|
802
|
+
if (heartbeat !== void 0) command.heartbeatTimeout = heartbeat;
|
|
803
|
+
if (options.cancellationType !== void 0) command.cancellationType = options.cancellationType;
|
|
804
|
+
if (options.priority !== void 0) command.priority = options.priority;
|
|
805
|
+
if (options.summary !== void 0) command.summary = options.summary;
|
|
806
|
+
if (options.runOn !== void 0) command.runOn = options.runOn;
|
|
807
|
+
if (options.headers !== void 0) command.headers = { ...options.headers };
|
|
808
|
+
if (scope.consideredCancelled)
|
|
809
|
+
return Promise.reject(new CancelledFailure("Scope is cancelled"));
|
|
810
|
+
if (!this.push(command)) return this.never();
|
|
811
|
+
this.issued.add(`activity:${activityId}`);
|
|
812
|
+
return new Promise((resolve, reject) => {
|
|
813
|
+
const entry = {
|
|
814
|
+
resolve,
|
|
815
|
+
reject,
|
|
816
|
+
description: `activity ${activityId} (${operation.nodeType} ${operationName})`,
|
|
817
|
+
nodeType: operation.nodeType,
|
|
818
|
+
operation: operationName,
|
|
819
|
+
cancellationType
|
|
820
|
+
};
|
|
821
|
+
const stack = this.stack();
|
|
822
|
+
if (stack !== void 0) entry.stack = stack;
|
|
823
|
+
entry.release = scope.onCancel(() => {
|
|
824
|
+
if (!this.activities.has(activityId)) return;
|
|
825
|
+
const cancelled = new ActivityFailure(
|
|
826
|
+
`Activity ${activityId} cancelled`,
|
|
827
|
+
activityId,
|
|
828
|
+
entry.nodeType,
|
|
829
|
+
entry.operation,
|
|
830
|
+
new CancelledFailure("Activity cancelled")
|
|
831
|
+
);
|
|
832
|
+
if (cancellationType !== "ABANDON")
|
|
833
|
+
this.push({ type: "REQUEST_CANCEL_ACTIVITY", activityId });
|
|
834
|
+
if (cancellationType !== "WAIT_CANCELLATION_COMPLETED") {
|
|
835
|
+
this.activities.delete(activityId);
|
|
836
|
+
reject(cancelled);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
this.activities.set(activityId, entry);
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
startTimer(scope, durationMs, summary) {
|
|
843
|
+
return this.timer(scope, durationMs, summary).promise;
|
|
844
|
+
}
|
|
845
|
+
/** Start a durable timer; `timerId` is undefined when no timer was needed or issued. */
|
|
846
|
+
timer(scope, durationMs, summary) {
|
|
847
|
+
if (durationMs <= 0) return { promise: Promise.resolve() };
|
|
848
|
+
if (scope.consideredCancelled)
|
|
849
|
+
return { promise: Promise.reject(new CancelledFailure("Scope is cancelled")) };
|
|
850
|
+
const timerId = this.ids.next("t");
|
|
851
|
+
const command = {
|
|
852
|
+
type: "START_TIMER",
|
|
853
|
+
timerId,
|
|
854
|
+
durationMs: Math.max(1, Math.round(durationMs))
|
|
855
|
+
};
|
|
856
|
+
if (summary !== void 0) command.summary = summary;
|
|
857
|
+
if (!this.push(command)) return { promise: this.never() };
|
|
858
|
+
this.issued.add(`timer:${timerId}`);
|
|
859
|
+
const promise = new Promise((resolve, reject) => {
|
|
860
|
+
const entry = {
|
|
861
|
+
resolve,
|
|
862
|
+
reject,
|
|
863
|
+
description: `timer ${timerId} (${durationMs} ms)`
|
|
864
|
+
};
|
|
865
|
+
const stack = this.stack();
|
|
866
|
+
if (stack !== void 0) entry.stack = stack;
|
|
867
|
+
entry.release = scope.onCancel(() => {
|
|
868
|
+
if (!this.timers.has(timerId)) return;
|
|
869
|
+
this.timers.delete(timerId);
|
|
870
|
+
this.push({ type: "CANCEL_TIMER", timerId });
|
|
871
|
+
reject(new CancelledFailure("Timer cancelled"));
|
|
872
|
+
});
|
|
873
|
+
this.timers.set(timerId, entry);
|
|
874
|
+
});
|
|
875
|
+
return { promise, timerId };
|
|
876
|
+
}
|
|
877
|
+
/** Cancel a pending timer the runtime started for itself (condition and scope timeouts). */
|
|
878
|
+
cancelOwnTimer(timerId) {
|
|
879
|
+
if (timerId === void 0) return;
|
|
880
|
+
const pending = this.timers.get(timerId);
|
|
881
|
+
if (!pending) return;
|
|
882
|
+
this.timers.delete(timerId);
|
|
883
|
+
pending.release?.();
|
|
884
|
+
this.push({ type: "CANCEL_TIMER", timerId });
|
|
885
|
+
}
|
|
886
|
+
condition(scope, predicate, timeout) {
|
|
887
|
+
if (predicate()) return Promise.resolve(true);
|
|
888
|
+
if (scope.consideredCancelled)
|
|
889
|
+
return Promise.reject(new CancelledFailure("Scope is cancelled"));
|
|
890
|
+
return new Promise((resolve, reject) => {
|
|
891
|
+
const condition = { predicate, resolve, reject };
|
|
892
|
+
const stack = this.stack();
|
|
893
|
+
if (stack !== void 0) condition.stack = stack;
|
|
894
|
+
this.conditions.push(condition);
|
|
895
|
+
if (timeout !== void 0) {
|
|
896
|
+
const timer = this.timer(scope, toMs(timeout, "condition timeout"), void 0);
|
|
897
|
+
if (timer.timerId !== void 0) condition.timerId = timer.timerId;
|
|
898
|
+
timer.promise.then(
|
|
899
|
+
() => {
|
|
900
|
+
if (!this.conditions.includes(condition)) return;
|
|
901
|
+
this.conditions = this.conditions.filter((entry) => entry !== condition);
|
|
902
|
+
condition.release?.();
|
|
903
|
+
resolve(false);
|
|
904
|
+
},
|
|
905
|
+
() => void 0
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
condition.release = scope.onCancel(() => {
|
|
909
|
+
if (!this.conditions.includes(condition)) return;
|
|
910
|
+
this.conditions = this.conditions.filter((entry) => entry !== condition);
|
|
911
|
+
this.cancelOwnTimer(condition.timerId);
|
|
912
|
+
reject(new CancelledFailure("Condition cancelled"));
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
/** Resolve conditions whose predicate now holds; true when any did. */
|
|
917
|
+
checkConditions() {
|
|
918
|
+
let fired = false;
|
|
919
|
+
for (const condition of [...this.conditions]) {
|
|
920
|
+
let holds = false;
|
|
921
|
+
try {
|
|
922
|
+
holds = condition.predicate();
|
|
923
|
+
} catch (error) {
|
|
924
|
+
this.conditions = this.conditions.filter((entry) => entry !== condition);
|
|
925
|
+
condition.release?.();
|
|
926
|
+
condition.reject(error);
|
|
927
|
+
fired = true;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (!holds) continue;
|
|
931
|
+
this.conditions = this.conditions.filter((entry) => entry !== condition);
|
|
932
|
+
condition.release?.();
|
|
933
|
+
this.cancelOwnTimer(condition.timerId);
|
|
934
|
+
condition.resolve(true);
|
|
935
|
+
fired = true;
|
|
936
|
+
}
|
|
937
|
+
return fired;
|
|
938
|
+
}
|
|
939
|
+
signalReceived(scope, signal) {
|
|
940
|
+
const name = signalName(signal);
|
|
941
|
+
const buffered = this.takeBuffered(name);
|
|
942
|
+
if (buffered !== void 0) return Promise.resolve(buffered);
|
|
943
|
+
return new Promise((resolve, reject) => {
|
|
944
|
+
const entry = {
|
|
945
|
+
resolve,
|
|
946
|
+
reject,
|
|
947
|
+
description: `signal ${name}`
|
|
948
|
+
};
|
|
949
|
+
const stack = this.stack();
|
|
950
|
+
if (stack !== void 0) entry.stack = stack;
|
|
951
|
+
const waiters = this.signalWaiters.get(name) ?? [];
|
|
952
|
+
waiters.push(entry);
|
|
953
|
+
this.signalWaiters.set(name, waiters);
|
|
954
|
+
entry.release = scope.onCancel(() => {
|
|
955
|
+
const list = this.signalWaiters.get(name) ?? [];
|
|
956
|
+
const position = list.indexOf(entry);
|
|
957
|
+
if (position < 0) return;
|
|
958
|
+
list.splice(position, 1);
|
|
959
|
+
reject(new CancelledFailure("Signal wait cancelled"));
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
onSignal(signal, handler) {
|
|
964
|
+
const name = signalName(signal);
|
|
965
|
+
this.signalHandlers.set(name, handler);
|
|
966
|
+
let payload = this.takeBuffered(name);
|
|
967
|
+
while (payload !== void 0) {
|
|
968
|
+
const value = payload;
|
|
969
|
+
this.runHandler(() => handler(value));
|
|
970
|
+
payload = this.takeBuffered(name);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
setDefaultSignalHandler(handler) {
|
|
974
|
+
this.defaultSignalHandler = handler;
|
|
975
|
+
const buffered = [...this.signalOrder];
|
|
976
|
+
for (const entry of buffered) {
|
|
977
|
+
if (this.signalHandlers.has(entry.name)) continue;
|
|
978
|
+
const payload = this.takeBuffered(entry.name);
|
|
979
|
+
if (payload !== void 0) this.runHandler(() => handler(entry.name, payload));
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
childTarget(target) {
|
|
983
|
+
if (typeof target === "string") {
|
|
984
|
+
if (target === "") throw new TypeError("ctx.child needs a workflow key or function");
|
|
985
|
+
return { workflowKey: target };
|
|
986
|
+
}
|
|
987
|
+
if (isDurableWorkflow(target)) {
|
|
988
|
+
const exportName = this.exportNames.get(target);
|
|
989
|
+
if (exportName === void 0)
|
|
990
|
+
throw new TypeError(
|
|
991
|
+
`workflow("${target.name}") is not exported by any module of this bundle; export it to start it as a child (in the test environment, pass it in \`workflows\`)`
|
|
992
|
+
);
|
|
993
|
+
return { workflowFn: exportName };
|
|
994
|
+
}
|
|
995
|
+
throw new TypeError("ctx.child needs a workflow key or a workflow() function");
|
|
996
|
+
}
|
|
997
|
+
childCommand(childId, target, options, defaultPolicy) {
|
|
998
|
+
const opts = { parentClosePolicy: options.parentClosePolicy ?? defaultPolicy };
|
|
999
|
+
if (options.cancellationType !== void 0) opts["cancellationType"] = options.cancellationType;
|
|
1000
|
+
if (options.taskQueue !== void 0) opts["taskQueue"] = options.taskQueue;
|
|
1001
|
+
if (options.workflowId !== void 0) opts["workflowId"] = options.workflowId;
|
|
1002
|
+
if (options.priority !== void 0) opts["priority"] = options.priority;
|
|
1003
|
+
if (options.summary !== void 0) opts["summary"] = options.summary;
|
|
1004
|
+
if (options.details !== void 0) opts["details"] = options.details;
|
|
1005
|
+
if (options.context !== void 0)
|
|
1006
|
+
opts["context"] = toJson(options.context, "The child context");
|
|
1007
|
+
const durations = [
|
|
1008
|
+
["startDelay", "startDelay"],
|
|
1009
|
+
["executionTimeout", "executionTimeout"],
|
|
1010
|
+
["runTimeout", "runTimeout"],
|
|
1011
|
+
["workflowTaskTimeout", "workflowTaskTimeout"]
|
|
1012
|
+
];
|
|
1013
|
+
for (const [key, name] of durations) {
|
|
1014
|
+
const value = options[key];
|
|
1015
|
+
if (value !== void 0 && (typeof value === "number" || typeof value === "string"))
|
|
1016
|
+
opts[name] = toMs(value, name);
|
|
1017
|
+
}
|
|
1018
|
+
const command = {
|
|
1019
|
+
type: "START_CHILD_RUN",
|
|
1020
|
+
childId,
|
|
1021
|
+
...this.childTarget(target),
|
|
1022
|
+
input: toJson(options.input, "The child input"),
|
|
1023
|
+
options: {
|
|
1024
|
+
parentClosePolicy: opts["parentClosePolicy"] === "ABANDON" ? "ABANDON" : opts["parentClosePolicy"] === "REQUEST_CANCEL" ? "REQUEST_CANCEL" : "TERMINATE",
|
|
1025
|
+
...opts
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
if (options.version !== void 0) command.version = options.version;
|
|
1029
|
+
if (options.headers !== void 0) command.headers = { ...options.headers };
|
|
1030
|
+
return command;
|
|
1031
|
+
}
|
|
1032
|
+
startChildRun(scope, target, options, defaultPolicy) {
|
|
1033
|
+
if (scope.consideredCancelled) return void 0;
|
|
1034
|
+
const childId = this.ids.next("c");
|
|
1035
|
+
const command = this.childCommand(childId, target, options, defaultPolicy);
|
|
1036
|
+
if (!this.push(command)) return void 0;
|
|
1037
|
+
this.issued.add(`child:${childId}`);
|
|
1038
|
+
const child = { childId, settled: false, scope };
|
|
1039
|
+
if (options.cancellationType !== void 0) child.cancellationType = options.cancellationType;
|
|
1040
|
+
this.children.set(childId, child);
|
|
1041
|
+
return child;
|
|
1042
|
+
}
|
|
1043
|
+
/** What cancelling the scope of a child's awaitable does (see `ChildOptions.cancellationType`). */
|
|
1044
|
+
onChildCancel(child, entry) {
|
|
1045
|
+
return () => {
|
|
1046
|
+
if (child.settled) return;
|
|
1047
|
+
const type = child.cancellationType ?? CHILD_DEFAULT_CANCELLATION_TYPE;
|
|
1048
|
+
const error = new ChildRunFailure(
|
|
1049
|
+
`Child run ${child.childId} cancelled`,
|
|
1050
|
+
child.childId,
|
|
1051
|
+
child.runId,
|
|
1052
|
+
new CancelledFailure("Child run cancelled")
|
|
1053
|
+
);
|
|
1054
|
+
if (type === "ABANDON") {
|
|
1055
|
+
child.result = void 0;
|
|
1056
|
+
entry.reject(error);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const externalId = this.ids.next("x");
|
|
1060
|
+
if (this.push({
|
|
1061
|
+
type: "REQUEST_CANCEL_EXTERNAL_RUN",
|
|
1062
|
+
externalId,
|
|
1063
|
+
target: { childId: child.childId }
|
|
1064
|
+
})) {
|
|
1065
|
+
this.issued.add(`external:${externalId}`);
|
|
1066
|
+
if (type === "WAIT_CANCELLATION_REQUESTED") {
|
|
1067
|
+
this.externals.set(externalId, {
|
|
1068
|
+
resolve: () => {
|
|
1069
|
+
child.result = void 0;
|
|
1070
|
+
entry.reject(error);
|
|
1071
|
+
},
|
|
1072
|
+
reject: () => {
|
|
1073
|
+
child.result = void 0;
|
|
1074
|
+
entry.reject(error);
|
|
1075
|
+
},
|
|
1076
|
+
description: `cancel of child ${child.childId}`
|
|
1077
|
+
});
|
|
1078
|
+
} else {
|
|
1079
|
+
this.externals.set(externalId, {
|
|
1080
|
+
resolve: () => void 0,
|
|
1081
|
+
reject: () => void 0,
|
|
1082
|
+
description: `cancel of child ${child.childId}`
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (type === "TRY_CANCEL") {
|
|
1087
|
+
child.result = void 0;
|
|
1088
|
+
entry.reject(error);
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
child(scope, target, options) {
|
|
1093
|
+
const child = this.startChildRun(
|
|
1094
|
+
scope,
|
|
1095
|
+
target,
|
|
1096
|
+
options,
|
|
1097
|
+
CHILD_DEFAULT_PARENT_CLOSE_POLICY.child
|
|
1098
|
+
);
|
|
1099
|
+
if (!child)
|
|
1100
|
+
return scope.consideredCancelled ? Promise.reject(new CancelledFailure("Scope is cancelled")) : this.never();
|
|
1101
|
+
return this.childResult(child);
|
|
1102
|
+
}
|
|
1103
|
+
childResult(child) {
|
|
1104
|
+
if (child.settled && child.outcome) {
|
|
1105
|
+
const outcome = child.outcome;
|
|
1106
|
+
return outcome.ok ? Promise.resolve(outcome.value) : Promise.reject(outcome.error);
|
|
1107
|
+
}
|
|
1108
|
+
return new Promise((resolve, reject) => {
|
|
1109
|
+
const entry = {
|
|
1110
|
+
resolve,
|
|
1111
|
+
reject,
|
|
1112
|
+
description: `child run ${child.childId}`
|
|
1113
|
+
};
|
|
1114
|
+
const stack = this.stack();
|
|
1115
|
+
if (stack !== void 0) entry.stack = stack;
|
|
1116
|
+
entry.release = child.scope.onCancel(this.onChildCancel(child, entry));
|
|
1117
|
+
child.result = entry;
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
startChild(scope, target, options) {
|
|
1121
|
+
const child = this.startChildRun(
|
|
1122
|
+
scope,
|
|
1123
|
+
target,
|
|
1124
|
+
options,
|
|
1125
|
+
CHILD_DEFAULT_PARENT_CLOSE_POLICY.startChild
|
|
1126
|
+
);
|
|
1127
|
+
if (!child)
|
|
1128
|
+
return scope.consideredCancelled ? Promise.reject(new CancelledFailure("Scope is cancelled")) : this.never();
|
|
1129
|
+
return new Promise((resolve, reject) => {
|
|
1130
|
+
const entry = {
|
|
1131
|
+
resolve,
|
|
1132
|
+
reject,
|
|
1133
|
+
description: `start of child run ${child.childId}`
|
|
1134
|
+
};
|
|
1135
|
+
const stack = this.stack();
|
|
1136
|
+
if (stack !== void 0) entry.stack = stack;
|
|
1137
|
+
child.started = entry;
|
|
1138
|
+
}).then((runId) => {
|
|
1139
|
+
const handle = {
|
|
1140
|
+
childId: child.childId,
|
|
1141
|
+
runId: runId ?? child.runId ?? "",
|
|
1142
|
+
result: () => this.childResult(child),
|
|
1143
|
+
signal: (signal, payload) => this.signalExternal({ childId: child.childId }, signalName(signal), payload),
|
|
1144
|
+
cancel: () => this.cancelExternal({ childId: child.childId })
|
|
1145
|
+
};
|
|
1146
|
+
return handle;
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
signalExternal(target, name, payload) {
|
|
1150
|
+
const externalId = this.ids.next("x");
|
|
1151
|
+
const command = {
|
|
1152
|
+
type: "SIGNAL_RUN",
|
|
1153
|
+
externalId,
|
|
1154
|
+
target,
|
|
1155
|
+
name,
|
|
1156
|
+
payload: toJson(payload, `The payload of signal ${name}`)
|
|
1157
|
+
};
|
|
1158
|
+
if (!this.push(command)) return this.never();
|
|
1159
|
+
this.issued.add(`external:${externalId}`);
|
|
1160
|
+
return new Promise((resolve, reject) => {
|
|
1161
|
+
const entry = {
|
|
1162
|
+
resolve,
|
|
1163
|
+
reject,
|
|
1164
|
+
description: `signal ${name}`
|
|
1165
|
+
};
|
|
1166
|
+
const stack = this.stack();
|
|
1167
|
+
if (stack !== void 0) entry.stack = stack;
|
|
1168
|
+
this.externals.set(externalId, entry);
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
cancelExternal(target) {
|
|
1172
|
+
const externalId = this.ids.next("x");
|
|
1173
|
+
if (!this.push({ type: "REQUEST_CANCEL_EXTERNAL_RUN", externalId, target }))
|
|
1174
|
+
return this.never();
|
|
1175
|
+
this.issued.add(`external:${externalId}`);
|
|
1176
|
+
return new Promise((resolve, reject) => {
|
|
1177
|
+
const entry = {
|
|
1178
|
+
resolve,
|
|
1179
|
+
reject,
|
|
1180
|
+
description: `cancel request ${externalId}`
|
|
1181
|
+
};
|
|
1182
|
+
const stack = this.stack();
|
|
1183
|
+
if (stack !== void 0) entry.stack = stack;
|
|
1184
|
+
this.externals.set(externalId, entry);
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
sideEffect(fn) {
|
|
1188
|
+
const sideEffectId = this.ids.next("s");
|
|
1189
|
+
let value;
|
|
1190
|
+
if (this.replaying && this.recordedSideEffects.has(sideEffectId)) {
|
|
1191
|
+
value = this.recordedSideEffects.get(sideEffectId) ?? null;
|
|
1192
|
+
} else {
|
|
1193
|
+
value = toJson(fn(), "The side effect value");
|
|
1194
|
+
}
|
|
1195
|
+
this.push({ type: "RECORD_SIDE_EFFECT", sideEffectId, value });
|
|
1196
|
+
return toJson(value, "The side effect value");
|
|
1197
|
+
}
|
|
1198
|
+
patched(patchId, deprecated) {
|
|
1199
|
+
if (typeof patchId !== "string" || patchId === "") throw new TypeError("A patch needs an id");
|
|
1200
|
+
const known = this.patches.get(patchId);
|
|
1201
|
+
if (known !== void 0) return known;
|
|
1202
|
+
const markerId = patchMarkerId(patchId);
|
|
1203
|
+
let result;
|
|
1204
|
+
if (this.replaying) {
|
|
1205
|
+
result = this.recordedMarkers.has(markerId);
|
|
1206
|
+
if (result)
|
|
1207
|
+
this.push({
|
|
1208
|
+
type: "RECORD_MARKER",
|
|
1209
|
+
markerId,
|
|
1210
|
+
data: { patchId, ...deprecated ? { deprecated: true } : {} }
|
|
1211
|
+
});
|
|
1212
|
+
} else {
|
|
1213
|
+
result = true;
|
|
1214
|
+
if (!this.recordedMarkers.has(markerId)) {
|
|
1215
|
+
this.push({
|
|
1216
|
+
type: "RECORD_MARKER",
|
|
1217
|
+
markerId,
|
|
1218
|
+
data: { patchId, ...deprecated ? { deprecated: true } : {} }
|
|
1219
|
+
});
|
|
1220
|
+
this.recordedMarkers.add(markerId);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
this.patches.set(patchId, result);
|
|
1224
|
+
return result;
|
|
1225
|
+
}
|
|
1226
|
+
setCurrentDetails(markdown) {
|
|
1227
|
+
if (typeof markdown !== "string") throw new TypeError("setCurrentDetails takes Markdown text");
|
|
1228
|
+
this.currentDetails = markdown;
|
|
1229
|
+
this.push({
|
|
1230
|
+
type: "RECORD_MARKER",
|
|
1231
|
+
markerId: this.ids.next("m"),
|
|
1232
|
+
data: { currentDetails: markdown }
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
upsertContext(entries) {
|
|
1236
|
+
this.push({
|
|
1237
|
+
type: "UPSERT_CONTEXT",
|
|
1238
|
+
entries: toJson(entries, "The context entries")
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
/** The request a `ctx.continueAsNew` call throws; the run ends with `CONTINUE_AS_NEW`. */
|
|
1242
|
+
continueAsNew(input, options) {
|
|
1243
|
+
const opts = {};
|
|
1244
|
+
if (options.taskQueue !== void 0) opts["taskQueue"] = options.taskQueue;
|
|
1245
|
+
if (options.retry !== void 0)
|
|
1246
|
+
opts["retry"] = toJson(explicitRetryPolicy(options.retry), "retry");
|
|
1247
|
+
if (options.workflowTaskTimeout !== void 0)
|
|
1248
|
+
opts["workflowTaskTimeout"] = toMs(options.workflowTaskTimeout, "workflowTaskTimeout");
|
|
1249
|
+
if (options.context !== void 0) opts["context"] = toJson(options.context, "context");
|
|
1250
|
+
if (options.versioningBehavior !== void 0)
|
|
1251
|
+
opts["versioningBehavior"] = options.versioningBehavior;
|
|
1252
|
+
return new ContinueAsNewRequest(toJson(input, "The continue-as-new input"), opts);
|
|
1253
|
+
}
|
|
1254
|
+
async cancellationScope(parent, fn, options) {
|
|
1255
|
+
const scope = new Scope(parent, options.nonCancellable === true);
|
|
1256
|
+
let timerId;
|
|
1257
|
+
if (options.timeout !== void 0) {
|
|
1258
|
+
const timer = this.timer(parent, toMs(options.timeout, "scope timeout"), void 0);
|
|
1259
|
+
timerId = timer.timerId;
|
|
1260
|
+
timer.promise.then(
|
|
1261
|
+
() => scope.cancel(),
|
|
1262
|
+
() => void 0
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
this.syncScopes.push(scope);
|
|
1266
|
+
let promise;
|
|
1267
|
+
try {
|
|
1268
|
+
promise = Promise.resolve(fn(this.contextFor(scope)));
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
promise = Promise.reject(error);
|
|
1271
|
+
} finally {
|
|
1272
|
+
this.syncScopes.pop();
|
|
1273
|
+
}
|
|
1274
|
+
try {
|
|
1275
|
+
return await promise;
|
|
1276
|
+
} finally {
|
|
1277
|
+
scope.detach();
|
|
1278
|
+
if (timerId !== void 0 && this.timers.has(timerId)) this.cancelOwnTimer(timerId);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
/** Waits for the signal handlers regardless of cancellation, as Temporal does. */
|
|
1282
|
+
allHandlersFinished() {
|
|
1283
|
+
return this.condition(this.detachedScope, () => this.runningHandlers === 0, void 0).then(
|
|
1284
|
+
() => void 0
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
// ---- queries --------------------------------------------------------------
|
|
1288
|
+
pendingStacks() {
|
|
1289
|
+
const out = [];
|
|
1290
|
+
const add = (entry, fallback) => {
|
|
1291
|
+
if (!entry) return;
|
|
1292
|
+
out.push({ description: entry.description ?? fallback, stack: entry.stack ?? "" });
|
|
1293
|
+
};
|
|
1294
|
+
for (const entry of this.activities.values()) add(entry, "activity");
|
|
1295
|
+
for (const entry of this.timers.values()) add(entry, "timer");
|
|
1296
|
+
for (const child of this.children.values()) {
|
|
1297
|
+
add(child.started, "child start");
|
|
1298
|
+
add(child.result, "child result");
|
|
1299
|
+
}
|
|
1300
|
+
for (const entry of this.externals.values()) add(entry, "external");
|
|
1301
|
+
for (const list of this.signalWaiters.values()) for (const entry of list) add(entry, "signal");
|
|
1302
|
+
for (const condition of this.conditions)
|
|
1303
|
+
add(
|
|
1304
|
+
{
|
|
1305
|
+
description: "condition",
|
|
1306
|
+
...condition.stack !== void 0 ? { stack: condition.stack } : {}
|
|
1307
|
+
},
|
|
1308
|
+
"condition"
|
|
1309
|
+
);
|
|
1310
|
+
return out;
|
|
1311
|
+
}
|
|
1312
|
+
answerQueries() {
|
|
1313
|
+
return (this.request.queries ?? []).map((query) => {
|
|
1314
|
+
if (!BUILT_IN_QUERIES.includes(query.name))
|
|
1315
|
+
return {
|
|
1316
|
+
queryId: query.queryId,
|
|
1317
|
+
error: `Unknown query ${String(query.name)}; custom queries are Phase C`
|
|
1318
|
+
};
|
|
1319
|
+
if (this.failure)
|
|
1320
|
+
return { queryId: query.queryId, error: `The replay failed: ${this.failure.message}` };
|
|
1321
|
+
if (query.name === "__stack_trace")
|
|
1322
|
+
return {
|
|
1323
|
+
queryId: query.queryId,
|
|
1324
|
+
value: this.pendingStacks().map((entry) => `${entry.description}
|
|
1325
|
+
${entry.stack}`).join("\n\n")
|
|
1326
|
+
};
|
|
1327
|
+
if (query.name === "__enhanced_stack_trace")
|
|
1328
|
+
return {
|
|
1329
|
+
queryId: query.queryId,
|
|
1330
|
+
value: {
|
|
1331
|
+
sdk: { name: SDK_NAME, version: this.sdkVersion },
|
|
1332
|
+
stacks: this.pendingStacks().map((entry) => ({
|
|
1333
|
+
description: entry.description,
|
|
1334
|
+
locations: parseStack(entry.stack).map((location) => {
|
|
1335
|
+
const out = {};
|
|
1336
|
+
if (location.filePath !== void 0) out["filePath"] = location.filePath;
|
|
1337
|
+
if (location.line !== void 0) out["line"] = location.line;
|
|
1338
|
+
if (location.column !== void 0) out["column"] = location.column;
|
|
1339
|
+
if (location.functionName !== void 0)
|
|
1340
|
+
out["functionName"] = location.functionName;
|
|
1341
|
+
return out;
|
|
1342
|
+
})
|
|
1343
|
+
}))
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
const options = this.definition.options;
|
|
1347
|
+
const describe = (items) => (items ?? []).map((item) => ({ name: item.name, description: item.description ?? null }));
|
|
1348
|
+
return {
|
|
1349
|
+
queryId: query.queryId,
|
|
1350
|
+
value: {
|
|
1351
|
+
definition: {
|
|
1352
|
+
name: this.definition.name,
|
|
1353
|
+
signals: describe(options.signals),
|
|
1354
|
+
queries: describe(options.queries),
|
|
1355
|
+
updates: describe(options.updates)
|
|
1356
|
+
},
|
|
1357
|
+
currentDetails: this.currentDetails ?? null
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
// ---- the context ----------------------------------------------------------
|
|
1363
|
+
contextFor(bound) {
|
|
1364
|
+
const scope = () => bound ?? this.currentScope();
|
|
1365
|
+
const log = (level) => (message, data) => {
|
|
1366
|
+
if (this.replaying) return;
|
|
1367
|
+
const line = { level, message: String(message) };
|
|
1368
|
+
if (data !== void 0) line.data = toJson(data, "The log data");
|
|
1369
|
+
this.logs.push(line);
|
|
1370
|
+
};
|
|
1371
|
+
const logger = {
|
|
1372
|
+
debug: log("debug"),
|
|
1373
|
+
info: log("info"),
|
|
1374
|
+
warn: log("warn"),
|
|
1375
|
+
error: log("error")
|
|
1376
|
+
};
|
|
1377
|
+
const scopeFn = ((fn, options = {}) => this.cancellationScope(scope(), fn, options));
|
|
1378
|
+
scopeFn.current = () => scope();
|
|
1379
|
+
const phaseC = (feature) => () => {
|
|
1380
|
+
throw new PhaseCFeatureError(feature);
|
|
1381
|
+
};
|
|
1382
|
+
const context = {
|
|
1383
|
+
info: this.info,
|
|
1384
|
+
log: logger,
|
|
1385
|
+
run: (operation, options) => this.scheduleActivity(scope(), operation, options),
|
|
1386
|
+
sleep: (duration, options = {}) => this.startTimer(scope(), toMs(duration, "sleep duration"), options.summary),
|
|
1387
|
+
condition: (predicate, timeout) => this.condition(scope(), predicate, timeout),
|
|
1388
|
+
signalReceived: (signal) => this.signalReceived(scope(), signal),
|
|
1389
|
+
onSignal: (signal, handler) => this.onSignal(signal, handler),
|
|
1390
|
+
setDefaultSignalHandler: (handler) => this.setDefaultSignalHandler(handler),
|
|
1391
|
+
child: (target, options = {}) => this.child(scope(), target, options),
|
|
1392
|
+
startChild: (target, options = {}) => this.startChild(scope(), target, options),
|
|
1393
|
+
signalRun: (runId, signal, payload) => this.signalExternal({ runId }, signalName(signal), payload),
|
|
1394
|
+
signalParent: (signal, payload) => this.signalExternal({ parent: true }, signalName(signal), payload),
|
|
1395
|
+
getRunHandle: (runId) => ({
|
|
1396
|
+
runId,
|
|
1397
|
+
signal: (signal, payload) => this.signalExternal({ runId }, signalName(signal), payload),
|
|
1398
|
+
cancel: () => this.cancelExternal({ runId })
|
|
1399
|
+
}),
|
|
1400
|
+
cancelRun: (runId) => this.cancelExternal({ runId }),
|
|
1401
|
+
now: () => this.clock,
|
|
1402
|
+
random: () => this.random(),
|
|
1403
|
+
uuid: () => uuidFrom(this.random),
|
|
1404
|
+
sideEffect: (fn) => this.sideEffect(fn),
|
|
1405
|
+
patched: (patchId) => this.patched(patchId, false),
|
|
1406
|
+
deprecatePatch: (patchId) => {
|
|
1407
|
+
this.patched(patchId, true);
|
|
1408
|
+
},
|
|
1409
|
+
setCurrentDetails: (markdown) => this.setCurrentDetails(markdown),
|
|
1410
|
+
upsertContext: (entries) => this.upsertContext(entries),
|
|
1411
|
+
continueAsNew: (input, options = {}) => {
|
|
1412
|
+
try {
|
|
1413
|
+
return Promise.reject(this.continueAsNew(input, options));
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
return Promise.reject(error);
|
|
1416
|
+
}
|
|
1417
|
+
},
|
|
1418
|
+
cancellationScope: scopeFn,
|
|
1419
|
+
nonCancellable: (fn) => this.cancellationScope(scope(), fn, { nonCancellable: true }),
|
|
1420
|
+
onCancel: (fn) => {
|
|
1421
|
+
scope().onCancel(() => {
|
|
1422
|
+
try {
|
|
1423
|
+
fn();
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
this.onWorkflowError(error);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
},
|
|
1429
|
+
isCancellation: (error) => isCancellation(error),
|
|
1430
|
+
saga: () => {
|
|
1431
|
+
const compensations = [];
|
|
1432
|
+
return {
|
|
1433
|
+
add: (compensation) => {
|
|
1434
|
+
compensations.push(compensation);
|
|
1435
|
+
},
|
|
1436
|
+
// Each compensation runs in its own non-cancellable scope, so it
|
|
1437
|
+
// still runs after a cancel request; it gets that scope's `ctx`.
|
|
1438
|
+
compensate: async () => {
|
|
1439
|
+
const pending = [...compensations].reverse();
|
|
1440
|
+
compensations.length = 0;
|
|
1441
|
+
for (const compensation of pending)
|
|
1442
|
+
await this.cancellationScope(
|
|
1443
|
+
scope(),
|
|
1444
|
+
async (scoped) => {
|
|
1445
|
+
await compensation(scoped);
|
|
1446
|
+
},
|
|
1447
|
+
{ nonCancellable: true }
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
},
|
|
1452
|
+
allHandlersFinished: () => this.allHandlersFinished(),
|
|
1453
|
+
activity: phaseC("ctx.activity (user activities)"),
|
|
1454
|
+
localActivity: phaseC(
|
|
1455
|
+
"ctx.localActivity (local activities)"
|
|
1456
|
+
),
|
|
1457
|
+
handleQuery: phaseC("ctx.handleQuery (custom queries)"),
|
|
1458
|
+
handleUpdate: phaseC("ctx.handleUpdate (updates)"),
|
|
1459
|
+
upsertSearchAttributes: phaseC("ctx.upsertSearchAttributes (search attributes)")
|
|
1460
|
+
};
|
|
1461
|
+
return Object.freeze(context);
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1465
|
+
// src/runtime/index.ts
|
|
1466
|
+
var RUNTIME_GLOBAL = "__fluidgridsRuntime";
|
|
1467
|
+
function buildWorkflowRegistry(modules) {
|
|
1468
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1469
|
+
const exportNames = /* @__PURE__ */ new Map();
|
|
1470
|
+
const ambiguous = /* @__PURE__ */ new Set();
|
|
1471
|
+
for (const [, namespace] of modules) {
|
|
1472
|
+
for (const exportName of Object.keys(namespace).sort()) {
|
|
1473
|
+
const value = namespace[exportName];
|
|
1474
|
+
if (!isDurableWorkflow(value)) continue;
|
|
1475
|
+
const existing = byName.get(exportName);
|
|
1476
|
+
if (existing && existing !== value) ambiguous.add(exportName);
|
|
1477
|
+
byName.set(exportName, value);
|
|
1478
|
+
if (!exportNames.has(value)) exportNames.set(value, exportName);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return { byName, exportNames, ambiguous };
|
|
1482
|
+
}
|
|
1483
|
+
function failedResult(cause, message) {
|
|
1484
|
+
return {
|
|
1485
|
+
protocol: HISTORY_PROTOCOL,
|
|
1486
|
+
outcome: "failed",
|
|
1487
|
+
commands: [],
|
|
1488
|
+
failure: { cause, message },
|
|
1489
|
+
logs: []
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
function isObject(value) {
|
|
1493
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1494
|
+
}
|
|
1495
|
+
function checkRequest(value) {
|
|
1496
|
+
if (!isObject(value)) return "the request is not an object";
|
|
1497
|
+
if (typeof value["workflowFn"] !== "string" || value["workflowFn"] === "")
|
|
1498
|
+
return "workflowFn is missing";
|
|
1499
|
+
if (value["kind"] !== "task" && value["kind"] !== "query" && value["kind"] !== "verify")
|
|
1500
|
+
return "kind must be task, query or verify";
|
|
1501
|
+
const info = value["info"];
|
|
1502
|
+
if (!isObject(info) || typeof info["runId"] !== "string") return "info.runId is missing";
|
|
1503
|
+
if (!Array.isArray(value["history"])) return "history must be an array";
|
|
1504
|
+
for (const [index, event] of value["history"].entries()) {
|
|
1505
|
+
if (!isObject(event) || typeof event["type"] !== "string" || typeof event["seq"] !== "number" || !isObject(event["payload"]) || typeof event["createdAt"] !== "string")
|
|
1506
|
+
return `history[${index}] is not an event`;
|
|
1507
|
+
}
|
|
1508
|
+
if (typeof value["nowMs"] !== "number" || !Number.isFinite(value["nowMs"]))
|
|
1509
|
+
return "nowMs must be a number";
|
|
1510
|
+
return value;
|
|
1511
|
+
}
|
|
1512
|
+
function createWorkflowRuntime(registry, shims = void 0) {
|
|
1513
|
+
let execution;
|
|
1514
|
+
let early;
|
|
1515
|
+
const internalFailure = (error) => {
|
|
1516
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1517
|
+
const stack = error instanceof Error ? error.stack : void 0;
|
|
1518
|
+
if (error instanceof HistoryLimitExceeded) execution?.failTask("HISTORY_LIMIT", message, stack);
|
|
1519
|
+
else if (error instanceof DeterminismViolation || error instanceof Error && error.name === "DeterminismViolation")
|
|
1520
|
+
execution?.failTask("NON_DETERMINISM", message, stack);
|
|
1521
|
+
else execution?.failTask("INTERNAL", message, stack);
|
|
1522
|
+
};
|
|
1523
|
+
return Object.freeze({
|
|
1524
|
+
protocol: HISTORY_PROTOCOL,
|
|
1525
|
+
sdkVersion: SDK_VERSION,
|
|
1526
|
+
start(requestJson) {
|
|
1527
|
+
shims?.detach();
|
|
1528
|
+
execution = void 0;
|
|
1529
|
+
early = void 0;
|
|
1530
|
+
let parsed;
|
|
1531
|
+
try {
|
|
1532
|
+
parsed = JSON.parse(requestJson);
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
early = failedResult(
|
|
1535
|
+
"INVALID_REQUEST",
|
|
1536
|
+
`The request is not JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
1537
|
+
);
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
if (isObject(parsed) && parsed["protocol"] !== HISTORY_PROTOCOL) {
|
|
1541
|
+
early = failedResult(
|
|
1542
|
+
"PROTOCOL_MISMATCH",
|
|
1543
|
+
`This bundle speaks runtime protocol ${HISTORY_PROTOCOL}; the request uses ${String(parsed["protocol"])}`
|
|
1544
|
+
);
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
const request = checkRequest(parsed);
|
|
1548
|
+
if (typeof request === "string") {
|
|
1549
|
+
early = failedResult("INVALID_REQUEST", `Invalid workflow task request: ${request}`);
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
if (registry.ambiguous.has(request.workflowFn)) {
|
|
1553
|
+
early = failedResult(
|
|
1554
|
+
"INVALID_REQUEST",
|
|
1555
|
+
`More than one module exports a workflow named "${request.workflowFn}"`
|
|
1556
|
+
);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const definition = registry.byName.get(request.workflowFn);
|
|
1560
|
+
if (!definition) {
|
|
1561
|
+
early = failedResult(
|
|
1562
|
+
"INVALID_REQUEST",
|
|
1563
|
+
`The bundle exports no workflow named "${request.workflowFn}"`
|
|
1564
|
+
);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
try {
|
|
1568
|
+
const created = new WorkflowExecution({
|
|
1569
|
+
definition,
|
|
1570
|
+
exportNames: registry.exportNames,
|
|
1571
|
+
request,
|
|
1572
|
+
sdkVersion: SDK_VERSION
|
|
1573
|
+
});
|
|
1574
|
+
execution = created;
|
|
1575
|
+
shims?.attach({ now: () => created.now(), random: () => created.nextRandom() });
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
early = failedResult("INTERNAL", error instanceof Error ? error.message : String(error));
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
step() {
|
|
1581
|
+
if (!execution) return "done";
|
|
1582
|
+
try {
|
|
1583
|
+
return execution.step();
|
|
1584
|
+
} catch (error) {
|
|
1585
|
+
internalFailure(error);
|
|
1586
|
+
return "done";
|
|
1587
|
+
}
|
|
1588
|
+
},
|
|
1589
|
+
complete() {
|
|
1590
|
+
try {
|
|
1591
|
+
if (early || !execution)
|
|
1592
|
+
return JSON.stringify(early ?? failedResult("INVALID_REQUEST", "start() was not called"));
|
|
1593
|
+
try {
|
|
1594
|
+
return JSON.stringify(execution.complete());
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
internalFailure(error);
|
|
1597
|
+
return JSON.stringify(execution.complete());
|
|
1598
|
+
}
|
|
1599
|
+
} finally {
|
|
1600
|
+
shims?.detach();
|
|
1601
|
+
execution = void 0;
|
|
1602
|
+
early = void 0;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
function installWorkflowRuntime(modules) {
|
|
1608
|
+
const holder = globalThis;
|
|
1609
|
+
if (holder[RUNTIME_GLOBAL] !== void 0) return;
|
|
1610
|
+
const runtime = createWorkflowRuntime(buildWorkflowRegistry(modules), takeShimControl());
|
|
1611
|
+
Object.defineProperty(globalThis, RUNTIME_GLOBAL, {
|
|
1612
|
+
value: runtime,
|
|
1613
|
+
writable: false,
|
|
1614
|
+
enumerable: false,
|
|
1615
|
+
configurable: false
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
function isRuntimeAbi(value) {
|
|
1619
|
+
return isObject(value) && typeof value["start"] === "function" && typeof value["step"] === "function" && typeof value["complete"] === "function";
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
export { CommandIds, RUNTIME_GLOBAL, SDK_NAME, WorkflowExecution, buildWorkflowRegistry, createWorkflowRuntime, installWorkflowRuntime, isPatchMarkerId, isRuntimeAbi, matchActivation, mismatch, patchMarkerId };
|