@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.
Files changed (168) hide show
  1. package/README.md +170 -18
  2. package/dist/activation-BBh1p82z.d.cts +57 -0
  3. package/dist/activation-DnhG7SmF.d.ts +57 -0
  4. package/dist/assemble-BLqQ8lSu.d.cts +39 -0
  5. package/dist/assemble-DWgaXHd-.d.ts +39 -0
  6. package/dist/chunk-2DP2Y6EU.cjs +832 -0
  7. package/dist/chunk-2J6TF7YI.cjs +86 -0
  8. package/dist/chunk-3FOTFQ3J.js +1622 -0
  9. package/dist/chunk-4LYSEANA.js +199 -0
  10. package/dist/{chunk-KUTMY2IO.cjs → chunk-57MSZ24T.cjs} +162 -98
  11. package/dist/{chunk-BWSLWF2E.js → chunk-5HDSBTBX.js} +99 -32
  12. package/dist/chunk-623FVQSN.cjs +251 -0
  13. package/dist/{chunk-4DPEGDPW.js → chunk-6FSHT5IL.js} +4 -4
  14. package/dist/{chunk-REV7ZOA4.js → chunk-7AAZGVKD.js} +1 -35
  15. package/dist/chunk-7NQ37GZU.js +73 -0
  16. package/dist/chunk-ABFZWYHT.js +612 -0
  17. package/dist/{chunk-SS4LT2IP.js → chunk-BD7ZPG53.js} +5 -10
  18. package/dist/chunk-BO6OVILK.js +79 -0
  19. package/dist/chunk-C5OLWZ42.js +245 -0
  20. package/dist/chunk-CBL5JSCC.cjs +75 -0
  21. package/dist/{chunk-LSKSBVL4.cjs → chunk-CPDK3K5N.cjs} +6 -6
  22. package/dist/chunk-CSNWNFKU.cjs +1635 -0
  23. package/dist/chunk-DJYMDLV5.js +227 -0
  24. package/dist/{chunk-X4HKU3MX.js → chunk-E2VK7J2C.js} +3 -1
  25. package/dist/chunk-FMSW2HDF.cjs +267 -0
  26. package/dist/chunk-FQIHX7YI.cjs +80 -0
  27. package/dist/chunk-FR7KYWRO.cjs +41 -0
  28. package/dist/{chunk-AU3YW7FZ.cjs → chunk-GBQ33SIZ.cjs} +46 -51
  29. package/dist/{chunk-RRQ5HOBF.cjs → chunk-GVTZMA5C.cjs} +4 -4
  30. package/dist/chunk-H7KEVW2M.js +816 -0
  31. package/dist/chunk-HR3BN4C4.cjs +27 -0
  32. package/dist/chunk-IISBRUWJ.js +13 -0
  33. package/dist/chunk-JD2VQIDZ.cjs +15 -0
  34. package/dist/{chunk-PL73GJR4.js → chunk-JZ4TOOLN.js} +1 -1
  35. package/dist/chunk-L24YIZY7.cjs +207 -0
  36. package/dist/chunk-LNUQE6D6.js +39 -0
  37. package/dist/{chunk-BYJ7QTLY.cjs → chunk-NEIUQASE.cjs} +13 -13
  38. package/dist/chunk-P6TA5EZT.cjs +634 -0
  39. package/dist/chunk-PDHLTDZB.js +72 -0
  40. package/dist/chunk-PMCM2OH4.js +46 -0
  41. package/dist/chunk-PZCVF76Q.cjs +868 -0
  42. package/dist/{chunk-GO34UHSY.js → chunk-QAN5MZEM.js} +3 -3
  43. package/dist/{chunk-27HASETB.cjs → chunk-QKZL3ONM.cjs} +3 -1
  44. package/dist/chunk-SAENRNBJ.cjs +52 -0
  45. package/dist/{chunk-LMX35GNQ.js → chunk-SMCANK3F.js} +5 -4
  46. package/dist/{chunk-IZQNUNSU.cjs → chunk-SX2PNKKT.cjs} +18 -17
  47. package/dist/{chunk-LRM6JXJA.cjs → chunk-TBDXTV22.cjs} +0 -37
  48. package/dist/chunk-VOKMAAJP.js +848 -0
  49. package/dist/chunk-XB53G2DK.js +25 -0
  50. package/dist/chunk-Z2Y65YOY.cjs +7 -0
  51. package/dist/codegen/index.cjs +9 -7
  52. package/dist/codegen/index.d.cts +2 -2
  53. package/dist/codegen/index.d.ts +2 -2
  54. package/dist/codegen/index.js +6 -5
  55. package/dist/file-BOoHZgoQ.d.cts +1557 -0
  56. package/dist/file-yuPCFU5b.d.ts +1557 -0
  57. package/dist/history/index.cjs +287 -0
  58. package/dist/history/index.d.cts +157 -0
  59. package/dist/history/index.d.ts +157 -0
  60. package/dist/history/index.js +5 -0
  61. package/dist/index.cjs +134 -36
  62. package/dist/index.d.cts +137 -9
  63. package/dist/index.d.ts +137 -9
  64. package/dist/index.js +8 -3
  65. package/dist/ir/index.cjs +161 -165
  66. package/dist/ir/index.d.cts +32 -12
  67. package/dist/ir/index.d.ts +32 -12
  68. package/dist/ir/index.js +54 -61
  69. package/dist/lower-BuSvAFqi.d.cts +129 -0
  70. package/dist/lower-C_Qv_34S.d.ts +129 -0
  71. package/dist/lower-types-nofRXw9c.d.cts +15 -0
  72. package/dist/lower-types-nofRXw9c.d.ts +15 -0
  73. package/dist/{manifest-Duf-gHdO.d.cts → manifest-0OLw0i7k.d.cts} +1 -1
  74. package/dist/{manifest-Bp9ZJlav.d.ts → manifest-C8N6HDYl.d.ts} +1 -1
  75. package/dist/node-evaluator-CJNP4GHX.js +12 -0
  76. package/dist/node-evaluator-DMKPKUWA.cjs +22 -0
  77. package/dist/quickjs/index.cjs +256 -78
  78. package/dist/quickjs/index.d.cts +48 -7
  79. package/dist/quickjs/index.d.ts +48 -7
  80. package/dist/quickjs/index.js +254 -79
  81. package/dist/roundtrip/index.cjs +404 -129
  82. package/dist/roundtrip/index.d.cts +43 -8
  83. package/dist/roundtrip/index.d.ts +43 -8
  84. package/dist/roundtrip/index.js +260 -19
  85. package/dist/runtime/index.cjs +90 -0
  86. package/dist/runtime/index.d.cts +297 -0
  87. package/dist/runtime/index.d.ts +297 -0
  88. package/dist/runtime/index.js +8 -0
  89. package/dist/testing/index.cjs +1057 -0
  90. package/dist/testing/index.d.cts +305 -0
  91. package/dist/testing/index.d.ts +305 -0
  92. package/dist/testing/index.js +1037 -0
  93. package/dist/{types-Dr2Fn1Ax.d.ts → types-BGoPzgcl.d.cts} +1 -1
  94. package/dist/{types-Dr2Fn1Ax.d.cts → types-BGoPzgcl.d.ts} +1 -1
  95. package/dist/types-CpBBjaGX.d.cts +380 -0
  96. package/dist/types-DV-HX_Gt.d.ts +380 -0
  97. package/dist/{assemble-DMOuaxRI.d.ts → workflow-Bpr3vnA_.d.ts} +60 -162
  98. package/dist/workflow-C2a05WUJ.d.ts +444 -0
  99. package/dist/{assemble-BJo9TECS.d.cts → workflow-FXUkpIBc.d.cts} +60 -162
  100. package/dist/workflow-J-yYixny.d.cts +444 -0
  101. package/fixtures/README.md +128 -0
  102. package/fixtures/histories/activity-failure-fails-run.history.json +163 -0
  103. package/fixtures/histories/activity-retry-then-success.history.json +147 -0
  104. package/fixtures/histories/cancel-activity-abandon.history.json +126 -0
  105. package/fixtures/histories/cancel-activity-compensate.history.json +307 -0
  106. package/fixtures/histories/cancel-concurrent-non-cancellable.history.json +234 -0
  107. package/fixtures/histories/cancel-concurrent-scope-timeout.history.json +184 -0
  108. package/fixtures/histories/cancel-condition-after-cancel.history.json +200 -0
  109. package/fixtures/histories/cancel-during-timer.history.json +123 -0
  110. package/fixtures/histories/cancel-saga-compensations.history.json +325 -0
  111. package/fixtures/histories/cancel-scope-timeout.history.json +148 -0
  112. package/fixtures/histories/child-abandon-outlives-parent.history.json +191 -0
  113. package/fixtures/histories/child-cancel-default.history.json +234 -0
  114. package/fixtures/histories/child-cancel-request.history.json +206 -0
  115. package/fixtures/histories/child-durable-child-side.history.json +124 -0
  116. package/fixtures/histories/child-durable-result.history.json +163 -0
  117. package/fixtures/histories/child-failure-caught.history.json +166 -0
  118. package/fixtures/histories/child-graph-by-key.history.json +138 -0
  119. package/fixtures/histories/child-terminated-by-parent-close.history.json +85 -0
  120. package/fixtures/histories/continue-as-new-first-run.history.json +146 -0
  121. package/fixtures/histories/continue-as-new-history-limit.history.json +489 -0
  122. package/fixtures/histories/continue-as-new-last-run.history.json +147 -0
  123. package/fixtures/histories/continue-as-new-suggested.history.json +352 -0
  124. package/fixtures/histories/details-and-context.history.json +176 -0
  125. package/fixtures/histories/deterministic-values.history.json +128 -0
  126. package/fixtures/histories/engine-builtins.history.json +172 -0
  127. package/fixtures/histories/gather-activities.history.json +291 -0
  128. package/fixtures/histories/gather-all-settled.history.json +219 -0
  129. package/fixtures/histories/gather-mixed.history.json +281 -0
  130. package/fixtures/histories/input-during-running-task.history.json +111 -0
  131. package/fixtures/histories/nondeterminism-reordered.history.json +218 -0
  132. package/fixtures/histories/patched-deprecated.history.json +271 -0
  133. package/fixtures/histories/patched-new-run.history.json +270 -0
  134. package/fixtures/histories/patched-old-history.history.json +190 -0
  135. package/fixtures/histories/race-activity-wins.history.json +152 -0
  136. package/fixtures/histories/race-signal-beats-child.history.json +161 -0
  137. package/fixtures/histories/race-timeout-wins.history.json +118 -0
  138. package/fixtures/histories/side-effect-recorded.history.json +153 -0
  139. package/fixtures/histories/signal-buffered-at-start.history.json +157 -0
  140. package/fixtures/histories/signal-default-handler.history.json +187 -0
  141. package/fixtures/histories/signal-handler.history.json +191 -0
  142. package/fixtures/histories/signal-parent-child-side.history.json +132 -0
  143. package/fixtures/histories/signal-parent.history.json +202 -0
  144. package/fixtures/histories/signal-run-delivered.history.json +128 -0
  145. package/fixtures/histories/signal-run-undelivered.history.json +134 -0
  146. package/fixtures/histories/signal-wait.history.json +111 -0
  147. package/fixtures/histories/task-failure-code-error.history.json +137 -0
  148. package/fixtures/histories/task-failure-then-transient-attempt.history.json +140 -0
  149. package/fixtures/histories/timer-condition-met.history.json +201 -0
  150. package/fixtures/histories/timer-condition-timeout.history.json +118 -0
  151. package/fixtures/histories/timer-sleep-once.history.json +118 -0
  152. package/fixtures/histories/timer-sleep-sequence.history.json +210 -0
  153. package/fixtures/histories/update-accepted-completed.history.json +149 -0
  154. package/fixtures/histories/update-with-start-rejected-validation.history.json +91 -0
  155. package/package.json +85 -20
  156. package/schema/history-file.schema.json +2544 -0
  157. package/schema/ir-v2.schema.json +16 -0
  158. package/schema/run-event.schema.json +2374 -0
  159. package/schema/workflow-command.schema.json +1095 -0
  160. package/schema/workflow-task.schema.json +3560 -0
  161. package/dist/chunk-265J2FCH.cjs +0 -33
  162. package/dist/chunk-LUXCXFOE.cjs +0 -269
  163. package/dist/chunk-S34NJE3A.js +0 -262
  164. package/dist/chunk-SRQRLWOS.js +0 -31
  165. package/dist/lower-DcOY8ux0.d.cts +0 -49
  166. package/dist/lower-Dw5grNrI.d.ts +0 -49
  167. package/dist/node-evaluator-2GTUZVPK.cjs +0 -17
  168. package/dist/node-evaluator-RMYEWCXZ.js +0 -8
@@ -1,90 +1,265 @@
1
- import { LowerError } from '../chunk-S34NJE3A.js';
2
- import '../chunk-BWSLWF2E.js';
3
- import '../chunk-SS4LT2IP.js';
1
+ import { runIsolateWorker, outcomeToCollection, LowerError } from '../chunk-H7KEVW2M.js';
2
+ import '../chunk-PDHLTDZB.js';
3
+ import { failedTaskResult } from '../chunk-IISBRUWJ.js';
4
+ import '../chunk-5HDSBTBX.js';
5
+ import { buildIdOf } from '../chunk-BD7ZPG53.js';
4
6
  import '../chunk-5LUIDMF4.js';
5
- import '../chunk-PL73GJR4.js';
6
- import '../chunk-X4HKU3MX.js';
7
- import '../chunk-REV7ZOA4.js';
8
- import { newQuickJSWASMModuleFromVariant, newVariant, RELEASE_SYNC, shouldInterruptAfterDeadline } from 'quickjs-emscripten';
7
+ import '../chunk-JZ4TOOLN.js';
8
+ import '../chunk-E2VK7J2C.js';
9
+ import '../chunk-7AAZGVKD.js';
10
+ import { isRecord } from '../chunk-PMCM2OH4.js';
11
+ import '../chunk-4LYSEANA.js';
12
+ import '../chunk-7NQ37GZU.js';
13
+ import '../chunk-DJYMDLV5.js';
14
+ import '../chunk-BO6OVILK.js';
15
+ import { createRequire } from 'module';
16
+ import { pathToFileURL } from 'url';
17
+ import { Worker } from 'worker_threads';
9
18
 
10
- var WASM_PAGE_BYTES = 64 * 1024;
11
- var ENGINE_HEADROOM_BYTES = 32 * 1024 * 1024;
12
- var INITIAL_ENGINE_BYTES = 16 * 1024 * 1024;
13
- function errorMessage(ctx, handle) {
14
- const dumped = ctx.dump(handle);
15
- if (typeof dumped === "object" && dumped !== null && "message" in dumped)
16
- return String(dumped.message);
17
- return String(dumped);
19
+ // src/generated/quickjs-worker-source.ts
20
+ var QUICKJS_WORKER_SOURCE = '"use strict";\n\nvar import_node_worker_threads = require("node:worker_threads");\n\nfunction isRecord(value) {\n return typeof value === "object" && value !== null && !Array.isArray(value);\n}\n\nvar DETERMINISM_VIOLATION = "DeterminismViolation";\nvar DETERMINISM_PRELUDE = `(() => {\n const NAME = ${JSON.stringify(DETERMINISM_VIOLATION)};\n const fail = (what) => {\n const error = new Error(\n what + " is not allowed in workflow code: lowering must give the same IR on every build. Read times and random values at run time instead (for example wf.now)."\n );\n error.name = NAME;\n throw error;\n };\n const lock = (target, key, value) =>\n Object.defineProperty(target, key, { value, writable: false, enumerable: false, configurable: false });\n const RealDate = Date;\n const LockedDate = function Date(...args) {\n if (new.target === undefined) return fail("Date()");\n if (args.length === 0) return fail("new Date()");\n return Reflect.construct(RealDate, args, new.target);\n };\n lock(LockedDate, "prototype", RealDate.prototype);\n lock(RealDate.prototype, "constructor", LockedDate);\n lock(LockedDate, "now", function now() { return fail("Date.now()"); });\n lock(LockedDate, "parse", RealDate.parse);\n lock(LockedDate, "UTC", RealDate.UTC);\n lock(globalThis, "Date", LockedDate);\n lock(Math, "random", function random() { return fail("Math.random()"); });\n const crypto = globalThis.crypto;\n if (crypto !== null && typeof crypto === "object") {\n for (const name of ["getRandomValues", "randomUUID"]) {\n if (typeof crypto[name] === "function") lock(crypto, name, function () { return fail("crypto." + name + "()"); });\n }\n }\n const DateTimeFormat = typeof Intl === "object" && Intl !== null ? Intl.DateTimeFormat : undefined;\n if (typeof DateTimeFormat === "function") {\n const proto = DateTimeFormat.prototype;\n const format = Object.getOwnPropertyDescriptor(proto, "format");\n if (format && typeof format.get === "function") {\n const get = format.get;\n Object.defineProperty(proto, "format", {\n get() {\n const bound = get.call(this);\n return (date) => (date === undefined ? fail("Intl.DateTimeFormat format()") : bound(date));\n },\n enumerable: false,\n configurable: false,\n });\n }\n const formatToParts = proto.formatToParts;\n if (typeof formatToParts === "function") {\n lock(proto, "formatToParts", function (date) {\n return date === undefined ? fail("Intl.DateTimeFormat formatToParts()") : formatToParts.call(this, date);\n });\n }\n }\n})();\n`;\nfunction failure(kind, message) {\n return { ok: false, failure: kind, message };\n}\nfunction failureForErrorName(name) {\n return name === DETERMINISM_VIOLATION ? "determinism" : "error";\n}\n\nfunction isQuickJSApi(value) {\n return isRecord(value) && typeof value["newQuickJSWASMModuleFromVariant"] === "function" && typeof value["newVariant"] === "function" && typeof value["shouldInterruptAfterDeadline"] === "function" && value["RELEASE_SYNC"] !== void 0;\n}\nvar WASM_PAGE_BYTES = 64 * 1024;\nvar ENGINE_HEADROOM_BYTES = 32 * 1024 * 1024;\nvar INITIAL_ENGINE_BYTES = 16 * 1024 * 1024;\nfunction describe(ctx, handle) {\n const dumped = ctx.dump(handle);\n if (isRecord(dumped)) {\n const name = typeof dumped["name"] === "string" ? dumped["name"] : "Error";\n const message = dumped["message"];\n return {\n name,\n message: typeof message === "string" ? message : message === void 0 ? JSON.stringify(dumped) : String(message)\n };\n }\n return { name: "Error", message: String(dumped) };\n}\nasync function evaluateInQuickJS(api, code, limits) {\n const maximum = Math.ceil((limits.memoryLimitBytes + ENGINE_HEADROOM_BYTES) / WASM_PAGE_BYTES);\n const initial = Math.min(maximum, INITIAL_ENGINE_BYTES / WASM_PAGE_BYTES);\n const engine = await api.newQuickJSWASMModuleFromVariant(\n api.newVariant(api.RELEASE_SYNC, { wasmMemory: new WebAssembly.Memory({ initial, maximum }) })\n );\n const runtime = engine.newRuntime();\n const deadline = Date.now() + limits.timeoutMs;\n runtime.setMemoryLimit(limits.memoryLimitBytes);\n runtime.setMaxStackSize(limits.maxStackSizeBytes);\n runtime.setInterruptHandler(api.shouldInterruptAfterDeadline(deadline));\n const ctx = runtime.newContext();\n const handles = [];\n const timedOut = () => failure("timeout", `Evaluating the workflow code timed out after ${limits.timeoutMs} ms`);\n const thrown = (handle) => {\n const { name, message } = describe(ctx, handle);\n handle.dispose();\n if (Date.now() > deadline && /interrupted/.test(message)) return timedOut();\n return failure(failureForErrorName(name), message);\n };\n try {\n const prelude = ctx.evalCode(DETERMINISM_PRELUDE, "determinism-prelude.js", { type: "global" });\n if (prelude.error) return thrown(prelude.error);\n prelude.value.dispose();\n const result = ctx.evalCode(code, "workflow-bundle.mjs", { type: "module" });\n if (result.error) return thrown(result.error);\n let namespace = result.value;\n handles.push(namespace);\n const state = ctx.getPromiseState(namespace);\n if (!(state.type === "fulfilled" && state.notAPromise === true)) {\n if (state.type === "fulfilled") state.value.dispose();\n else if (state.type === "rejected") state.error.dispose();\n while (runtime.hasPendingJob()) {\n if (Date.now() > deadline) return timedOut();\n const jobs = runtime.executePendingJobs();\n if (jobs.error) return thrown(jobs.error);\n }\n const settled = ctx.getPromiseState(namespace);\n if (settled.type === "rejected") return thrown(settled.error);\n if (settled.type === "pending")\n return failure("error", "The workflow module never finished evaluating");\n namespace = settled.value;\n handles.push(namespace);\n }\n const exported = ctx.getProp(namespace, "__fluidgrids");\n handles.push(exported);\n if (ctx.typeof(exported) !== "string")\n return failure("no-collection", "The bundle did not export a workflow collection");\n return { ok: true, value: ctx.getString(exported) };\n } catch (error) {\n return failure("error", error instanceof Error ? error.message : String(error));\n } finally {\n for (const handle of handles.reverse()) if (handle.alive) handle.dispose();\n ctx.dispose();\n runtime.dispose();\n }\n}\n\nprocess.on("unhandledRejection", () => void 0);\nasync function main(input) {\n if (!isRecord(input) || typeof input["code"] !== "string" || typeof input["moduleUrl"] !== "string" || !isRecord(input["limits"]))\n return failure("crashed", "The evaluation worker received no bundle");\n const { timeoutMs, memoryLimitBytes, maxStackSizeBytes } = input["limits"];\n if (typeof timeoutMs !== "number" || typeof memoryLimitBytes !== "number" || typeof maxStackSizeBytes !== "number")\n return failure("crashed", "The evaluation worker received no limits");\n const loaded = await import(input["moduleUrl"]);\n const api = isQuickJSApi(loaded) ? loaded : isRecord(loaded) && isQuickJSApi(loaded["default"]) ? loaded["default"] : void 0;\n if (!api) return failure("crashed", "quickjs-emscripten could not be loaded");\n return evaluateInQuickJS(api, input["code"], {\n timeoutMs,\n memoryLimitBytes,\n maxStackSizeBytes\n });\n}\nmain(import_node_worker_threads.workerData).then(\n (outcome) => import_node_worker_threads.parentPort?.postMessage(outcome),\n (error) => import_node_worker_threads.parentPort?.postMessage(\n failure("crashed", error instanceof Error ? error.message : String(error))\n )\n);\n';
21
+
22
+ // src/generated/quickjs-task-worker-source.ts
23
+ var QUICKJS_TASK_WORKER_SOURCE = '"use strict";\n\nvar import_node_worker_threads = require("node:worker_threads");\n\nfunction isRecord(value) {\n return typeof value === "object" && value !== null && !Array.isArray(value);\n}\n\nfunction isQuickJSApi(value) {\n return isRecord(value) && typeof value["newQuickJSWASMModuleFromVariant"] === "function" && typeof value["newVariant"] === "function" && typeof value["shouldInterruptAfterDeadline"] === "function" && value["RELEASE_SYNC"] !== void 0;\n}\nvar WASM_PAGE_BYTES = 64 * 1024;\nvar ENGINE_HEADROOM_BYTES = 32 * 1024 * 1024;\nvar INITIAL_ENGINE_BYTES = 16 * 1024 * 1024;\n\nvar HISTORY_PROTOCOL = 1;\nvar DEFAULT_HISTORY_LIMIT = { events: 1e4, bytes: 16 * 1024 * 1024 };\n\nvar SHIM_VIOLATION = "DeterminismViolation";\nvar SHIM_HOOK = "__fluidgridsShims";\nvar TASK_PRELUDE = `(() => {\n const NAME = ${JSON.stringify(SHIM_VIOLATION)};\n const fail = (what) => {\n const error = new Error(what + " is not allowed in workflow code: use ctx.now(), ctx.random(), ctx.sleep() or an activity.");\n error.name = NAME;\n throw error;\n };\n let hooks = null;\n const now = (what) => (hooks !== null ? hooks.now() : fail(what + " outside a workflow task"));\n const lock = (target, key, value) =>\n Object.defineProperty(target, key, { value, writable: false, enumerable: false, configurable: false });\n const RealDate = Date;\n // ---- Built-ins that differ between hosts and engines --------------------\n // Local time is UTC whatever the host\'s TZ, locale formatting is fixed\n // (en-US shapes, no ICU), Intl is absent and garbage collection cannot be\n // observed, so QuickJS and vm on any host run workflow code the same way.\n const proto = RealDate.prototype;\n const hostOffset = proto.getTimezoneOffset;\n const realParse = RealDate.parse;\n const isInvalid = (date) => Number.isNaN(proto.getTime.call(date));\n for (const unit of ["FullYear", "Month", "Date", "Day", "Hours", "Minutes", "Seconds", "Milliseconds"]) {\n lock(proto, "get" + unit, proto["getUTC" + unit]);\n if (unit !== "Day") lock(proto, "set" + unit, proto["setUTC" + unit]);\n }\n lock(proto, "getTimezoneOffset", function getTimezoneOffset() { return isInvalid(this) ? NaN : 0; });\n if (typeof proto.getYear === "function")\n lock(proto, "getYear", function getYear() { return isInvalid(this) ? NaN : this.getUTCFullYear() - 1900; });\n if (typeof proto.setYear === "function")\n lock(proto, "setYear", function setYear(value) {\n const year = Number(value);\n if (Number.isNaN(year)) return this.setTime(NaN);\n const whole = Math.trunc(year);\n const base = new RealDate(isInvalid(this) ? 0 : this.getTime());\n base.setUTCFullYear(whole >= 0 && whole <= 99 ? 1900 + whole : whole);\n return this.setTime(base.getTime());\n });\n const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];\n const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];\n const pad = (value, width) => String(value).padStart(width, "0");\n const yearText = (year) => (year < 0 ? "-" + pad(-year, 6) : pad(year, 4));\n const dateText = (d) =>\n DAYS[d.getUTCDay()] + " " + MONTHS[d.getUTCMonth()] + " " + pad(d.getUTCDate(), 2) + " " + yearText(d.getUTCFullYear());\n const timeText = (d) =>\n pad(d.getUTCHours(), 2) + ":" + pad(d.getUTCMinutes(), 2) + ":" + pad(d.getUTCSeconds(), 2) + " GMT+0000 (Coordinated Universal Time)";\n const localeDate = (d) => d.getUTCMonth() + 1 + "/" + d.getUTCDate() + "/" + d.getUTCFullYear();\n const localeTime = (d) => {\n const hours = d.getUTCHours();\n return ((hours + 11) % 12) + 1 + ":" + pad(d.getUTCMinutes(), 2) + ":" + pad(d.getUTCSeconds(), 2) + (hours < 12 ? " AM" : " PM");\n };\n const formatter = (format) => function () { return isInvalid(this) ? "Invalid Date" : format(this); };\n lock(proto, "toString", formatter((d) => dateText(d) + " " + timeText(d)));\n lock(proto, "toDateString", formatter(dateText));\n lock(proto, "toTimeString", formatter(timeText));\n lock(proto, "toLocaleString", formatter((d) => localeDate(d) + ", " + localeTime(d)));\n lock(proto, "toLocaleDateString", formatter(localeDate));\n lock(proto, "toLocaleTimeString", formatter(localeTime));\n // A date-time without an offset is local time to the host\'s parser; read it as UTC.\n const DATE_ONLY = /^[+-]?\\\\d{4,6}(?:-\\\\d{2}(?:-\\\\d{2})?)?$/;\n const ZONE_AFTER_TIME = /\\\\d:\\\\d{2}(?::\\\\d{2}(?:[.,]\\\\d+)?)?\\\\s*(?:[AP]M\\\\s*)?(?:Z|[+-]\\\\d{2}(?::?\\\\d{2})?|[ECMP][SD]T)\\\\s*(?:\\\\([^)]*\\\\))?\\\\s*$/i;\n const NAMED_ZONE = /\\\\b(?:GMT|UTC|UT)\\\\b(?:\\\\s*[+-]\\\\d{2}:?\\\\d{2})?\\\\s*(?:\\\\([^)]*\\\\))?\\\\s*$/i;\n const parse = function parse(value) {\n const text = String(value);\n const time = realParse(text);\n if (Number.isNaN(time)) return time;\n const trimmed = text.trim();\n if (DATE_ONLY.test(trimmed) || ZONE_AFTER_TIME.test(trimmed) || NAMED_ZONE.test(trimmed)) return time;\n return time - hostOffset.call(new RealDate(time)) * 60000;\n };\n const toTimeValue = (value) => {\n if (value instanceof RealDate) return value.getTime();\n let primitive = value;\n if (typeof value === "object" && value !== null) {\n primitive = value.valueOf();\n if (typeof primitive === "object" && primitive !== null) primitive = String(value);\n }\n return typeof primitive === "string" ? parse(primitive) : primitive;\n };\n // ---- Number, string and array locale methods ----------------------------\n const group = (digits) => digits.replace(/\\\\B(?=(\\\\d{3})+(?!\\\\d))/g, ",");\n lock(Number.prototype, "toLocaleString", function toLocaleString() {\n const x = Number.prototype.valueOf.call(this);\n if (Number.isNaN(x)) return "NaN";\n const sign = x < 0 || Object.is(x, -0) ? "-" : "";\n const abs = Math.abs(x);\n if (abs === Infinity) return sign + "\\\\u221e";\n const fixed = abs < 1e21 ? abs.toFixed(3) : BigInt(abs).toString() + ".000";\n const [whole, fraction] = fixed.split(".");\n const kept = fraction.replace(/0+$/, "");\n return sign + group(whole) + (kept === "" ? "" : "." + kept);\n });\n if (typeof BigInt === "function")\n lock(BigInt.prototype, "toLocaleString", function toLocaleString() {\n const text = BigInt.prototype.toString.call(this);\n return text.startsWith("-") ? "-" + group(text.slice(1)) : group(text);\n });\n lock(String.prototype, "localeCompare", function localeCompare(that) {\n const a = String(this);\n const b = String(that);\n return a < b ? -1 : a > b ? 1 : 0;\n });\n lock(String.prototype, "toLocaleUpperCase", function toLocaleUpperCase() { return String(this).toUpperCase(); });\n lock(String.prototype, "toLocaleLowerCase", function toLocaleLowerCase() { return String(this).toLowerCase(); });\n const listLocale = function toLocaleString() {\n return Array.prototype.map.call(this, (item) => (item === null || item === undefined ? "" : item.toLocaleString())).join(",");\n };\n lock(Array.prototype, "toLocaleString", listLocale);\n lock(Object.getPrototypeOf(Uint8Array.prototype), "toLocaleString", listLocale);\n // ---- Absent or refused ---------------------------------------------------\n if (!Reflect.deleteProperty(globalThis, "Intl")) lock(globalThis, "Intl", undefined);\n for (const name of ["WeakRef", "FinalizationRegistry"]) {\n lock(globalThis, name, function () { return fail(name + " (garbage collection is not deterministic)"); });\n }\n const LockedDate = function Date(...args) {\n if (new.target === undefined) return new RealDate(now("Date()")).toString();\n if (args.length === 0) return Reflect.construct(RealDate, [now("new Date()")], new.target);\n if (args.length === 1) return Reflect.construct(RealDate, [toTimeValue(args[0])], new.target);\n return Reflect.construct(RealDate, [RealDate.UTC(...args)], new.target);\n };\n lock(LockedDate, "prototype", RealDate.prototype);\n lock(RealDate.prototype, "constructor", LockedDate);\n lock(LockedDate, "now", function now_() { return now("Date.now()"); });\n lock(LockedDate, "parse", parse);\n lock(LockedDate, "UTC", RealDate.UTC);\n lock(globalThis, "Date", LockedDate);\n lock(Math, "random", function random() {\n return hooks !== null ? hooks.random() : fail("Math.random() outside a workflow task");\n });\n const crypto = globalThis.crypto;\n if (crypto !== null && typeof crypto === "object") {\n if (typeof crypto.getRandomValues === "function")\n lock(crypto, "getRandomValues", function getRandomValues(array) {\n if (hooks === null) return fail("crypto.getRandomValues() outside a workflow task");\n for (let i = 0; i < array.length; i++) array[i] = Math.floor(hooks.random() * 256);\n return array;\n });\n if (typeof crypto.randomUUID === "function")\n lock(crypto, "randomUUID", function randomUUID() { return fail("crypto.randomUUID(): use ctx.uuid()"); });\n }\n for (const name of ["setTimeout", "setInterval", "setImmediate", "clearTimeout", "clearInterval", "clearImmediate"]) {\n lock(globalThis, name, function () { return fail(name + "(): use ctx.sleep()"); });\n }\n for (const name of ["fetch", "XMLHttpRequest", "WebSocket", "EventSource"]) {\n lock(globalThis, name, function () { return fail(name + ": do I/O in an activity (ctx.run)"); });\n }\n Object.defineProperty(globalThis, ${JSON.stringify(SHIM_HOOK)}, {\n value: Object.freeze({\n attach(next) { hooks = next; },\n detach() { hooks = null; },\n }),\n writable: false,\n enumerable: false,\n configurable: true,\n });\n})();\n`;\n\nvar WASM_PAGE_BYTES2 = 64 * 1024;\nvar HOST_STACK_OVERFLOW = /maximum call stack size exceeded|too much recursion/i;\nvar ENGINE_HEADROOM_BYTES2 = 32 * 1024 * 1024;\nvar INITIAL_ENGINE_BYTES2 = 16 * 1024 * 1024;\nasync function createTaskModule(api, memoryLimitBytes) {\n const maximum = Math.ceil((memoryLimitBytes + ENGINE_HEADROOM_BYTES2) / WASM_PAGE_BYTES2);\n const initial = Math.min(maximum, INITIAL_ENGINE_BYTES2 / WASM_PAGE_BYTES2);\n return api.newQuickJSWASMModuleFromVariant(\n api.newVariant(api.RELEASE_SYNC, { wasmMemory: new WebAssembly.Memory({ initial, maximum }) })\n );\n}\nfunction describe(ctx, handle) {\n const dumped = ctx.dump(handle);\n if (isRecord(dumped)) {\n const name = typeof dumped["name"] === "string" ? dumped["name"] : "Error";\n const message = typeof dumped["message"] === "string" ? dumped["message"] : JSON.stringify(dumped);\n return { name, message };\n }\n return { name: "Error", message: String(dumped) };\n}\nvar TaskError = class extends Error {\n constructor(cause, message, fatal = false) {\n super(message);\n this.cause = cause;\n this.fatal = fatal;\n }\n cause;\n fatal;\n};\nfunction runTaskInQuickJS(api, module2, code, requestJson, limits) {\n const disposed = { failed: false };\n const outcome = runTask(api, module2, code, requestJson, limits, disposed);\n return disposed.failed && outcome.fatal !== true ? { ...outcome, fatal: true } : outcome;\n}\nfunction runTask(api, module2, code, requestJson, limits, disposed) {\n let runtime;\n let ctx;\n const handles = [];\n const deadline = Date.now() + limits.timeoutMs;\n try {\n runtime = module2.newRuntime();\n runtime.setMemoryLimit(limits.memoryLimitBytes);\n runtime.setMaxStackSize(limits.maxStackSizeBytes);\n runtime.setInterruptHandler(api.shouldInterruptAfterDeadline(deadline));\n const rt = runtime;\n const context = rt.newContext();\n ctx = context;\n const fail = (handle, during) => {\n const { name, message } = describe(context, handle);\n handle.dispose();\n if (Date.now() > deadline && /interrupted/i.test(message))\n throw new TaskError(\n "TASK_TIMEOUT",\n `The workflow task ran past its ${limits.timeoutMs} ms budget`\n );\n if (/out of memory/i.test(message) || name === "InternalError" && /memory/i.test(message))\n throw new TaskError("ISOLATE_MEMORY", `The workflow task ran out of memory ${during}`);\n if (name === "DeterminismViolation") throw new TaskError("NON_DETERMINISM", message);\n throw new TaskError("WORKFLOW_CODE_ERROR", `${name}: ${message} (${during})`);\n };\n const drain = (during) => {\n while (rt.hasPendingJob()) {\n if (Date.now() > deadline)\n throw new TaskError(\n "TASK_TIMEOUT",\n `The workflow task ran past its ${limits.timeoutMs} ms budget`\n );\n const jobs = rt.executePendingJobs();\n if (jobs.error) fail(jobs.error, during);\n }\n };\n const prelude = context.evalCode(TASK_PRELUDE, "fluidgrids-task-prelude.js", {\n type: "global"\n });\n if (prelude.error) fail(prelude.error, "in the task prelude");\n else prelude.value.dispose();\n const evaluated = context.evalCode(code, "workflow-bundle.mjs", { type: "module" });\n if (evaluated.error) fail(evaluated.error, "while loading the bundle");\n else {\n handles.push(evaluated.value);\n drain("while loading the bundle");\n const state = context.getPromiseState(evaluated.value);\n if (state.type === "rejected") fail(state.error, "while loading the bundle");\n else if (state.type === "fulfilled" && state.notAPromise !== true) state.value.dispose();\n }\n const abi = context.getProp(context.global, "__fluidgridsRuntime");\n handles.push(abi);\n if (context.typeof(abi) !== "object")\n throw new TaskError(\n "INVALID_REQUEST",\n "The bundle has no workflow runtime (build it with @fluidgrids/workflows 2026.1028 or later)"\n );\n const protocol = context.getProp(abi, "protocol");\n const protocolValue = context.dump(protocol);\n protocol.dispose();\n if (protocolValue !== HISTORY_PROTOCOL)\n throw new TaskError(\n "PROTOCOL_MISMATCH",\n `The bundle speaks runtime protocol ${String(protocolValue)}; this host speaks ${HISTORY_PROTOCOL}`\n );\n const call = (name, args) => {\n const fn = context.getProp(abi, name);\n try {\n const result = context.callFunction(fn, abi, ...args);\n if (result.error) return fail(result.error, `in ${name}()`);\n return result.value;\n } finally {\n fn.dispose();\n }\n };\n const request = context.newString(requestJson);\n handles.push(request);\n call("start", [request]).dispose();\n drain("in the workflow code");\n for (; ; ) {\n const step = call("step", []);\n const value = context.dump(step);\n step.dispose();\n drain("in the workflow code");\n if (value !== "more") break;\n }\n const completed = call("complete", []);\n const resultValue = context.dump(completed);\n completed.dispose();\n if (typeof resultValue !== "string")\n throw new TaskError("INTERNAL", "The runtime returned no result");\n return { ok: true, resultJson: resultValue };\n } catch (error) {\n if (error instanceof TaskError)\n return {\n ok: false,\n cause: error.cause,\n message: error.message,\n ...error.fatal ? { fatal: true } : {}\n };\n const message = error instanceof Error ? error.message : String(error);\n if (HOST_STACK_OVERFLOW.test(message))\n return {\n ok: false,\n cause: "WORKFLOW_CODE_ERROR",\n message: `RangeError: stack overflow (the engine\'s host stack ran out: ${message})`,\n fatal: true\n };\n return {\n ok: false,\n cause: /memory|out of bounds|abort/i.test(message) ? "ISOLATE_MEMORY" : "INTERNAL",\n message,\n fatal: true\n };\n } finally {\n for (const handle of handles.reverse()) if (handle.alive) handle.dispose();\n try {\n ctx?.dispose();\n runtime?.dispose();\n } catch {\n disposed.failed = true;\n }\n }\n}\n\nprocess.on("unhandledRejection", () => void 0);\nasync function main(input) {\n const port = import_node_worker_threads.parentPort;\n if (!port) return;\n if (!isRecord(input) || typeof input["moduleUrl"] !== "string" || typeof input["memoryLimitBytes"] !== "number") {\n port.postMessage({ type: "fatal", message: "The task worker received no configuration" });\n return;\n }\n const loaded = await import(input["moduleUrl"]);\n const api = isQuickJSApi(loaded) ? loaded : isRecord(loaded) && isQuickJSApi(loaded["default"]) ? loaded["default"] : void 0;\n if (!api) {\n port.postMessage({ type: "fatal", message: "quickjs-emscripten could not be loaded" });\n return;\n }\n const module2 = await createTaskModule(api, input["memoryLimitBytes"]);\n port.on("message", (message) => {\n if (!isRecord(message) || message["type"] !== "task") return;\n const id = message["id"];\n const limits = message["limits"];\n let outcome;\n if (typeof message["code"] !== "string" || typeof message["requestJson"] !== "string" || !isRecord(limits) || typeof limits["timeoutMs"] !== "number" || typeof limits["memoryLimitBytes"] !== "number" || typeof limits["maxStackSizeBytes"] !== "number")\n outcome = {\n ok: false,\n cause: "INTERNAL",\n message: "The task worker received a malformed task"\n };\n else\n outcome = runTaskInQuickJS(api, module2, message["code"], message["requestJson"], {\n timeoutMs: limits["timeoutMs"],\n memoryLimitBytes: limits["memoryLimitBytes"],\n maxStackSizeBytes: limits["maxStackSizeBytes"]\n });\n port.postMessage({ type: "result", id, outcome });\n if (outcome.fatal === true) process.exit(1);\n });\n port.postMessage({ type: "ready" });\n}\nmain(import_node_worker_threads.workerData).catch((error) => {\n import_node_worker_threads.parentPort?.postMessage({\n type: "fatal",\n message: error instanceof Error ? error.message : String(error)\n });\n});\n';
24
+
25
+ // src/quickjs/task-runner.ts
26
+ var MAX_START_FAILURES = 3;
27
+ var DEFAULT_MAX_STACK_SIZE_BYTES = 256 * 1024;
28
+ function quickjsModuleUrl() {
29
+ return pathToFileURL(createRequire(import.meta.url).resolve("quickjs-emscripten")).href;
18
30
  }
19
- function createQuickJSEvaluator(options = {}) {
20
- const timeoutMs = options.timeoutMs ?? 5e3;
21
- const memoryLimitBytes = options.memoryLimitBytes ?? 64 * 1024 * 1024;
22
- const maxStackSizeBytes = options.maxStackSizeBytes ?? 1024 * 1024;
23
- return {
24
- format: "esm",
25
- async evaluate(code) {
26
- const maximum = Math.ceil((memoryLimitBytes + ENGINE_HEADROOM_BYTES) / WASM_PAGE_BYTES);
27
- const initial = Math.min(maximum, INITIAL_ENGINE_BYTES / WASM_PAGE_BYTES);
28
- const engine = await newQuickJSWASMModuleFromVariant(
29
- newVariant(RELEASE_SYNC, { wasmMemory: new WebAssembly.Memory({ initial, maximum }) })
31
+ function parseResult(outcome) {
32
+ if (!isRecord(outcome))
33
+ return failedTaskResult("INTERNAL", "The task worker returned no outcome");
34
+ const typed = outcome;
35
+ if (typed.ok === true && typeof typed.resultJson === "string") {
36
+ try {
37
+ const parsed = JSON.parse(typed.resultJson);
38
+ if (isRecord(parsed) && typeof parsed["outcome"] === "string" && Array.isArray(parsed["commands"]))
39
+ return parsed;
40
+ } catch {
41
+ }
42
+ return failedTaskResult("INTERNAL", "The runtime returned an invalid result");
43
+ }
44
+ if (typed.ok === false && typeof typed.cause === "string")
45
+ return failedTaskResult(typed.cause, String(typed.message));
46
+ return failedTaskResult("INTERNAL", "The task worker returned an invalid outcome");
47
+ }
48
+ function createWorkflowTaskRunner(options = {}) {
49
+ const poolSize = Math.max(1, Math.floor(options.poolSize ?? 4));
50
+ const memoryLimitBytes = options.memoryLimitBytes ?? 128 * 1024 * 1024;
51
+ const maxStackSizeBytes = options.maxStackSizeBytes ?? DEFAULT_MAX_STACK_SIZE_BYTES;
52
+ const defaultTimeoutMs = options.timeoutMs ?? 1e4;
53
+ const cacheEntries = Math.max(1, options.bundleCacheEntries ?? 64);
54
+ const startupTimeoutMs = options.startupTimeoutMs ?? 15e3;
55
+ const verified = /* @__PURE__ */ new Map();
56
+ const queue = [];
57
+ const slots = [];
58
+ let closed = false;
59
+ let moduleUrl;
60
+ let nextJobId = 0;
61
+ let startFailures = 0;
62
+ const finish = (slot, result) => {
63
+ const job = slot.job;
64
+ slot.job = void 0;
65
+ if (slot.timer !== void 0) clearTimeout(slot.timer);
66
+ slot.timer = void 0;
67
+ job?.resolve(result);
68
+ };
69
+ const replace = (slot) => {
70
+ void slot.worker.terminate();
71
+ const index = slots.indexOf(slot);
72
+ if (index >= 0) slots.splice(index, 1);
73
+ if (!closed) spawn();
74
+ pump();
75
+ };
76
+ const startFailed = (slot, reason) => {
77
+ startFailures += 1;
78
+ if (startFailures < MAX_START_FAILURES) {
79
+ replace(slot);
80
+ return;
81
+ }
82
+ startFailures = 0;
83
+ void slot.worker.terminate();
84
+ const index = slots.indexOf(slot);
85
+ if (index >= 0) slots.splice(index, 1);
86
+ if (slots.some((entry) => entry.ready)) return;
87
+ for (const job of queue.splice(0))
88
+ job.resolve(
89
+ failedTaskResult("INTERNAL", `The workflow task threads could not start: ${reason}`)
30
90
  );
31
- const runtime = engine.newRuntime();
32
- const deadline = Date.now() + timeoutMs;
33
- runtime.setMemoryLimit(memoryLimitBytes);
34
- runtime.setMaxStackSize(maxStackSizeBytes);
35
- runtime.setInterruptHandler(shouldInterruptAfterDeadline(deadline));
36
- const ctx = runtime.newContext();
37
- const handles = [];
38
- try {
39
- const result = ctx.evalCode(code, "workflow-bundle.mjs", { type: "module" });
40
- if (result.error) {
41
- const message = errorMessage(ctx, result.error);
42
- result.error.dispose();
43
- throw new LowerError(`Evaluating the workflow code failed: ${message}`);
44
- }
45
- let namespace = result.value;
46
- handles.push(namespace);
47
- const state = ctx.getPromiseState(namespace);
48
- if (!(state.type === "fulfilled" && state.notAPromise === true)) {
49
- if (state.type === "fulfilled") state.value.dispose();
50
- else if (state.type === "rejected") state.error.dispose();
51
- while (runtime.hasPendingJob()) {
52
- if (Date.now() > deadline)
53
- throw new LowerError("Evaluating the workflow code timed out");
54
- const jobs = runtime.executePendingJobs();
55
- if (jobs.error) {
56
- const message = errorMessage(ctx, jobs.error);
57
- jobs.error.dispose();
58
- throw new LowerError(`Evaluating the workflow code failed: ${message}`);
59
- }
60
- }
61
- const settled = ctx.getPromiseState(namespace);
62
- if (settled.type === "rejected") {
63
- const message = errorMessage(ctx, settled.error);
64
- settled.error.dispose();
65
- throw new LowerError(`Evaluating the workflow code failed: ${message}`);
66
- }
67
- if (settled.type === "pending")
68
- throw new LowerError("The workflow module never finished evaluating");
69
- namespace = settled.value;
70
- handles.push(namespace);
91
+ };
92
+ const spawn = () => {
93
+ moduleUrl ??= quickjsModuleUrl();
94
+ const worker = new Worker(QUICKJS_TASK_WORKER_SOURCE, {
95
+ eval: true,
96
+ workerData: { moduleUrl, memoryLimitBytes }
97
+ });
98
+ const slot = { worker, ready: false, jobId: 0 };
99
+ slots.push(slot);
100
+ const startup = setTimeout(() => {
101
+ if (!slot.ready) startFailed(slot, `no thread was ready within ${startupTimeoutMs} ms`);
102
+ }, startupTimeoutMs);
103
+ worker.on("message", (message) => {
104
+ if (!isRecord(message)) return;
105
+ if (message["type"] === "ready") {
106
+ clearTimeout(startup);
107
+ slot.ready = true;
108
+ startFailures = 0;
109
+ pump();
110
+ } else if (message["type"] === "result" && message["id"] === slot.jobId && slot.job) {
111
+ const outcome = message["outcome"];
112
+ finish(slot, parseResult(outcome));
113
+ if (isRecord(outcome) && outcome["fatal"] === true) replace(slot);
114
+ else pump();
115
+ } else if (message["type"] === "fatal") {
116
+ clearTimeout(startup);
117
+ if (!slot.ready) {
118
+ startFailed(slot, String(message["message"]));
119
+ return;
71
120
  }
72
- const exported = ctx.getProp(namespace, "__fluidgrids");
73
- handles.push(exported);
74
- if (ctx.typeof(exported) !== "string")
75
- throw new LowerError("The bundle did not export a workflow collection");
76
- return ctx.getString(exported);
121
+ if (slot.job) finish(slot, failedTaskResult("INTERNAL", String(message["message"])));
122
+ replace(slot);
123
+ }
124
+ });
125
+ worker.on("error", (error) => {
126
+ clearTimeout(startup);
127
+ const outOfMemory = "code" in error && error.code === "ERR_WORKER_OUT_OF_MEMORY";
128
+ if (slot.job)
129
+ finish(
130
+ slot,
131
+ failedTaskResult(
132
+ outOfMemory ? "ISOLATE_MEMORY" : "INTERNAL",
133
+ outOfMemory ? "The workflow task ran out of memory" : `The task thread failed: ${error.message}`
134
+ )
135
+ );
136
+ });
137
+ worker.on("exit", () => {
138
+ clearTimeout(startup);
139
+ if (!slots.includes(slot)) return;
140
+ if (!slot.ready) {
141
+ startFailed(slot, "the thread exited while starting");
142
+ return;
143
+ }
144
+ if (slot.job) finish(slot, failedTaskResult("INTERNAL", "The task thread exited"));
145
+ replace(slot);
146
+ });
147
+ };
148
+ const pump = () => {
149
+ for (const slot of slots) {
150
+ if (!slot.ready || slot.job) continue;
151
+ const job = queue.shift();
152
+ if (!job) return;
153
+ nextJobId += 1;
154
+ slot.jobId = nextJobId;
155
+ slot.job = job;
156
+ slot.timer = setTimeout(() => {
157
+ finish(
158
+ slot,
159
+ failedTaskResult(
160
+ "TASK_TIMEOUT",
161
+ `The workflow task ran past its ${job.timeoutMs} ms budget`
162
+ )
163
+ );
164
+ replace(slot);
165
+ }, job.timeoutMs + 250);
166
+ slot.worker.postMessage({
167
+ type: "task",
168
+ id: slot.jobId,
169
+ code: job.code,
170
+ requestJson: job.requestJson,
171
+ limits: { timeoutMs: job.timeoutMs, memoryLimitBytes, maxStackSizeBytes }
172
+ });
173
+ }
174
+ };
175
+ const verify = (code, buildId) => {
176
+ const known = verified.get(buildId);
177
+ if (known === code) {
178
+ verified.delete(buildId);
179
+ verified.set(buildId, code);
180
+ return true;
181
+ }
182
+ if (buildIdOf(code) !== buildId) return false;
183
+ verified.delete(buildId);
184
+ verified.set(buildId, code);
185
+ while (verified.size > cacheEntries) {
186
+ const oldest = verified.keys().next().value;
187
+ if (oldest === void 0) break;
188
+ verified.delete(oldest);
189
+ }
190
+ return true;
191
+ };
192
+ return {
193
+ run(bundle, request, runOptions = {}) {
194
+ if (closed) return Promise.resolve(failedTaskResult("INTERNAL", "The task runner is closed"));
195
+ const code = typeof bundle === "string" ? bundle : bundle.code;
196
+ const buildId = typeof bundle === "string" ? void 0 : bundle.buildId;
197
+ if (buildId !== void 0 && !verify(code, buildId))
198
+ return Promise.resolve(
199
+ failedTaskResult("BUNDLE_INTEGRITY", `The bundle does not hash to ${buildId}`)
200
+ );
201
+ let requestJson;
202
+ try {
203
+ requestJson = JSON.stringify(request);
77
204
  } catch (error) {
78
- if (error instanceof LowerError) throw error;
79
- const message = error instanceof Error ? error.message : String(error);
80
- throw new LowerError(`Evaluating the workflow code failed: ${message}`);
81
- } finally {
82
- for (const handle of handles.reverse()) if (handle.alive) handle.dispose();
83
- ctx.dispose();
84
- runtime.dispose();
205
+ return Promise.resolve(
206
+ failedTaskResult(
207
+ "INVALID_REQUEST",
208
+ error instanceof Error ? error.message : String(error)
209
+ )
210
+ );
85
211
  }
212
+ if (slots.length === 0) for (let index = 0; index < poolSize; index++) spawn();
213
+ return new Promise((resolve) => {
214
+ queue.push({
215
+ code,
216
+ requestJson,
217
+ timeoutMs: runOptions.timeoutMs ?? defaultTimeoutMs,
218
+ resolve
219
+ });
220
+ pump();
221
+ });
222
+ },
223
+ async close() {
224
+ closed = true;
225
+ for (const job of queue.splice(0))
226
+ job.resolve(failedTaskResult("INTERNAL", "The task runner is closed"));
227
+ const all = slots.splice(0);
228
+ for (const slot of all)
229
+ if (slot.job) finish(slot, failedTaskResult("INTERNAL", "The task runner is closed"));
230
+ await Promise.all(all.map((slot) => slot.worker.terminate()));
231
+ }
232
+ };
233
+ }
234
+
235
+ // src/quickjs/index.ts
236
+ function quickjsModuleUrl2() {
237
+ try {
238
+ return pathToFileURL(createRequire(import.meta.url).resolve("quickjs-emscripten")).href;
239
+ } catch (error) {
240
+ throw new LowerError(
241
+ `quickjs-emscripten cannot be resolved: ${error instanceof Error ? error.message : String(error)}`,
242
+ "crashed"
243
+ );
244
+ }
245
+ }
246
+ function createQuickJSEvaluator(options = {}) {
247
+ const limits = {
248
+ timeoutMs: options.timeoutMs ?? 5e3,
249
+ memoryLimitBytes: options.memoryLimitBytes ?? 64 * 1024 * 1024,
250
+ maxStackSizeBytes: options.maxStackSizeBytes ?? 1024 * 1024
251
+ };
252
+ return {
253
+ format: "esm",
254
+ async evaluate(code) {
255
+ const outcome = await runIsolateWorker({
256
+ source: QUICKJS_WORKER_SOURCE,
257
+ workerData: { code, limits, moduleUrl: quickjsModuleUrl2() },
258
+ timeoutMs: limits.timeoutMs
259
+ });
260
+ return outcomeToCollection(outcome);
86
261
  }
87
262
  };
88
263
  }
89
264
 
90
- export { createQuickJSEvaluator };
265
+ export { DEFAULT_MAX_STACK_SIZE_BYTES, createQuickJSEvaluator, createWorkflowTaskRunner };