@smithers-orchestrator/engine 0.20.1 → 0.20.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithers-orchestrator/engine",
3
- "version": "0.20.1",
3
+ "version": "0.20.4",
4
4
  "description": "Concrete Smithers workflow execution engine",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -33,20 +33,19 @@
33
33
  "react": "^19.2.5",
34
34
  "react-dom": "^19.2.5",
35
35
  "zod": "^4.3.6",
36
- "@smithers-orchestrator/components": "0.20.1",
37
- "@smithers-orchestrator/db": "0.20.1",
38
- "@smithers-orchestrator/driver": "0.20.1",
39
- "@smithers-orchestrator/errors": "0.20.1",
40
- "@smithers-orchestrator/agents": "0.20.1",
41
- "@smithers-orchestrator/react-reconciler": "0.20.1",
42
- "@smithers-orchestrator/memory": "0.20.1",
43
- "@smithers-orchestrator/observability": "0.20.1",
44
- "@smithers-orchestrator/graph": "0.20.1",
45
- "@smithers-orchestrator/sandbox": "0.20.1",
46
- "@smithers-orchestrator/scheduler": "0.20.1",
47
- "@smithers-orchestrator/scorers": "0.20.1",
48
- "@smithers-orchestrator/time-travel": "0.20.1",
49
- "@smithers-orchestrator/vcs": "0.20.1"
36
+ "@smithers-orchestrator/components": "0.20.4",
37
+ "@smithers-orchestrator/agents": "0.20.4",
38
+ "@smithers-orchestrator/driver": "0.20.4",
39
+ "@smithers-orchestrator/errors": "0.20.4",
40
+ "@smithers-orchestrator/graph": "0.20.4",
41
+ "@smithers-orchestrator/observability": "0.20.4",
42
+ "@smithers-orchestrator/sandbox": "0.20.4",
43
+ "@smithers-orchestrator/react-reconciler": "0.20.4",
44
+ "@smithers-orchestrator/scheduler": "0.20.4",
45
+ "@smithers-orchestrator/db": "0.20.4",
46
+ "@smithers-orchestrator/scorers": "0.20.4",
47
+ "@smithers-orchestrator/time-travel": "0.20.4",
48
+ "@smithers-orchestrator/vcs": "0.20.4"
50
49
  },
51
50
  "devDependencies": {
52
51
  "@types/bun": "latest",
package/src/approvals.js CHANGED
@@ -217,3 +217,9 @@ export function denyNode(adapter, runId, nodeId, iteration, note, decidedBy, dec
217
217
  approvalDecidedBy: decidedBy ?? null,
218
218
  }), Effect.withLogSpan("approval:deny"));
219
219
  }
220
+ export const __approvalInternals = {
221
+ isAsyncApprovalRequest,
222
+ nextRunStatusForApproval,
223
+ serializeDecision,
224
+ validateNodeWaitingForApproval,
225
+ };
@@ -161,3 +161,10 @@ export async function executeChildWorkflow(parentWorkflow, options) {
161
161
  output: normalizeChildOutput(result),
162
162
  };
163
163
  }
164
+ export const __childWorkflowInternals = {
165
+ buildChildWorkflowRunId,
166
+ normalizeChildInput,
167
+ normalizeChildOutput,
168
+ resolveChildWorkflow,
169
+ stripSystemColumns,
170
+ };
@@ -289,21 +289,16 @@ function deriveRetryCount(retry) {
289
289
  return Math.max(0, Math.floor(maxAttempts - 1));
290
290
  }
291
291
  }
292
- try {
293
- const driver = Effect.runSync(Schedule.driver(retry));
294
- let count = 0;
295
- while (count < 100) {
296
- const exit = Effect.runSyncExit(driver.next(undefined));
297
- if (Exit.isFailure(exit)) {
298
- return count;
299
- }
300
- count += 1;
292
+ const driver = Effect.runSync(Schedule.driver(retry));
293
+ let count = 0;
294
+ while (count < 100) {
295
+ const exit = Effect.runSyncExit(driver.next(undefined));
296
+ if (Exit.isFailure(exit)) {
297
+ return count;
301
298
  }
302
- return count;
303
- }
304
- catch {
305
- return 0;
299
+ count += 1;
306
300
  }
301
+ return count;
307
302
  }
308
303
  /**
309
304
  * @template T
@@ -1101,3 +1096,43 @@ export const Smithers = {
1101
1096
  workflow,
1102
1097
  fragment,
1103
1098
  };
1099
+
1100
+ export const __builderInternals = {
1101
+ ApprovalDecision,
1102
+ SmithersSqlite,
1103
+ annotateLoops,
1104
+ applyPrefixId,
1105
+ assertUniqueHandleIds,
1106
+ buildNeedsContext,
1107
+ buildUserContext,
1108
+ collectHandles,
1109
+ compileGraph,
1110
+ compileNeeds,
1111
+ createBuilder,
1112
+ createBuilderDb,
1113
+ createInputTable,
1114
+ createPayloadTable,
1115
+ decodeSchema,
1116
+ deriveRetryCount,
1117
+ deriveRetryPolicy,
1118
+ durationToMs,
1119
+ encodeSchema,
1120
+ evaluateSkip,
1121
+ executeStepHandle,
1122
+ extractResult,
1123
+ isBuilderNode,
1124
+ isWorkflowGraph,
1125
+ makeFactory,
1126
+ makeGraph,
1127
+ makeTableName,
1128
+ normalizeExecutionError,
1129
+ readHandle,
1130
+ readHandleMaybe,
1131
+ readLatestHandleResult,
1132
+ renderNode,
1133
+ resolveEffectResult,
1134
+ resolveHandleIteration,
1135
+ sanitizeIdentifier,
1136
+ sqlite,
1137
+ stripPersistedKeys,
1138
+ };
@@ -38,13 +38,13 @@ function isAbortError(err) {
38
38
  }
39
39
  if (fromTaggedError(err)?.code === "TASK_ABORTED")
40
40
  return true;
41
- if (err.name === "AbortError")
42
- return true;
43
41
  if (typeof DOMException !== "undefined" &&
44
42
  err instanceof DOMException &&
45
43
  err.name === "AbortError") {
46
44
  return true;
47
45
  }
46
+ if (err.name === "AbortError")
47
+ return true;
48
48
  if (err instanceof Error) {
49
49
  return /aborted|abort/i.test(err.message);
50
50
  }
@@ -90,9 +90,6 @@ function validateHeartbeatValue(value, path, seen) {
90
90
  typeof value === "symbol") {
91
91
  throw new SmithersError("HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE", `Heartbeat payload contains a non-JSON value (invalid at ${path}).`, { path, valueType: typeof value });
92
92
  }
93
- if (typeof value !== "object") {
94
- throw new SmithersError("HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE", `Heartbeat payload contains an unsupported value at ${path}.`, { path });
95
- }
96
93
  if (seen.has(value)) {
97
94
  throw new SmithersError("HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE", "Heartbeat payload cannot contain circular references.", { path });
98
95
  }
@@ -171,6 +168,25 @@ function isHeartbeatPayloadValidationError(err) {
171
168
  return (code === "HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE" ||
172
169
  code === "HEARTBEAT_PAYLOAD_TOO_LARGE");
173
170
  }
171
+ /**
172
+ * @param {AbortController} controller
173
+ * @param {AbortSignal} signal
174
+ * @returns {() => void}
175
+ */
176
+ function linkEffectAbortSignal(controller, signal) {
177
+ const forwardAbort = () => {
178
+ controller.abort(signal.reason ?? makeAbortError());
179
+ };
180
+ if (signal.aborted) {
181
+ forwardAbort();
182
+ }
183
+ else {
184
+ signal.addEventListener("abort", forwardAbort, { once: true });
185
+ }
186
+ return () => {
187
+ signal.removeEventListener("abort", forwardAbort);
188
+ };
189
+ }
174
190
  /**
175
191
  * @param {_TaskDescriptor} desc
176
192
  * @param {boolean} cacheEnabled
@@ -288,46 +304,26 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
288
304
  finally {
289
305
  heartbeatWriteInFlight = false;
290
306
  if (heartbeatHasPendingWrite && !heartbeatClosed) {
291
- if (heartbeatWriteTimer) {
292
- clearTimeout(heartbeatWriteTimer);
293
- heartbeatWriteTimer = undefined;
294
- }
295
307
  void flushHeartbeat();
296
308
  }
297
309
  }
298
310
  };
299
311
  /**
300
312
  * @param {unknown} data
301
- * @param {{ internal?: boolean }} [opts]
302
313
  */
303
- const queueHeartbeat = (data, opts) => {
314
+ const queueHeartbeat = (data) => {
304
315
  if (taskCompleted ||
305
316
  heartbeatClosed ||
306
- (!opts?.internal && taskExecutionReturned)) {
317
+ taskExecutionReturned) {
307
318
  return;
308
319
  }
309
320
  const heartbeatAtMs = nowMs();
310
321
  let nextHeartbeatDataJson = null;
311
322
  let dataSizeBytes = 0;
312
- try {
313
- if (data !== undefined) {
314
- const serialized = serializeHeartbeatPayload(data);
315
- nextHeartbeatDataJson = serialized.heartbeatDataJson;
316
- dataSizeBytes = serialized.dataSizeBytes;
317
- }
318
- }
319
- catch (error) {
320
- if (!opts?.internal) {
321
- throw error;
322
- }
323
- logWarning("internal heartbeat payload rejected", {
324
- runId,
325
- nodeId: desc.nodeId,
326
- iteration: desc.iteration,
327
- attempt: attemptNo,
328
- error: error instanceof Error ? error.message : String(error),
329
- }, "heartbeat:record");
330
- return;
323
+ if (data !== undefined) {
324
+ const serialized = serializeHeartbeatPayload(data);
325
+ nextHeartbeatDataJson = serialized.heartbeatDataJson;
326
+ dataSizeBytes = serialized.dataSizeBytes;
331
327
  }
332
328
  heartbeatPendingAtMs = heartbeatAtMs;
333
329
  heartbeatPendingDataJson = nextHeartbeatDataJson;
@@ -358,10 +354,6 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
358
354
  if (Exit.isSuccess(exit)) {
359
355
  return exit.value;
360
356
  }
361
- const failure = Cause.failureOption(exit.cause);
362
- if (failure._tag === "Some") {
363
- throw failure.value;
364
- }
365
357
  throw Cause.squash(exit.cause);
366
358
  };
367
359
  const heartbeatTimeoutMs = desc.heartbeatTimeoutMs;
@@ -484,17 +476,7 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
484
476
  try: (effectSignal) => {
485
477
  const computeAbortController = new AbortController();
486
478
  const removeTaskAbortForwarder = wireAbortSignal(computeAbortController, taskSignal);
487
- const forwardEffectAbort = () => {
488
- computeAbortController.abort(effectSignal.reason ?? makeAbortError());
489
- };
490
- if (effectSignal.aborted) {
491
- forwardEffectAbort();
492
- }
493
- else {
494
- effectSignal.addEventListener("abort", forwardEffectAbort, {
495
- once: true,
496
- });
497
- }
479
+ const removeEffectAbortForwarder = linkEffectAbortSignal(computeAbortController, effectSignal);
498
480
  return Promise.resolve()
499
481
  .then(() => withTaskRuntime({
500
482
  runId,
@@ -510,7 +492,7 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
510
492
  }, () => desc.computeFn()))
511
493
  .finally(() => {
512
494
  removeTaskAbortForwarder();
513
- effectSignal.removeEventListener("abort", forwardEffectAbort);
495
+ removeEffectAbortForwarder();
514
496
  });
515
497
  },
516
498
  catch: (error) => error,
@@ -738,3 +720,16 @@ export const executeComputeTaskBridge = async (adapter, db, runId, desc, eventBu
738
720
  removeAbortForwarder();
739
721
  }
740
722
  };
723
+
724
+ export const __computeTaskBridgeInternals = {
725
+ TASK_HEARTBEAT_MAX_PAYLOAD_BYTES,
726
+ TASK_HEARTBEAT_THROTTLE_MS,
727
+ TASK_HEARTBEAT_TIMEOUT_CHECK_MS,
728
+ heartbeatTimeoutReasonFromAbort,
729
+ isAbortError,
730
+ isHeartbeatPayloadValidationError,
731
+ linkEffectAbortSignal,
732
+ parseAttemptHeartbeatData,
733
+ serializeHeartbeatPayload,
734
+ validateHeartbeatValue,
735
+ };
@@ -1340,3 +1340,40 @@ export async function cancelPendingTimersBridge(adapter, runId, eventBus, reason
1340
1340
  }));
1341
1341
  }
1342
1342
  }
1343
+
1344
+ export const __deferredStateBridgeInternals = {
1345
+ buildApprovalRequestJson,
1346
+ buildHumanRequestSchemaJson,
1347
+ buildTimerAttemptMeta,
1348
+ buildTimerSnapshot,
1349
+ buildWaitForEventAttemptMeta,
1350
+ buildWaitForEventSnapshot,
1351
+ cancelPendingTimersBridge,
1352
+ defaultAutoApprovalDecision,
1353
+ ensurePendingHumanRequest,
1354
+ failWaitForEventTaskBridge,
1355
+ finishWaitForEventTaskBridge,
1356
+ getHumanTaskPrompt,
1357
+ parseAttemptErrorCode,
1358
+ parseOptionalFiniteNumber,
1359
+ parseTimerDurationMs,
1360
+ parseTimerSnapshot,
1361
+ parseTimerType,
1362
+ parseTimerUntilMs,
1363
+ parseWaitForEventCorrelationId,
1364
+ parseWaitForEventOnTimeout,
1365
+ parseWaitForEventSignalName,
1366
+ parseWaitForEventSnapshot,
1367
+ reconcileHumanRequestValidationFailure,
1368
+ renderHumanPromptToText,
1369
+ resolveApprovalTaskStateBridge,
1370
+ resolveTimerTaskStateBridge,
1371
+ resolveWaitForEventTaskStateBridge,
1372
+ resolveWaitForEventTimeoutBridge,
1373
+ shouldAutoApprove,
1374
+ shouldClearAsyncWaitMetric,
1375
+ syncApprovalDurableDeferredFromDb,
1376
+ syncWaitForEventDurableDeferredFromDb,
1377
+ updateAsyncExternalWaitPendingSafe,
1378
+ validateDeferredOutputPayload,
1379
+ };
@@ -171,6 +171,24 @@ async function computeUntrackedDiffs(currentDir) {
171
171
  }
172
172
  return diffs;
173
173
  }
174
+ /**
175
+ * @param {string} currentDir
176
+ * @param {string} targetRef
177
+ * @param {string} path
178
+ * @returns {Promise<string | undefined>}
179
+ */
180
+ async function readBinaryContentAtRef(currentDir, targetRef, path) {
181
+ try {
182
+ const { stdout } = await runGit(currentDir, [
183
+ "show",
184
+ `${targetRef}:${path}`,
185
+ ]);
186
+ return Buffer.from(stdout, "binary").toString("base64");
187
+ }
188
+ catch {
189
+ return undefined;
190
+ }
191
+ }
174
192
  /**
175
193
  * Compute a diff bundle strictly between two immutable refs.
176
194
  *
@@ -225,18 +243,7 @@ export async function computeDiffBundleBetweenRefs(baseRef, targetRef, currentDi
225
243
  const binary = isBinaryPatch(chunk) || binaryPaths.has(path);
226
244
  let binaryContent;
227
245
  if (binary && operation !== "delete") {
228
- try {
229
- const { stdout } = await runGit(currentDir, [
230
- "show",
231
- `${targetRef}:${path}`,
232
- ]);
233
- binaryContent = Buffer.from(stdout, "binary").toString("base64");
234
- }
235
- catch {
236
- // File may not be readable as a blob at targetRef; fall through
237
- // without binaryContent. Caller receives operation + diff only.
238
- binaryContent = undefined;
239
- }
246
+ binaryContent = await readBinaryContentAtRef(currentDir, targetRef, path);
240
247
  }
241
248
  patches.push({
242
249
  path,
@@ -350,3 +357,8 @@ export async function applyDiffBundle(bundle, targetDir) {
350
357
  }
351
358
  }
352
359
  }
360
+ export const __diffBundleInternals = {
361
+ extractPatchPath,
362
+ readBinaryContentAtRef,
363
+ splitGitDiff,
364
+ };
@@ -135,6 +135,12 @@ function buildResolvedWaitForEventMetaJson(snapshot, signal) {
135
135
  },
136
136
  });
137
137
  }
138
+ async function decrementAsyncEventWaitPending(updatePending = updateAsyncExternalWaitPending) {
139
+ try {
140
+ await Effect.runPromise(updatePending("event", -1));
141
+ }
142
+ catch { }
143
+ }
138
144
  /**
139
145
  * @param {_SmithersDb} adapter
140
146
  * @param {string} runId
@@ -154,10 +160,7 @@ async function markWaitForEventResolved(adapter, runId, nodeId, iteration, signa
154
160
  metaJson: buildResolvedWaitForEventMetaJson(snapshot, signal),
155
161
  }));
156
162
  if (snapshot.waitAsync) {
157
- try {
158
- await Effect.runPromise(updateAsyncExternalWaitPending("event", -1));
159
- }
160
- catch { }
163
+ await decrementAsyncEventWaitPending();
161
164
  }
162
165
  }
163
166
  const deferredResolutions = new Map();
@@ -280,3 +283,12 @@ export const bridgeSignalResolve = async (adapter, runId, signal) => {
280
283
  await bridgeWaitForEventResolve(adapter, runId, node.nodeId, iteration, signal);
281
284
  }
282
285
  };
286
+ export const __durableDeferredBridgeInternals = {
287
+ buildResolvedWaitForEventMetaJson,
288
+ decrementAsyncEventWaitPending,
289
+ getAdapterNamespace,
290
+ markWaitForEventResolved,
291
+ normalizeCorrelationId,
292
+ parseOptionalFiniteNumber,
293
+ parseWaitForEventAttemptSnapshot,
294
+ };
@@ -1,86 +1,2 @@
1
- import { HttpRunner } from "@effect/cluster";
2
- import { mkdir, cp, rm } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { Effect, Layer } from "effect";
5
- import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
6
- import { spawnCaptureEffect } from "@smithers-orchestrator/driver/child-process";
7
- import { toSmithersError } from "@smithers-orchestrator/errors/toSmithersError";
8
- import { SandboxEntityExecutor } from "@smithers-orchestrator/sandbox/effect/sandbox-entity";
9
- /**
10
- * @param {SandboxTransportConfig} config
11
- * @returns {SandboxHandle}
12
- */
13
- function baseHandle(config) {
14
- const sandboxRoot = join(config.rootDir, ".smithers", "sandboxes", config.runId, config.sandboxId);
15
- return {
16
- runtime: config.runtime,
17
- runId: config.runId,
18
- sandboxId: config.sandboxId,
19
- sandboxRoot,
20
- requestPath: join(sandboxRoot, "request"),
21
- resultPath: join(sandboxRoot, "result"),
22
- };
23
- }
24
- export const DockerSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, SandboxEntityExecutor.of({
25
- create: (config) => Effect.gen(function* () {
26
- const handle = baseHandle(config);
27
- yield* spawnCaptureEffect("docker", ["info"], {
28
- cwd: config.rootDir,
29
- env: process.env,
30
- timeoutMs: 10_000,
31
- maxOutputBytes: 200_000,
32
- }).pipe(Effect.catchAll(() => Effect.fail(new SmithersError("PROCESS_SPAWN_FAILED", "Docker daemon not reachable.", { runtime: "docker" }))));
33
- yield* Effect.tryPromise({
34
- try: async () => {
35
- await mkdir(handle.requestPath, { recursive: true });
36
- await mkdir(handle.resultPath, { recursive: true });
37
- },
38
- catch: (cause) => toSmithersError(cause, "create docker sandbox workspace"),
39
- });
40
- return handle;
41
- }),
42
- ship: (bundlePath, handle) => Effect.tryPromise({
43
- try: async () => {
44
- await rm(handle.requestPath, { recursive: true, force: true });
45
- await mkdir(handle.requestPath, { recursive: true });
46
- await cp(bundlePath, handle.requestPath, { recursive: true });
47
- },
48
- catch: (cause) => toSmithersError(cause, "ship docker bundle"),
49
- }),
50
- execute: (_command, _handle) => Effect.succeed({ exitCode: 0 }),
51
- collect: (handle) => Effect.succeed({ bundlePath: handle.resultPath }),
52
- cleanup: (_handle) => Effect.void,
53
- }));
54
- export const CodeplaneSandboxExecutorLive = Layer.succeed(SandboxEntityExecutor, SandboxEntityExecutor.of({
55
- create: (config) => Effect.gen(function* () {
56
- const apiUrl = process.env.CODEPLANE_API_URL;
57
- const apiKey = process.env.CODEPLANE_API_KEY;
58
- if (!apiUrl || !apiKey) {
59
- yield* Effect.fail(new SmithersError("INVALID_INPUT", "Codeplane runtime requires CODEPLANE_API_URL and CODEPLANE_API_KEY."));
60
- }
61
- const handle = baseHandle(config);
62
- yield* Effect.tryPromise({
63
- try: async () => {
64
- await mkdir(handle.requestPath, { recursive: true });
65
- await mkdir(handle.resultPath, { recursive: true });
66
- },
67
- catch: (cause) => toSmithersError(cause, "create codeplane sandbox workspace"),
68
- });
69
- return {
70
- ...handle,
71
- workspaceId: `${config.runId}:${config.sandboxId}`,
72
- };
73
- }),
74
- ship: (bundlePath, handle) => Effect.tryPromise({
75
- try: async () => {
76
- await rm(handle.requestPath, { recursive: true, force: true });
77
- await mkdir(handle.requestPath, { recursive: true });
78
- await cp(bundlePath, handle.requestPath, { recursive: true });
79
- },
80
- catch: (cause) => toSmithersError(cause, "ship codeplane bundle"),
81
- }),
82
- execute: (_command, _handle) => Effect.succeed({ exitCode: 0 }),
83
- collect: (handle) => Effect.succeed({ bundlePath: handle.resultPath }),
84
- cleanup: (_handle) => Effect.void,
85
- }));
86
- export const SandboxHttpRunner = HttpRunner;
1
+ // Preserve the engine subpath while using the canonical sandbox implementation.
2
+ export * from "@smithers-orchestrator/sandbox/effect/http-runner";
@@ -142,8 +142,15 @@ async function buildSingleRunnerRuntime() {
142
142
  * @returns {Promise<SingleRunnerRuntime>}
143
143
  */
144
144
  async function getSingleRunnerRuntime() {
145
+ return getSingleRunnerRuntimeFromBuilder(buildSingleRunnerRuntime);
146
+ }
147
+ /**
148
+ * @param {() => Promise<SingleRunnerRuntime>} buildRuntime
149
+ * @returns {Promise<SingleRunnerRuntime>}
150
+ */
151
+ async function getSingleRunnerRuntimeFromBuilder(buildRuntime) {
145
152
  if (!singleRunnerRuntimePromise) {
146
- singleRunnerRuntimePromise = buildSingleRunnerRuntime().catch((error) => {
153
+ singleRunnerRuntimePromise = buildRuntime().catch((error) => {
147
154
  singleRunnerRuntimePromise = undefined;
148
155
  throw error;
149
156
  });
@@ -187,3 +194,21 @@ export function subscribeTaskWorkerDispatches(subscriber) {
187
194
  dispatchSubscribers.delete(subscriber);
188
195
  };
189
196
  }
197
+
198
+ export const __singleRunnerInternals = {
199
+ buildMissingExecutionResult,
200
+ buildSingleRunnerRuntime,
201
+ consumeWorkerError,
202
+ dispatchSubscribers,
203
+ getSingleRunnerRuntimeFromBuilder,
204
+ getSingleRunnerRuntime,
205
+ setSingleRunnerRuntimePromiseForTest: (promise) => {
206
+ singleRunnerRuntimePromise = promise;
207
+ },
208
+ notifyDispatchSubscribers,
209
+ runRegisteredExecution,
210
+ storeWorkerError,
211
+ toWorkerTaskError,
212
+ workerErrors,
213
+ workerExecutions,
214
+ };