bunqueue 2.8.59 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +109 -77
  2. package/dist/application/background/dlq.js +2 -0
  3. package/dist/application/contextFactory.js +5 -0
  4. package/dist/application/dlqManager.d.ts +11 -1
  5. package/dist/application/dlqManager.js +48 -10
  6. package/dist/application/dlqRetry.js +59 -38
  7. package/dist/application/eventsManager.d.ts +1 -1
  8. package/dist/application/eventsManager.js +30 -26
  9. package/dist/application/lockManager.js +1 -1
  10. package/dist/application/operations/jobManagement.d.ts +4 -0
  11. package/dist/application/operations/jobMoveOperations.js +9 -4
  12. package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
  13. package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
  14. package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
  15. package/dist/application/postgres-queue-manager/cloud.js +55 -0
  16. package/dist/application/postgres-queue-manager/config.d.ts +5 -0
  17. package/dist/application/postgres-queue-manager/config.js +1 -0
  18. package/dist/application/postgres-queue-manager/control.d.ts +26 -0
  19. package/dist/application/postgres-queue-manager/control.js +186 -0
  20. package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
  21. package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
  22. package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
  23. package/dist/application/postgres-queue-manager/delivery.js +80 -0
  24. package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
  25. package/dist/application/postgres-queue-manager/lease.js +88 -0
  26. package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
  27. package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
  28. package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
  29. package/dist/application/postgres-queue-manager/maintenance.js +100 -0
  30. package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
  31. package/dist/application/postgres-queue-manager/operationGate.js +96 -0
  32. package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
  33. package/dist/application/postgres-queue-manager/operations.js +194 -0
  34. package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
  35. package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
  36. package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
  37. package/dist/application/postgres-queue-manager/queries.js +231 -0
  38. package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
  39. package/dist/application/postgres-queue-manager/relationships.js +45 -0
  40. package/dist/application/postgres-queue-manager/services.d.ts +31 -0
  41. package/dist/application/postgres-queue-manager/services.js +149 -0
  42. package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
  43. package/dist/application/postgres-queue-manager/snapshot.js +236 -0
  44. package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
  45. package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
  46. package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
  47. package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
  48. package/dist/application/postgres-queue-manager/state.d.ts +47 -0
  49. package/dist/application/postgres-queue-manager/state.js +273 -0
  50. package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
  51. package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
  52. package/dist/application/postgresQueueManager.d.ts +2 -0
  53. package/dist/application/postgresQueueManager.js +1 -0
  54. package/dist/application/queue-manager/ack.js +1 -1
  55. package/dist/application/queue-manager/delivery.js +1 -1
  56. package/dist/application/queue-manager/flow-failures.js +4 -0
  57. package/dist/application/queue-manager/limits.d.ts +2 -0
  58. package/dist/application/queue-manager/limits.js +4 -0
  59. package/dist/application/queue-manager/observability.js +15 -3
  60. package/dist/application/queue-manager/queries.d.ts +7 -0
  61. package/dist/application/queue-manager/queries.js +9 -0
  62. package/dist/application/queue-manager/services.d.ts +1 -1
  63. package/dist/application/queue-manager/services.js +4 -2
  64. package/dist/application/queue-manager/state.js +16 -1
  65. package/dist/application/statsManager.d.ts +1 -1
  66. package/dist/application/statsManager.js +1 -1
  67. package/dist/cli/commands/server.js +1 -1
  68. package/dist/client/events.js +2 -2
  69. package/dist/client/flow.js +0 -1
  70. package/dist/client/forwarder.d.ts +6 -4
  71. package/dist/client/forwarder.js +5 -3
  72. package/dist/client/queue/dlq.d.ts +2 -0
  73. package/dist/client/queue/dlq.js +14 -0
  74. package/dist/client/queue/dlqOps.d.ts +2 -0
  75. package/dist/client/queue/dlqOps.js +4 -0
  76. package/dist/client/queue/helpers.js +2 -0
  77. package/dist/client/queue/operations/add/single.js +1 -1
  78. package/dist/client/queue/runtime/configuration.d.ts +4 -0
  79. package/dist/client/queue/runtime/configuration.js +8 -0
  80. package/dist/client/sandboxed/runtime/state.js +2 -2
  81. package/dist/client/tcp/connection.d.ts +1 -1
  82. package/dist/client/tcp/connection.js +1 -1
  83. package/dist/client/tcp/runtime/state.js +2 -2
  84. package/dist/client/types/metrics.d.ts +1 -1
  85. package/dist/client/worker/runtime/control.js +0 -1
  86. package/dist/client/worker/runtime/execution.js +3 -8
  87. package/dist/client/worker/runtime/lifecycle.js +0 -1
  88. package/dist/client/worker/runtime/polling.d.ts +1 -0
  89. package/dist/client/worker/runtime/polling.js +9 -0
  90. package/dist/client/worker/runtime/state.js +3 -3
  91. package/dist/client/workflow/compensationChild.d.ts +2 -0
  92. package/dist/client/workflow/compensationChild.js +3 -1
  93. package/dist/client/workflow/compensationPass.d.ts +1 -0
  94. package/dist/client/workflow/compensationPass.js +18 -2
  95. package/dist/client/workflow/compensator.d.ts +2 -1
  96. package/dist/client/workflow/compensator.js +11 -3
  97. package/dist/client/workflow/engine.js +1 -1
  98. package/dist/client/workflow/executionFence.d.ts +15 -0
  99. package/dist/client/workflow/executionFence.js +29 -0
  100. package/dist/client/workflow/executor.d.ts +5 -11
  101. package/dist/client/workflow/executor.js +48 -47
  102. package/dist/client/workflow/executorLifecycle.d.ts +1 -0
  103. package/dist/client/workflow/executorLifecycle.js +41 -10
  104. package/dist/client/workflow/executorNodes.d.ts +1 -0
  105. package/dist/client/workflow/executorNodes.js +45 -16
  106. package/dist/client/workflow/executorQueue.d.ts +9 -0
  107. package/dist/client/workflow/executorQueue.js +15 -0
  108. package/dist/client/workflow/forEachRunner.d.ts +4 -0
  109. package/dist/client/workflow/forEachRunner.js +70 -0
  110. package/dist/client/workflow/loops.d.ts +4 -5
  111. package/dist/client/workflow/loops.js +27 -93
  112. package/dist/client/workflow/mapRunner.d.ts +1 -1
  113. package/dist/client/workflow/mapRunner.js +11 -2
  114. package/dist/client/workflow/recovery.d.ts +1 -0
  115. package/dist/client/workflow/recovery.js +24 -18
  116. package/dist/client/workflow/rollbackControl.d.ts +1 -0
  117. package/dist/client/workflow/rollbackControl.js +5 -2
  118. package/dist/client/workflow/runner.d.ts +3 -1
  119. package/dist/client/workflow/runner.js +26 -3
  120. package/dist/client/workflow/stepTypes.js +1 -1
  121. package/dist/client/workflow/subWorkflowRunner.d.ts +1 -0
  122. package/dist/client/workflow/subWorkflowRunner.js +7 -0
  123. package/dist/client/workflow/waitFor.d.ts +4 -0
  124. package/dist/client/workflow/waitFor.js +23 -11
  125. package/dist/client/workflow/workflowDecisions.d.ts +1 -1
  126. package/dist/client/workflow/workflowDecisions.js +3 -1
  127. package/dist/config/resolve.d.ts +14 -0
  128. package/dist/config/resolve.js +50 -5
  129. package/dist/config/types.d.ts +18 -0
  130. package/dist/domain/queue/dlqShard.d.ts +2 -0
  131. package/dist/domain/queue/dlqShard.js +16 -0
  132. package/dist/domain/queue/shard/dlq.d.ts +1 -0
  133. package/dist/domain/queue/shard/dlq.js +3 -0
  134. package/dist/domain/types/commands/dlq.d.ts +5 -0
  135. package/dist/domain/types/commands/union.d.ts +1 -1
  136. package/dist/domain/types/cron.d.ts +5 -0
  137. package/dist/domain/types/cron.js +14 -3
  138. package/dist/domain/types/dlq.d.ts +1 -1
  139. package/dist/domain/types/dlq.js +1 -2
  140. package/dist/domain/types/jobs/model.d.ts +1 -1
  141. package/dist/infrastructure/cloud/commandHandler.js +5 -3
  142. package/dist/infrastructure/cloud/commands/integrations.js +11 -11
  143. package/dist/infrastructure/cloud/commands/jobs.js +48 -40
  144. package/dist/infrastructure/cloud/commands/queues.js +61 -75
  145. package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
  146. package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
  147. package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
  148. package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
  149. package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
  150. package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
  151. package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
  152. package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
  153. package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
  154. package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
  155. package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
  156. package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
  157. package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
  158. package/dist/infrastructure/cloud/snapshot/config.js +43 -0
  159. package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
  160. package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
  161. package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
  162. package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
  163. package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
  164. package/dist/infrastructure/cloud/statsRefresh.js +2 -1
  165. package/dist/infrastructure/cloud/types/command.d.ts +2 -2
  166. package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
  167. package/dist/infrastructure/persistence/postgres/admission.js +193 -0
  168. package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
  169. package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
  170. package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
  171. package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
  172. package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
  173. package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
  174. package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
  175. package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
  176. package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
  177. package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
  178. package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
  179. package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
  180. package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
  181. package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
  182. package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
  183. package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
  184. package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
  185. package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
  186. package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
  187. package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
  188. package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
  189. package/dist/infrastructure/persistence/postgres/claims.js +137 -0
  190. package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
  191. package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
  192. package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
  193. package/dist/infrastructure/persistence/postgres/codec.js +82 -0
  194. package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
  195. package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
  196. package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
  197. package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
  198. package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
  199. package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
  200. package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
  201. package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
  202. package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
  203. package/dist/infrastructure/persistence/postgres/connection.js +15 -0
  204. package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
  205. package/dist/infrastructure/persistence/postgres/context.js +74 -0
  206. package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
  207. package/dist/infrastructure/persistence/postgres/control.js +105 -0
  208. package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
  209. package/dist/infrastructure/persistence/postgres/crons.js +205 -0
  210. package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
  211. package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
  212. package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
  213. package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
  214. package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
  215. package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
  216. package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
  217. package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
  218. package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
  219. package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
  220. package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
  221. package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
  222. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
  223. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
  224. package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
  225. package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
  226. package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
  227. package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
  228. package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
  229. package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
  230. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
  231. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
  232. package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
  233. package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
  234. package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
  235. package/dist/infrastructure/persistence/postgres/events.js +249 -0
  236. package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
  237. package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
  238. package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
  239. package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
  240. package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
  241. package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
  242. package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
  243. package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
  244. package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
  245. package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
  246. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
  247. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
  248. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
  249. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
  250. package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
  251. package/dist/infrastructure/persistence/postgres/logs.js +70 -0
  252. package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
  253. package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
  254. package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
  255. package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
  256. package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
  257. package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
  258. package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
  259. package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
  260. package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
  261. package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
  262. package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
  263. package/dist/infrastructure/persistence/postgres/policies.js +18 -0
  264. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
  265. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
  266. package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
  267. package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
  268. package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
  269. package/dist/infrastructure/persistence/postgres/queries.js +152 -0
  270. package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
  271. package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
  272. package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
  273. package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
  274. package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
  275. package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
  276. package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
  277. package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
  278. package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
  279. package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
  280. package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
  281. package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
  282. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
  283. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
  284. package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
  285. package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
  286. package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
  287. package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
  288. package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
  289. package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
  290. package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
  291. package/dist/infrastructure/persistence/postgres/schema.js +257 -0
  292. package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
  293. package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
  294. package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
  295. package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
  296. package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
  297. package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
  298. package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
  299. package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
  300. package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
  301. package/dist/infrastructure/persistence/postgres/store.js +136 -0
  302. package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
  303. package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
  304. package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
  305. package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
  306. package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
  307. package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
  308. package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
  309. package/dist/infrastructure/persistence/postgres/types.js +1 -0
  310. package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
  311. package/dist/infrastructure/persistence/postgres/workers.js +126 -0
  312. package/dist/infrastructure/persistence/postgres.d.ts +2 -0
  313. package/dist/infrastructure/persistence/postgres.js +1 -0
  314. package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
  315. package/dist/infrastructure/persistence/sqlite.d.ts +1 -1
  316. package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
  317. package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
  318. package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
  319. package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
  320. package/dist/infrastructure/scheduler/cron/validation.js +11 -0
  321. package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
  322. package/dist/infrastructure/scheduler/cronParser.js +144 -11
  323. package/dist/infrastructure/server/bootstrap.d.ts +2 -3
  324. package/dist/infrastructure/server/bootstrap.js +33 -59
  325. package/dist/infrastructure/server/errors.d.ts +4 -0
  326. package/dist/infrastructure/server/errors.js +54 -0
  327. package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
  328. package/dist/infrastructure/server/handler-routes/control.js +3 -1
  329. package/dist/infrastructure/server/handler-routes/jobs.js +0 -3
  330. package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
  331. package/dist/infrastructure/server/handler.js +9 -11
  332. package/dist/infrastructure/server/handlerRoutes.d.ts +2 -2
  333. package/dist/infrastructure/server/handlerRoutes.js +2 -2
  334. package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
  335. package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
  336. package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
  337. package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
  338. package/dist/infrastructure/server/handlers/core.js +32 -31
  339. package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
  340. package/dist/infrastructure/server/handlers/cron.js +60 -59
  341. package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
  342. package/dist/infrastructure/server/handlers/dashboard.js +21 -8
  343. package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
  344. package/dist/infrastructure/server/handlers/dlq.js +35 -9
  345. package/dist/infrastructure/server/handlers/flow.js +2 -1
  346. package/dist/infrastructure/server/handlers/management.d.ts +5 -5
  347. package/dist/infrastructure/server/handlers/management.js +36 -9
  348. package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
  349. package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
  350. package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
  351. package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
  352. package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
  353. package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
  354. package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
  355. package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
  356. package/dist/infrastructure/server/handlers/query.d.ts +1 -1
  357. package/dist/infrastructure/server/handlers/query.js +5 -4
  358. package/dist/infrastructure/server/http.js +2 -2
  359. package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
  360. package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
  361. package/dist/infrastructure/server/httpEndpoints.js +20 -7
  362. package/dist/infrastructure/server/httpRouteQueues.js +11 -1
  363. package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
  364. package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
  365. package/dist/infrastructure/server/sseHandler.js +8 -3
  366. package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
  367. package/dist/infrastructure/server/storageAdapter.js +61 -0
  368. package/dist/infrastructure/server/storageManager.d.ts +12 -0
  369. package/dist/infrastructure/server/storageManager.js +102 -0
  370. package/dist/infrastructure/server/tcp.js +2 -3
  371. package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
  372. package/dist/infrastructure/server/ws/snapshots.js +19 -4
  373. package/dist/infrastructure/server/wsHandler.js +10 -5
  374. package/dist/main.js +1 -1
  375. package/dist/mcp/backend/embedded/services.js +2 -1
  376. package/dist/mcp/index.d.ts +2 -2
  377. package/dist/mcp/index.js +2 -2
  378. package/dist/shared/storageHealth.d.ts +9 -0
  379. package/dist/shared/storageHealth.js +12 -0
  380. package/package.json +85 -74
@@ -5,11 +5,13 @@
5
5
  * The edge pattern: jobs buffer in the local queue (typically embedded
6
6
  * SQLite on a gateway); the Forwarder drains them to a central server over
7
7
  * TCP/TLS. A remote failure makes the local job fail → local retry/backoff →
8
- * local DLQ, so nothing is lost while the uplink is down.
8
+ * local DLQ. With a persistent local `dataPath`, queued work survives uplink
9
+ * outages and process restarts; memory-only sources provide no such guarantee.
9
10
  *
10
11
  * Idempotency: every forwarded job carries the deterministic remote jobId
11
- * `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds, so a
12
- * re-forward after a crash or retry never duplicates the job remotely.
12
+ * `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds while
13
+ * their ownership is retained; after bounded retention or removal, downstream
14
+ * effects must still tolerate at-least-once delivery.
13
15
  */
14
16
  import { EventEmitter } from 'events';
15
17
  import type { ConnectionOptions, QueueOptions } from './types';
@@ -21,7 +23,7 @@ export interface ForwardOptions {
21
23
  queue?: string;
22
24
  /** Parallel forwards (default: 4) */
23
25
  concurrency?: number;
24
- /** Push to the remote with durable: true (immediate fsync server-side) */
26
+ /** Push with durable: true (bypasses SQLite buffering; PostgreSQL is already transactional). */
25
27
  durable?: boolean;
26
28
  }
27
29
  /** Source queue identity + config, provided by Queue.forward() */
@@ -5,11 +5,13 @@
5
5
  * The edge pattern: jobs buffer in the local queue (typically embedded
6
6
  * SQLite on a gateway); the Forwarder drains them to a central server over
7
7
  * TCP/TLS. A remote failure makes the local job fail → local retry/backoff →
8
- * local DLQ, so nothing is lost while the uplink is down.
8
+ * local DLQ. With a persistent local `dataPath`, queued work survives uplink
9
+ * outages and process restarts; memory-only sources provide no such guarantee.
9
10
  *
10
11
  * Idempotency: every forwarded job carries the deterministic remote jobId
11
- * `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds, so a
12
- * re-forward after a crash or retry never duplicates the job remotely.
12
+ * `fwd:<localQueueKey>:<localJobId>`. The server dedupes custom jobIds while
13
+ * their ownership is retained; after bounded retention or removal, downstream
14
+ * effects must still tolerate at-least-once delivery.
13
15
  */
14
16
  import { EventEmitter } from 'events';
15
17
  import { Worker } from './worker';
@@ -52,6 +52,8 @@ export declare function purgeDlq(ctx: DlqContext): number;
52
52
  * purge-then-assert-empty patterns need this variant.
53
53
  */
54
54
  export declare function purgeDlqAsync(ctx: DlqContext): Promise<number>;
55
+ /** Remove one DLQ entry without re-enqueuing it. */
56
+ export declare function removeDlqJobAsync(ctx: DlqContext, id: string): Promise<boolean>;
55
57
  /** Retry completed job */
56
58
  export declare function retryCompleted(ctx: DlqContext, id?: string): number;
57
59
  /** Retry completed job (async) */
@@ -197,6 +197,20 @@ export async function purgeDlqAsync(ctx) {
197
197
  return 0;
198
198
  return (response.count ?? 0);
199
199
  }
200
+ /** Remove one DLQ entry without re-enqueuing it. */
201
+ export async function removeDlqJobAsync(ctx, id) {
202
+ if (ctx.embedded)
203
+ return dlqOps.removeDlqJobEmbedded(ctx.name, id);
204
+ if (!ctx.tcp)
205
+ return false;
206
+ const response = await ctx.tcp.send({ cmd: 'RemoveDlqJob', queue: ctx.name, jobId: id });
207
+ if (!response.ok) {
208
+ const error = typeof response.error === 'string' ? response.error : 'Failed to remove the DLQ job';
209
+ throw new Error(error);
210
+ }
211
+ const payload = response;
212
+ return payload.data?.removed === true;
213
+ }
200
214
  /** Retry completed job */
201
215
  export function retryCompleted(ctx, id) {
202
216
  if (ctx.embedded) {
@@ -18,6 +18,8 @@ export declare function retryDlqByFilterEmbedded(queue: string, filter: DlqFilte
18
18
  export declare function retryDlqEmbedded(queue: string, id?: string): number;
19
19
  /** Purge DLQ (embedded) */
20
20
  export declare function purgeDlqEmbedded(queue: string): number;
21
+ /** Remove one DLQ entry in embedded mode. */
22
+ export declare function removeDlqJobEmbedded(queue: string, id: string): boolean;
21
23
  /** Set stall config (embedded only) */
22
24
  export declare function setStallConfigEmbedded(queue: string, config: Record<string, unknown>): void;
23
25
  /** Get stall config (embedded only) */
@@ -50,6 +50,10 @@ export function retryDlqEmbedded(queue, id) {
50
50
  export function purgeDlqEmbedded(queue) {
51
51
  return getSharedManager().purgeDlq(queue);
52
52
  }
53
+ /** Remove one DLQ entry in embedded mode. */
54
+ export function removeDlqJobEmbedded(queue, id) {
55
+ return getSharedManager().removeDlqJob(queue, jobId(id));
56
+ }
53
57
  /** Set stall config (embedded only) */
54
58
  export function setStallConfigEmbedded(queue, config) {
55
59
  const manager = getSharedManager();
@@ -20,6 +20,8 @@ export function getDlqContext(manager) {
20
20
  shards: getShards(manager),
21
21
  jobIndex: manager.getJobIndex(),
22
22
  jobResults: manager.jobResults,
23
+ dependencyResults: manager.dependencyResults,
24
+ customIdMap: manager.customIdMap,
23
25
  jobLogs: manager.jobLogs,
24
26
  // #110-class: without storage, retryDlqByFilter's deleteDlqEntry/insertJob
25
27
  // silently no-op — filtered retries were never persisted in embedded mode
@@ -57,7 +57,7 @@ export async function add(context, jobName, data, options = {}) {
57
57
  updateProgress: async (id, progress, message) => {
58
58
  await manager.updateProgress(jobId(id), progress, message);
59
59
  },
60
- // biome-ignore lint/suspicious/useAwait: public callback contract is asynchronous.
60
+ // oxlint-disable-next-line typescript/require-await -- public callback contract is asynchronous
61
61
  log: async (id, message) => {
62
62
  manager.addLog(jobId(id), message);
63
63
  },
@@ -21,6 +21,10 @@ export declare class QueueConfiguration<T> extends QueueControl<T> {
21
21
  retryDlqByFilterAsync(filter: DlqFilter): Promise<number>;
22
22
  purgeDlq(): number;
23
23
  purgeDlqAsync(): Promise<number>;
24
+ /** Remove one DLQ entry and resolve when the removal completes. */
25
+ removeDlqJob(id: string): Promise<boolean>;
26
+ /** Explicit async alias for removeDlqJob. */
27
+ removeDlqJobAsync(id: string): Promise<boolean>;
24
28
  retryCompleted(id?: string): number;
25
29
  retryCompletedAsync(id?: string): Promise<number>;
26
30
  setGlobalConcurrency(concurrency: number): void;
@@ -61,6 +61,14 @@ export class QueueConfiguration extends QueueControl {
61
61
  purgeDlqAsync() {
62
62
  return dlqOps.purgeDlqAsync(this.ctx);
63
63
  }
64
+ /** Remove one DLQ entry and resolve when the removal completes. */
65
+ removeDlqJob(id) {
66
+ return dlqOps.removeDlqJobAsync(this.ctx, id);
67
+ }
68
+ /** Explicit async alias for removeDlqJob. */
69
+ removeDlqJobAsync(id) {
70
+ return this.removeDlqJob(id);
71
+ }
64
72
  retryCompleted(id) {
65
73
  return dlqOps.retryCompleted(this.ctx, id);
66
74
  }
@@ -3,11 +3,11 @@ import { getSharedManager } from '../../manager';
3
3
  import { getSharedPool } from '../../tcpPool';
4
4
  import { createEmbeddedOps, createTcpOps } from '../queueOps';
5
5
  export class SandboxedState extends EventEmitter {
6
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback listener is intentionally untyped
6
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback listener is intentionally untyped
7
7
  on(event, listener) {
8
8
  return super.on(event, listener);
9
9
  }
10
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback listener is intentionally untyped
10
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback listener is intentionally untyped
11
11
  once(event, listener) {
12
12
  return super.once(event, listener);
13
13
  }
@@ -1,4 +1,4 @@
1
1
  /** Stable low-level TCP connection import surface. */
2
2
  export { CommandQueue } from './commandQueue';
3
- export { buildClientTls, createConnection, tlsRequiresVerification, } from './transport';
3
+ export { buildClientTls, createConnection, tlsRequiresVerification } from './transport';
4
4
  export type { ConnectionEvents, ConnectionResult, ConnectionTarget } from './transport';
@@ -1,3 +1,3 @@
1
1
  /** Stable low-level TCP connection import surface. */
2
2
  export { CommandQueue } from './commandQueue';
3
- export { buildClientTls, createConnection, tlsRequiresVerification, } from './transport';
3
+ export { buildClientTls, createConnection, tlsRequiresVerification } from './transport';
@@ -5,11 +5,11 @@ import { ReconnectManager } from '../reconnect';
5
5
  import { DEFAULT_CONNECTION } from '../types';
6
6
  /** Shared TCP client state and event contract. */
7
7
  export class TcpClientState extends EventEmitter {
8
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback overload requires any[]
8
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback overload requires any[]
9
9
  on(event, listener) {
10
10
  return super.on(event, listener);
11
11
  }
12
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback overload requires any[]
12
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback overload requires any[]
13
13
  once(event, listener) {
14
14
  return super.once(event, listener);
15
15
  }
@@ -1 +1 @@
1
- export type { QueueMetrics, QueueMetricsMeta, QueueMetricType, } from '../../domain/types/metrics';
1
+ export type { QueueMetrics, QueueMetricsMeta, QueueMetricType } from '../../domain/types/metrics';
@@ -152,7 +152,6 @@ export class WorkerControl extends WorkerState {
152
152
  async startStalledCheckTimer() {
153
153
  // No-op for API compatibility; stall detection is automatic.
154
154
  }
155
- // biome-ignore lint/suspicious/useAwait: public compatibility contract is asynchronous
156
155
  async delay(milliseconds = 0, abortController) {
157
156
  if (milliseconds <= 0)
158
157
  return;
@@ -39,15 +39,10 @@ export class WorkerExecution extends WorkerPolling {
39
39
  if (this.groupLimiter)
40
40
  this.groupLimiter.decrement(job);
41
41
  if (this.running && !this._closing)
42
- this.poll();
42
+ this.scheduleProcessing();
43
43
  });
44
- if (this.activeJobs < this.opts.concurrency && !this._closing && !this.processingScheduled) {
45
- this.processingScheduled = true;
46
- setImmediate(() => {
47
- this.processingScheduled = false;
48
- void this.tryProcess();
49
- });
50
- }
44
+ if (this.activeJobs < this.opts.concurrency && !this._closing)
45
+ this.scheduleProcessing();
51
46
  return true;
52
47
  }
53
48
  registerWithServer() {
@@ -2,7 +2,6 @@ import { jobId } from '../../../domain/types/job';
2
2
  import { getSharedManager } from '../../manager';
3
3
  import { WorkerManual } from './manual';
4
4
  export class WorkerLifecycle extends WorkerManual {
5
- // biome-ignore lint/suspicious/useAwait: preserves the public async close contract
6
5
  async close(force = false) {
7
6
  if (this.closed)
8
7
  return;
@@ -3,6 +3,7 @@ import { WorkerBuffer } from './buffer';
3
3
  import type { PulledJob, WorkerDelivery } from './state';
4
4
  export declare abstract class WorkerPolling<T = unknown, R = unknown> extends WorkerBuffer<T, R> {
5
5
  protected poll(): void;
6
+ protected scheduleProcessing(): void;
6
7
  protected tryProcess(): Promise<void>;
7
8
  protected doPullBatch(): Promise<PulledJob[]>;
8
9
  private scheduleRateLimitPoll;
@@ -16,6 +16,15 @@ export class WorkerPolling extends WorkerBuffer {
16
16
  }
17
17
  void this.tryProcess();
18
18
  }
19
+ scheduleProcessing() {
20
+ if (!this.running || this._closing || this.processingScheduled)
21
+ return;
22
+ this.processingScheduled = true;
23
+ setImmediate(() => {
24
+ this.processingScheduled = false;
25
+ void this.tryProcess();
26
+ });
27
+ }
19
28
  async tryProcess() {
20
29
  if (!this.running || this._closing)
21
30
  return;
@@ -46,15 +46,15 @@ export class WorkerState extends EventEmitter {
46
46
  lastDrainedEmit = 0;
47
47
  stalledUnsubscribe = null;
48
48
  stalledSubscription = null;
49
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback listener is intentionally untyped
49
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback listener is intentionally untyped
50
50
  on(event, listener) {
51
51
  return super.on(event, listener);
52
52
  }
53
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback listener is intentionally untyped
53
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback listener is intentionally untyped
54
54
  once(event, listener) {
55
55
  return super.once(event, listener);
56
56
  }
57
- // biome-ignore lint/suspicious/noExplicitAny: EventEmitter's fallback listener is intentionally untyped
57
+ // oxlint-disable-next-line typescript/no-explicit-any -- EventEmitter's fallback listener is intentionally untyped
58
58
  off(event, listener) {
59
59
  return super.off(event, listener);
60
60
  }
@@ -11,6 +11,7 @@ export interface ChildUnwindRequest {
11
11
  emitter: WorkflowEmitter | null;
12
12
  workflows?: Map<string, Workflow>;
13
13
  retryFailed?: boolean;
14
+ assertActive: () => void;
14
15
  }
15
16
  export type ChildUnwind = (request: ChildUnwindRequest) => Promise<'ran' | LostCompensationClaim>;
16
17
  export interface UnwindChildRequest {
@@ -20,6 +21,7 @@ export interface UnwindChildRequest {
20
21
  workflows?: Map<string, Workflow>;
21
22
  runChild: ChildUnwind;
22
23
  retryFailed?: boolean;
24
+ assertActive: () => void;
23
25
  }
24
26
  /** Run the child workflow's own unwind and propagate every uncertain outcome. */
25
27
  export declare function unwindChild(request: UnwindChildRequest): Promise<void>;
@@ -2,7 +2,8 @@
2
2
  import { isLive } from './admission';
3
3
  /** Run the child workflow's own unwind and propagate every uncertain outcome. */
4
4
  export async function unwindChild(request) {
5
- const { record, store, emitter, workflows, runChild, retryFailed } = request;
5
+ const { record, store, emitter, workflows, runChild, retryFailed, assertActive } = request;
6
+ assertActive();
6
7
  const childId = record.childExecutionId;
7
8
  if (!childId)
8
9
  throw new Error('sub-workflow record carries no child execution id');
@@ -29,6 +30,7 @@ export async function unwindChild(request) {
29
30
  emitter,
30
31
  workflows,
31
32
  retryFailed,
33
+ assertActive,
32
34
  });
33
35
  if (outcome !== 'ran') {
34
36
  throw new Error(`child "${child.workflowName}" (${childId}) is being rolled back by another driver; ` +
@@ -12,5 +12,6 @@ export interface UnwindPass {
12
12
  eligible: [string, StepRecord][];
13
13
  workflows?: Map<string, Workflow>;
14
14
  retryFailed?: boolean;
15
+ assertActive: () => void;
15
16
  }
16
17
  export declare function unwind(pass: UnwindPass, runChild: ChildUnwind): Promise<void>;
@@ -1,14 +1,16 @@
1
1
  /** One checkpointed compensation pass. */
2
2
  import { compensationContext, settle } from './compensationSupport';
3
3
  import { unwindChild } from './compensationChild';
4
+ import { isWorkflowExecutionClosed } from './executionFence';
4
5
  import { describeError } from './identity';
5
6
  import { buildContext, findStepDef, runWithTimeout } from './runner';
6
7
  import { decideUnwindAction } from './unwindPlan';
7
8
  export async function unwind(pass, runChild) {
8
- const { exec, wf, store, emitter, eligible, workflows, retryFailed } = pass;
9
+ const { exec, wf, store, emitter, eligible, workflows, retryFailed, assertActive } = pass;
9
10
  const baseCtx = buildContext(exec);
10
11
  let haltedAt = null;
11
12
  let writeFailure;
13
+ assertActive();
12
14
  exec.state = 'compensating';
13
15
  try {
14
16
  store.update(exec);
@@ -20,8 +22,10 @@ export async function unwind(pass, runChild) {
20
22
  }
21
23
  if (writeFailure === undefined) {
22
24
  emitter?.emitWorkflow('workflow:compensating', exec.id, exec.workflowName, 'compensating');
25
+ assertActive();
23
26
  }
24
27
  for (const [name, record] of eligible) {
28
+ assertActive();
25
29
  if (writeFailure !== undefined)
26
30
  break;
27
31
  const action = decideUnwindAction(wf, name, record, haltedAt !== null, retryFailed);
@@ -39,12 +43,22 @@ export async function unwind(pass, runChild) {
39
43
  emitter?.emitStep('compensation:failed', exec.id, exec.workflowName, name, {
40
44
  error: 'step no longer declared',
41
45
  });
46
+ assertActive();
42
47
  continue;
43
48
  }
44
49
  emitter?.emitStep('compensation:started', exec.id, exec.workflowName, name);
50
+ assertActive();
45
51
  try {
46
52
  if (action.kind === 'unwind-child') {
47
- await unwindChild({ record, store, emitter, workflows, runChild, retryFailed });
53
+ await unwindChild({
54
+ record,
55
+ store,
56
+ emitter,
57
+ workflows,
58
+ runChild,
59
+ retryFailed,
60
+ assertActive,
61
+ });
48
62
  }
49
63
  else {
50
64
  const def = findStepDef(wf, name);
@@ -59,6 +73,8 @@ export async function unwind(pass, runChild) {
59
73
  emitter?.emitStep('compensation:completed', exec.id, exec.workflowName, name);
60
74
  }
61
75
  catch (error) {
76
+ if (isWorkflowExecutionClosed(error))
77
+ throw error;
62
78
  const message = describeError(error);
63
79
  settle(record, 'compensation-failed', message);
64
80
  emitter?.emitStep('compensation:failed', exec.id, exec.workflowName, name, {
@@ -25,9 +25,10 @@ export type UnwindOutcome = 'ran' | LostCompensationClaim;
25
25
  /** Run compensation handlers in reverse start order for every eligible step. */
26
26
  export declare function runCompensation(exec: Execution, wf: Workflow, store: WorkflowStore, emitter: WorkflowEmitter | null, workflows?: Map<string, Workflow>, opts?: {
27
27
  retryFailed?: boolean;
28
+ assertActive?: () => void;
28
29
  }): Promise<UnwindOutcome>;
29
30
  /**
30
31
  * Give up on a parked unwind and record an explicit skipped outcome for every
31
32
  * remaining eligible step.
32
33
  */
33
- export declare function abandonCompensation(exec: Execution, wf: Workflow, store: WorkflowStore, emitter: WorkflowEmitter | null): void;
34
+ export declare function abandonCompensation(exec: Execution, wf: Workflow, store: WorkflowStore, emitter: WorkflowEmitter | null, assertActive?: () => void): void;
@@ -9,6 +9,7 @@ import { unwind } from './compensationPass';
9
9
  import { AbandonedCompensationError } from './compensationChild';
10
10
  import { acquireCompensationClaim } from './compensationClaim';
11
11
  import { settle, unwindSet } from './compensationSupport';
12
+ import { assertWorkflowActive } from './executionFence';
12
13
  import { owesOutcome } from './unwindPlan';
13
14
  export { AbandonedCompensationError, CommittedChildError } from './compensationChild';
14
15
  /** Sentinel thrown when an execution deliberately parks for a signal. */
@@ -20,8 +21,10 @@ export class WaitForSignalError extends Error {
20
21
  }
21
22
  }
22
23
  /** Run compensation handlers in reverse start order for every eligible step. */
23
- // biome-ignore lint/complexity/useMaxParams: registry and operator retry are independent inputs
24
+ // oxlint-disable-next-line max-params -- registry and operator retry are independent inputs
24
25
  export async function runCompensation(exec, wf, store, emitter, workflows, opts = {}) {
26
+ const assertActive = opts.assertActive ?? assertWorkflowActive;
27
+ assertActive();
25
28
  // `failed` + `stuck` is the durable terminal form written by an explicit
26
29
  // abandon. No caller, including an ancestor retry, may reopen that decision.
27
30
  if (exec.state === 'failed' && exec.rollbackStatus === 'stuck') {
@@ -29,17 +32,19 @@ export async function runCompensation(exec, wf, store, emitter, workflows, opts
29
32
  }
30
33
  const eligible = unwindSet(exec, wf);
31
34
  if (eligible.length === 0) {
35
+ assertActive();
32
36
  exec.rollbackStatus = 'not-applicable';
33
37
  if (exec.state !== 'failed' && exec.state !== 'completed')
34
38
  exec.state = 'failed';
35
39
  store.update(exec);
36
40
  return 'ran';
37
41
  }
42
+ assertActive();
38
43
  const claim = acquireCompensationClaim(exec.id, store);
39
44
  if (claim.kind === 'claim-lost')
40
45
  return claim;
41
46
  try {
42
- await unwind({ exec, wf, store, emitter, eligible, workflows, ...opts }, ({ exec, workflow, store, emitter, workflows, retryFailed }) => runCompensation(exec, workflow, store, emitter, workflows, { retryFailed }));
47
+ await unwind({ exec, wf, store, emitter, eligible, workflows, ...opts, assertActive }, ({ exec, workflow, store, emitter, workflows, retryFailed, assertActive }) => runCompensation(exec, workflow, store, emitter, workflows, { retryFailed, assertActive }));
43
48
  }
44
49
  finally {
45
50
  claim.release();
@@ -50,8 +55,9 @@ export async function runCompensation(exec, wf, store, emitter, workflows, opts
50
55
  * Give up on a parked unwind and record an explicit skipped outcome for every
51
56
  * remaining eligible step.
52
57
  */
53
- export function abandonCompensation(exec, wf, store, emitter) {
58
+ export function abandonCompensation(exec, wf, store, emitter, assertActive = assertWorkflowActive) {
54
59
  for (const [name, record] of unwindSet(exec, wf)) {
60
+ assertActive();
55
61
  if (record.compensation)
56
62
  continue;
57
63
  if (!owesOutcome(wf, name, record))
@@ -59,7 +65,9 @@ export function abandonCompensation(exec, wf, store, emitter) {
59
65
  const reason = 'unwind abandoned by operator';
60
66
  settle(record, 'compensation-skipped', reason);
61
67
  emitter?.emitStep('compensation:skipped', exec.id, exec.workflowName, name, { error: reason });
68
+ assertActive();
62
69
  }
70
+ assertActive();
63
71
  exec.state = 'failed';
64
72
  exec.rollbackStatus = 'stuck';
65
73
  store.update(exec);
@@ -136,7 +136,7 @@ export class Engine {
136
136
  // Before the worker: a waitFor timer that fires during shutdown enqueues a step
137
137
  // job, and a queue closing underneath it turns an orderly shutdown into a rejected
138
138
  // add. Releasing the timers first makes the order irrelevant.
139
- this.executor.close();
139
+ this.executor.close(force);
140
140
  await this.worker.close(force);
141
141
  this.queue.close();
142
142
  this.store.close();
@@ -0,0 +1,15 @@
1
+ /** Lifecycle fence shared by every asynchronous workflow execution path. */
2
+ /** Internal control-flow signal used to abandon work owned by a closed Engine. */
3
+ export declare class WorkflowExecutionClosedError extends Error {
4
+ constructor();
5
+ }
6
+ export declare function isWorkflowExecutionClosed(error: unknown): boolean;
7
+ /** A graceful close leaves this active; only close(true) aborts in-flight control flow. */
8
+ export declare class WorkflowExecutionFence {
9
+ private readonly controller;
10
+ readonly assertActive: () => void;
11
+ isActive(): boolean;
12
+ close(force: boolean): void;
13
+ }
14
+ /** Default for isolated runner tests that do not own an Engine lifecycle. */
15
+ export declare const assertWorkflowActive: () => void;
@@ -0,0 +1,29 @@
1
+ /** Lifecycle fence shared by every asynchronous workflow execution path. */
2
+ /** Internal control-flow signal used to abandon work owned by a closed Engine. */
3
+ export class WorkflowExecutionClosedError extends Error {
4
+ constructor() {
5
+ super('Workflow execution belongs to a force-closed Engine');
6
+ this.name = 'WorkflowExecutionClosedError';
7
+ }
8
+ }
9
+ export function isWorkflowExecutionClosed(error) {
10
+ return (error instanceof WorkflowExecutionClosedError ||
11
+ (error instanceof AggregateError && error.errors.some(isWorkflowExecutionClosed)));
12
+ }
13
+ /** A graceful close leaves this active; only close(true) aborts in-flight control flow. */
14
+ export class WorkflowExecutionFence {
15
+ controller = new AbortController();
16
+ assertActive = () => {
17
+ if (this.controller.signal.aborted)
18
+ throw new WorkflowExecutionClosedError();
19
+ };
20
+ isActive() {
21
+ return !this.controller.signal.aborted;
22
+ }
23
+ close(force) {
24
+ if (force)
25
+ this.controller.abort();
26
+ }
27
+ }
28
+ /** Default for isolated runner tests that do not own an Engine lifecycle. */
29
+ export const assertWorkflowActive = () => undefined;
@@ -10,13 +10,14 @@ export declare class WorkflowExecutor {
10
10
  private readonly emitter;
11
11
  private readonly workflows;
12
12
  private readonly timeoutTimers;
13
+ private readonly fence;
13
14
  /**
14
15
  * Release every armed waitFor timer. The engine owns the executor's lifetime, so
15
16
  * `Engine.close()` must call this: an armed timer would otherwise fire into a
16
17
  * closing queue, and a caller that keeps the process alive after closing one engine
17
18
  * has no other handle on them.
18
19
  */
19
- close(): void;
20
+ close(force?: boolean): void;
20
21
  private readonly updateFn;
21
22
  constructor(store: WorkflowStore, queue: Queue, emitter?: WorkflowEmitter | null);
22
23
  register(workflow: Workflow): void;
@@ -24,16 +25,9 @@ export declare class WorkflowExecutor {
24
25
  /**
25
26
  * Nodes this process is currently executing, keyed `<execution>:<nodeIndex>`.
26
27
  *
27
- * The cursor guard below rejects a job for a node the run has already left, but not
28
- * a SECOND job for the node it is on right now: both carry the same index. That is
29
- * the reachable duplicate, because `recover()` re-enqueues the current node of every
30
- * `running` execution and is documented as callable on a live engine. Without this
31
- * claim the node runs twice and each copy advances the run independently, doubling
32
- * every side effect after it while the run still ends `completed`.
33
- *
34
- * A claim, not a queue-level dedup: a deterministic `jobId` was tried and could
35
- * swallow a LEGITIMATE later re-enqueue of the same node, wedging the run forever.
36
- * This drops only a duplicate that overlaps in time.
28
+ * The cursor rejects stale jobs but not two overlapping deliveries for the current
29
+ * node. This process-local claim drops that overlap. Queue-level dedup is unsuitable:
30
+ * a retained ID can swallow a legitimate recovery enqueue and wedge the run.
37
31
  */
38
32
  private readonly nodesInFlight;
39
33
  processStep(data: StepJobData): Promise<unknown>;