bunqueue 2.8.60 → 2.9.2

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 (427) hide show
  1. package/README.md +114 -78
  2. package/dist/application/background/dlq.js +2 -0
  3. package/dist/application/background/recovery/active.d.ts +1 -0
  4. package/dist/application/background/recovery/active.js +12 -0
  5. package/dist/application/background/recovery/index.js +3 -1
  6. package/dist/application/background/recovery/pending.js +1 -0
  7. package/dist/application/cleanupTasks.js +4 -16
  8. package/dist/application/contextFactory.js +8 -0
  9. package/dist/application/dlqManager.d.ts +10 -0
  10. package/dist/application/dlqManager.js +48 -9
  11. package/dist/application/dlqRetry.js +60 -38
  12. package/dist/application/eventsManager.d.ts +9 -2
  13. package/dist/application/eventsManager.js +106 -55
  14. package/dist/application/operations/ackHelpers.d.ts +1 -4
  15. package/dist/application/operations/ackHelpers.js +39 -14
  16. package/dist/application/operations/flowPush.js +1 -0
  17. package/dist/application/operations/flowValidation.js +4 -4
  18. package/dist/application/operations/jobManagement.d.ts +4 -0
  19. package/dist/application/operations/jobMoveOperations.js +9 -4
  20. package/dist/application/operations/pull.d.ts +7 -2
  21. package/dist/application/operations/pull.js +15 -57
  22. package/dist/application/operations/pullFinalization.d.ts +6 -0
  23. package/dist/application/operations/pullFinalization.js +69 -0
  24. package/dist/application/operations/pullStateTransition.d.ts +8 -7
  25. package/dist/application/operations/pullStateTransition.js +25 -18
  26. package/dist/application/operations/push.js +3 -0
  27. package/dist/application/operations/pushBatch.js +14 -7
  28. package/dist/application/operations/pushContext.d.ts +2 -1
  29. package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
  30. package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
  31. package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
  32. package/dist/application/postgres-queue-manager/cloud.js +55 -0
  33. package/dist/application/postgres-queue-manager/config.d.ts +5 -0
  34. package/dist/application/postgres-queue-manager/config.js +1 -0
  35. package/dist/application/postgres-queue-manager/control.d.ts +30 -0
  36. package/dist/application/postgres-queue-manager/control.js +210 -0
  37. package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
  38. package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
  39. package/dist/application/postgres-queue-manager/delivery.d.ts +20 -0
  40. package/dist/application/postgres-queue-manager/delivery.js +83 -0
  41. package/dist/application/postgres-queue-manager/groupQueries.d.ts +10 -0
  42. package/dist/application/postgres-queue-manager/groupQueries.js +40 -0
  43. package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
  44. package/dist/application/postgres-queue-manager/lease.js +88 -0
  45. package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
  46. package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
  47. package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
  48. package/dist/application/postgres-queue-manager/maintenance.js +100 -0
  49. package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
  50. package/dist/application/postgres-queue-manager/operationGate.js +96 -0
  51. package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
  52. package/dist/application/postgres-queue-manager/operations.js +194 -0
  53. package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
  54. package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
  55. package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
  56. package/dist/application/postgres-queue-manager/queries.js +231 -0
  57. package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
  58. package/dist/application/postgres-queue-manager/relationships.js +45 -0
  59. package/dist/application/postgres-queue-manager/services.d.ts +31 -0
  60. package/dist/application/postgres-queue-manager/services.js +149 -0
  61. package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
  62. package/dist/application/postgres-queue-manager/snapshot.js +236 -0
  63. package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
  64. package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
  65. package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
  66. package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
  67. package/dist/application/postgres-queue-manager/state.d.ts +47 -0
  68. package/dist/application/postgres-queue-manager/state.js +273 -0
  69. package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
  70. package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
  71. package/dist/application/postgresQueueManager.d.ts +2 -0
  72. package/dist/application/postgresQueueManager.js +1 -0
  73. package/dist/application/queue-manager/control.js +1 -0
  74. package/dist/application/queue-manager/delivery.d.ts +5 -4
  75. package/dist/application/queue-manager/delivery.js +15 -8
  76. package/dist/application/queue-manager/flow-failures.js +4 -0
  77. package/dist/application/queue-manager/limits.d.ts +13 -0
  78. package/dist/application/queue-manager/limits.js +65 -0
  79. package/dist/application/queue-manager/observability.js +15 -3
  80. package/dist/application/queue-manager/queries.d.ts +7 -0
  81. package/dist/application/queue-manager/queries.js +9 -0
  82. package/dist/application/queue-manager/services.d.ts +1 -1
  83. package/dist/application/queue-manager/services.js +4 -2
  84. package/dist/application/queue-manager/state.js +17 -2
  85. package/dist/application/queueTelemetryJournal.d.ts +2 -0
  86. package/dist/application/queueTelemetryJournal.js +23 -0
  87. package/dist/application/types/ack.d.ts +3 -1
  88. package/dist/cli/commandRouter.d.ts +1 -1
  89. package/dist/cli/commands/server.js +1 -1
  90. package/dist/client/flowPush.js +6 -0
  91. package/dist/client/forwarder.d.ts +6 -4
  92. package/dist/client/forwarder.js +5 -3
  93. package/dist/client/groupId.d.ts +1 -0
  94. package/dist/client/groupId.js +1 -0
  95. package/dist/client/index.d.ts +1 -1
  96. package/dist/client/jobHelpers.js +1 -0
  97. package/dist/client/queue/dlq.d.ts +2 -0
  98. package/dist/client/queue/dlq.js +14 -0
  99. package/dist/client/queue/dlqOps.d.ts +2 -0
  100. package/dist/client/queue/dlqOps.js +4 -0
  101. package/dist/client/queue/helpers.js +2 -0
  102. package/dist/client/queue/operations/add/bulk.js +4 -4
  103. package/dist/client/queue/operations/add/payload.d.ts +1 -0
  104. package/dist/client/queue/operations/add/payload.js +7 -1
  105. package/dist/client/queue/operations/add/single.js +3 -3
  106. package/dist/client/queue/operations/groups.d.ts +21 -0
  107. package/dist/client/queue/operations/groups.js +88 -0
  108. package/dist/client/queue/runtime/configuration.d.ts +4 -0
  109. package/dist/client/queue/runtime/configuration.js +8 -0
  110. package/dist/client/queue/runtime/queries.d.ts +13 -0
  111. package/dist/client/queue/runtime/queries.js +31 -0
  112. package/dist/client/queue/runtime/state.d.ts +4 -0
  113. package/dist/client/queue/runtime/state.js +1 -0
  114. package/dist/client/types/index.d.ts +2 -2
  115. package/dist/client/types/options.d.ts +4 -0
  116. package/dist/client/types/worker.d.ts +8 -0
  117. package/dist/client/worker/runtime/execution.js +4 -8
  118. package/dist/client/worker/runtime/options.js +3 -0
  119. package/dist/client/worker/runtime/polling.d.ts +1 -0
  120. package/dist/client/worker/runtime/polling.js +9 -0
  121. package/dist/client/worker/types/options.d.ts +2 -1
  122. package/dist/client/worker/workerPull.d.ts +2 -0
  123. package/dist/client/worker/workerPull.js +6 -4
  124. package/dist/config/resolve.d.ts +14 -0
  125. package/dist/config/resolve.js +50 -5
  126. package/dist/config/types.d.ts +18 -0
  127. package/dist/domain/job/groupFifoOrder.d.ts +9 -0
  128. package/dist/domain/job/groupFifoOrder.js +26 -0
  129. package/dist/domain/queue/dlqShard.d.ts +2 -0
  130. package/dist/domain/queue/dlqShard.js +16 -0
  131. package/dist/domain/queue/groupLimiterManager.d.ts +23 -0
  132. package/dist/domain/queue/groupLimiterManager.js +107 -0
  133. package/dist/domain/queue/groupScheduler.d.ts +31 -0
  134. package/dist/domain/queue/groupScheduler.js +256 -0
  135. package/dist/domain/queue/groupSchedulerOrder.d.ts +6 -0
  136. package/dist/domain/queue/groupSchedulerOrder.js +30 -0
  137. package/dist/domain/queue/priorityQueue.d.ts +13 -23
  138. package/dist/domain/queue/priorityQueue.js +53 -42
  139. package/dist/domain/queue/shard/dlq.d.ts +1 -0
  140. package/dist/domain/queue/shard/dlq.js +3 -0
  141. package/dist/domain/queue/shard/keys.d.ts +1 -0
  142. package/dist/domain/queue/shard/keys.js +25 -2
  143. package/dist/domain/queue/shard/lifecycle.js +4 -0
  144. package/dist/domain/queue/shard/limits.d.ts +20 -1
  145. package/dist/domain/queue/shard/limits.js +56 -0
  146. package/dist/domain/queue/shard/state.d.ts +5 -0
  147. package/dist/domain/queue/shard/state.js +9 -1
  148. package/dist/domain/types/commands/core.d.ts +3 -0
  149. package/dist/domain/types/commands/dlq.d.ts +5 -0
  150. package/dist/domain/types/commands/limits.d.ts +41 -0
  151. package/dist/domain/types/commands/union.d.ts +1 -1
  152. package/dist/domain/types/cron.d.ts +5 -0
  153. package/dist/domain/types/cron.js +14 -3
  154. package/dist/domain/types/dlq.d.ts +1 -1
  155. package/dist/domain/types/dlq.js +1 -2
  156. package/dist/domain/types/group.d.ts +21 -0
  157. package/dist/domain/types/group.js +78 -0
  158. package/dist/domain/types/jobs/model.d.ts +1 -1
  159. package/dist/domain/types/priorityQueue.d.ts +1 -0
  160. package/dist/infrastructure/cloud/commandHandler.js +5 -3
  161. package/dist/infrastructure/cloud/commands/integrations.js +11 -11
  162. package/dist/infrastructure/cloud/commands/jobs.js +48 -40
  163. package/dist/infrastructure/cloud/commands/queues.js +61 -75
  164. package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
  165. package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
  166. package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
  167. package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
  168. package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
  169. package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
  170. package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
  171. package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
  172. package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
  173. package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
  174. package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
  175. package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
  176. package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
  177. package/dist/infrastructure/cloud/snapshot/config.js +43 -0
  178. package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
  179. package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
  180. package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
  181. package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
  182. package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
  183. package/dist/infrastructure/cloud/statsRefresh.js +2 -1
  184. package/dist/infrastructure/cloud/types/command.d.ts +2 -2
  185. package/dist/infrastructure/persistence/jobOptionsBlob.d.ts +1 -0
  186. package/dist/infrastructure/persistence/jobOptionsBlob.js +10 -2
  187. package/dist/infrastructure/persistence/migrations.js +11 -0
  188. package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
  189. package/dist/infrastructure/persistence/postgres/admission.js +196 -0
  190. package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
  191. package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
  192. package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
  193. package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
  194. package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
  195. package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
  196. package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
  197. package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
  198. package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
  199. package/dist/infrastructure/persistence/postgres/batchAdmission.js +128 -0
  200. package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
  201. package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
  202. package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
  203. package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
  204. package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
  205. package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
  206. package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +14 -0
  207. package/dist/infrastructure/persistence/postgres/claimBatch.js +69 -0
  208. package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +11 -0
  209. package/dist/infrastructure/persistence/postgres/claimSelection.js +140 -0
  210. package/dist/infrastructure/persistence/postgres/claims.d.ts +10 -0
  211. package/dist/infrastructure/persistence/postgres/claims.js +153 -0
  212. package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
  213. package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
  214. package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
  215. package/dist/infrastructure/persistence/postgres/codec.js +82 -0
  216. package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
  217. package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
  218. package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
  219. package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
  220. package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
  221. package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
  222. package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
  223. package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
  224. package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
  225. package/dist/infrastructure/persistence/postgres/connection.js +15 -0
  226. package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
  227. package/dist/infrastructure/persistence/postgres/context.js +74 -0
  228. package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
  229. package/dist/infrastructure/persistence/postgres/control.js +105 -0
  230. package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
  231. package/dist/infrastructure/persistence/postgres/crons.js +205 -0
  232. package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
  233. package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
  234. package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
  235. package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
  236. package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
  237. package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
  238. package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
  239. package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
  240. package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
  241. package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
  242. package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
  243. package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
  244. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
  245. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
  246. package/dist/infrastructure/persistence/postgres/eventCatchupCursors.d.ts +32 -0
  247. package/dist/infrastructure/persistence/postgres/eventCatchupCursors.js +66 -0
  248. package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
  249. package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
  250. package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
  251. package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
  252. package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
  253. package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +267 -0
  254. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +26 -0
  255. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +129 -0
  256. package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
  257. package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
  258. package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
  259. package/dist/infrastructure/persistence/postgres/events.js +276 -0
  260. package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
  261. package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
  262. package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
  263. package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
  264. package/dist/infrastructure/persistence/postgres/groupClaims.d.ts +15 -0
  265. package/dist/infrastructure/persistence/postgres/groupClaims.js +95 -0
  266. package/dist/infrastructure/persistence/postgres/groupSchema.d.ts +2 -0
  267. package/dist/infrastructure/persistence/postgres/groupSchema.js +155 -0
  268. package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.d.ts +3 -0
  269. package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.js +127 -0
  270. package/dist/infrastructure/persistence/postgres/groupStateRetention.d.ts +3 -0
  271. package/dist/infrastructure/persistence/postgres/groupStateRetention.js +93 -0
  272. package/dist/infrastructure/persistence/postgres/groups.d.ts +11 -0
  273. package/dist/infrastructure/persistence/postgres/groups.js +117 -0
  274. package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
  275. package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
  276. package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
  277. package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
  278. package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
  279. package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
  280. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
  281. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
  282. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
  283. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
  284. package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
  285. package/dist/infrastructure/persistence/postgres/logs.js +70 -0
  286. package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
  287. package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
  288. package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
  289. package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
  290. package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
  291. package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
  292. package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
  293. package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
  294. package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
  295. package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
  296. package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
  297. package/dist/infrastructure/persistence/postgres/policies.js +18 -0
  298. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
  299. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
  300. package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
  301. package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
  302. package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
  303. package/dist/infrastructure/persistence/postgres/queries.js +152 -0
  304. package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
  305. package/dist/infrastructure/persistence/postgres/queueDestruction.js +158 -0
  306. package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
  307. package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
  308. package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
  309. package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
  310. package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
  311. package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
  312. package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
  313. package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
  314. package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
  315. package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
  316. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
  317. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
  318. package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
  319. package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
  320. package/dist/infrastructure/persistence/postgres/runtime.d.ts +45 -0
  321. package/dist/infrastructure/persistence/postgres/runtime.js +238 -0
  322. package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
  323. package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
  324. package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
  325. package/dist/infrastructure/persistence/postgres/schema.js +261 -0
  326. package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
  327. package/dist/infrastructure/persistence/postgres/schemaInitialization.js +226 -0
  328. package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
  329. package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
  330. package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
  331. package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
  332. package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
  333. package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
  334. package/dist/infrastructure/persistence/postgres/store.d.ts +100 -0
  335. package/dist/infrastructure/persistence/postgres/store.js +180 -0
  336. package/dist/infrastructure/persistence/postgres/telemetry.d.ts +8 -0
  337. package/dist/infrastructure/persistence/postgres/telemetry.js +100 -0
  338. package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
  339. package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
  340. package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
  341. package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
  342. package/dist/infrastructure/persistence/postgres/types.d.ts +176 -0
  343. package/dist/infrastructure/persistence/postgres/types.js +1 -0
  344. package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
  345. package/dist/infrastructure/persistence/postgres/workers.js +126 -0
  346. package/dist/infrastructure/persistence/postgres.d.ts +2 -0
  347. package/dist/infrastructure/persistence/postgres.js +1 -0
  348. package/dist/infrastructure/persistence/schema.d.ts +2 -2
  349. package/dist/infrastructure/persistence/schema.js +12 -1
  350. package/dist/infrastructure/persistence/sqlite/control.d.ts +14 -0
  351. package/dist/infrastructure/persistence/sqlite/control.js +69 -0
  352. package/dist/infrastructure/persistence/sqlite/jobs.d.ts +12 -0
  353. package/dist/infrastructure/persistence/sqlite/jobs.js +38 -0
  354. package/dist/infrastructure/persistence/sqlite/telemetry.d.ts +2 -1
  355. package/dist/infrastructure/persistence/sqlite/telemetry.js +5 -64
  356. package/dist/infrastructure/persistence/sqlite/telemetryWrites.d.ts +4 -0
  357. package/dist/infrastructure/persistence/sqlite/telemetryWrites.js +123 -0
  358. package/dist/infrastructure/persistence/sqliteSerializer.js +2 -0
  359. package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
  360. package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
  361. package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
  362. package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
  363. package/dist/infrastructure/scheduler/cron/validation.js +11 -0
  364. package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
  365. package/dist/infrastructure/scheduler/cronParser.js +144 -11
  366. package/dist/infrastructure/server/bootstrap.d.ts +2 -3
  367. package/dist/infrastructure/server/bootstrap.js +33 -59
  368. package/dist/infrastructure/server/errors.d.ts +4 -0
  369. package/dist/infrastructure/server/errors.js +54 -0
  370. package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
  371. package/dist/infrastructure/server/handler-routes/control.js +16 -1
  372. package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
  373. package/dist/infrastructure/server/handler.js +9 -11
  374. package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
  375. package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
  376. package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
  377. package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
  378. package/dist/infrastructure/server/handlers/core.d.ts +0 -5
  379. package/dist/infrastructure/server/handlers/core.js +47 -49
  380. package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
  381. package/dist/infrastructure/server/handlers/cron.js +60 -59
  382. package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
  383. package/dist/infrastructure/server/handlers/dashboard.js +21 -8
  384. package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
  385. package/dist/infrastructure/server/handlers/dlq.js +35 -9
  386. package/dist/infrastructure/server/handlers/flow.js +2 -1
  387. package/dist/infrastructure/server/handlers/groups.d.ts +11 -0
  388. package/dist/infrastructure/server/handlers/groups.js +55 -0
  389. package/dist/infrastructure/server/handlers/management.d.ts +5 -5
  390. package/dist/infrastructure/server/handlers/management.js +36 -9
  391. package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
  392. package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
  393. package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
  394. package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
  395. package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
  396. package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
  397. package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
  398. package/dist/infrastructure/server/handlers/pushBatchValidation.js +56 -8
  399. package/dist/infrastructure/server/handlers/query.d.ts +1 -1
  400. package/dist/infrastructure/server/handlers/query.js +5 -4
  401. package/dist/infrastructure/server/http.js +2 -2
  402. package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
  403. package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
  404. package/dist/infrastructure/server/httpEndpoints.js +18 -7
  405. package/dist/infrastructure/server/httpRouteQueues.js +11 -1
  406. package/dist/infrastructure/server/protocol/validation.d.ts +1 -0
  407. package/dist/infrastructure/server/protocol/validation.js +1 -0
  408. package/dist/infrastructure/server/protocol.d.ts +1 -1
  409. package/dist/infrastructure/server/protocol.js +1 -1
  410. package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
  411. package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
  412. package/dist/infrastructure/server/sseHandler.js +8 -3
  413. package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
  414. package/dist/infrastructure/server/storageAdapter.js +61 -0
  415. package/dist/infrastructure/server/storageManager.d.ts +12 -0
  416. package/dist/infrastructure/server/storageManager.js +102 -0
  417. package/dist/infrastructure/server/tcp.js +2 -3
  418. package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
  419. package/dist/infrastructure/server/ws/snapshots.js +19 -4
  420. package/dist/infrastructure/server/wsHandler.js +10 -5
  421. package/dist/main.js +1 -1
  422. package/dist/mcp/backend/embedded/services.js +2 -1
  423. package/dist/mcp/index.d.ts +2 -2
  424. package/dist/mcp/index.js +2 -2
  425. package/dist/shared/storageHealth.d.ts +9 -0
  426. package/dist/shared/storageHealth.js +12 -0
  427. package/package.json +75 -65
@@ -0,0 +1,5 @@
1
+ import { type CronJob } from '../../../domain/types/cron';
2
+ /** Reject persisted Croner-only syntax before any scheduler state is mutated. */
3
+ export declare function assertPersistedCronSupported(cron: CronJob): void;
4
+ /** Validate a persisted collection atomically before loading any definition. */
5
+ export declare function assertPersistedCronsSupported(crons: readonly CronJob[]): void;
@@ -0,0 +1,27 @@
1
+ import { assertValidCronTiming } from '../../../domain/types/cron';
2
+ import { expandCronShortcut, validateCronExpression } from '../cronParser';
3
+ const NATIVE_CRON_MIGRATION_VERSION = '2.9.0';
4
+ /** Reject persisted Croner-only syntax before any scheduler state is mutated. */
5
+ export function assertPersistedCronSupported(cron) {
6
+ try {
7
+ assertValidCronTiming(cron);
8
+ }
9
+ catch (error) {
10
+ const message = error instanceof Error ? error.message : String(error);
11
+ throw new Error(`Persisted cron ${JSON.stringify(cron.name)} has invalid timing: ${message}. ` +
12
+ `Update or remove it before upgrading to bunqueue ${NATIVE_CRON_MIGRATION_VERSION}.`);
13
+ }
14
+ if (!cron.schedule)
15
+ return;
16
+ const error = validateCronExpression(expandCronShortcut(cron.schedule), cron.timezone ?? undefined);
17
+ if (!error)
18
+ return;
19
+ throw new Error(`Persisted cron ${JSON.stringify(cron.name)} uses unsupported schedule ${JSON.stringify(cron.schedule)}. ` +
20
+ `Update or remove it before upgrading to bunqueue ${NATIVE_CRON_MIGRATION_VERSION}. ` +
21
+ `Bun.cron validation failed: ${error}`);
22
+ }
23
+ /** Validate a persisted collection atomically before loading any definition. */
24
+ export function assertPersistedCronsSupported(crons) {
25
+ for (const cron of crons)
26
+ assertPersistedCronSupported(cron);
27
+ }
@@ -1,6 +1,8 @@
1
1
  import { createCronJob } from '../../../domain/types/cron';
2
2
  import { MinHeap } from '../../../shared/minHeap';
3
- import { expandCronShortcut, getNextCronRun, getNextIntervalRun, validateCronExpression, } from '../cronParser';
3
+ import { expandCronShortcut, getNextCronRun, getNextIntervalRun } from '../cronParser';
4
+ import { assertPersistedCronsSupported } from './persisted';
5
+ import { assertValidCronInput } from './validation';
4
6
  const SAFETY_FALLBACK_MS = 60_000;
5
7
  const MAX_TIMER_DELAY_MS = 2_147_483_647;
6
8
  /** Registry, timers, and schedule mutation shared with cron execution. */
@@ -52,15 +54,7 @@ export class CronRuntime {
52
54
  }
53
55
  }
54
56
  add(input) {
55
- if (!input.schedule && !input.repeatEvery) {
56
- throw new Error('Cron job must have either schedule or repeatEvery');
57
- }
58
- if (input.schedule) {
59
- const expanded = expandCronShortcut(input.schedule);
60
- const error = validateCronExpression(expanded, input.timezone);
61
- if (error)
62
- throw new Error(`Invalid cron expression: ${error}`);
63
- }
57
+ assertValidCronInput(input);
64
58
  const now = Date.now();
65
59
  const nextRun = input.schedule
66
60
  ? getNextCronRun(expandCronShortcut(input.schedule), now, input.timezone)
@@ -97,6 +91,7 @@ export class CronRuntime {
97
91
  return Array.from(this.cronJobs.values()).map((entry) => entry.cron);
98
92
  }
99
93
  load(crons) {
94
+ assertPersistedCronsSupported(crons);
100
95
  const now = Date.now();
101
96
  const entries = [];
102
97
  for (const cron of crons) {
@@ -0,0 +1,3 @@
1
+ import { type CronJobInput } from '../../../domain/types/cron';
2
+ /** Reject invalid timing, calendar syntax, and timezones before state mutation. */
3
+ export declare function assertValidCronInput(input: CronJobInput): void;
@@ -0,0 +1,11 @@
1
+ import { assertValidCronTiming } from '../../../domain/types/cron';
2
+ import { expandCronShortcut, validateCronExpression } from '../cronParser';
3
+ /** Reject invalid timing, calendar syntax, and timezones before state mutation. */
4
+ export function assertValidCronInput(input) {
5
+ assertValidCronTiming(input);
6
+ if (!input.schedule)
7
+ return;
8
+ const error = validateCronExpression(expandCronShortcut(input.schedule), input.timezone);
9
+ if (error)
10
+ throw new Error(`Invalid cron expression: ${error}`);
11
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Cron Expression Parser
3
- * Parses 5-6 field cron expressions and calculates next run time
3
+ * Parses 5-6 field cron expressions and calculates next run time with Bun.cron
4
4
  */
5
5
  /**
6
6
  * Validate a cron expression
@@ -1,8 +1,102 @@
1
1
  /**
2
2
  * Cron Expression Parser
3
- * Parses 5-6 field cron expressions and calculates next run time
3
+ * Parses 5-6 field cron expressions and calculates next run time with Bun.cron
4
4
  */
5
- import { Cron } from 'croner';
5
+ const CRON_FIELD_COUNT = 5;
6
+ const CRON_WITH_SECONDS_FIELD_COUNT = 6;
7
+ const MINUTE_MS = 60_000;
8
+ const SECOND_MS = 1_000;
9
+ function invalidSeconds(field, reason) {
10
+ throw new TypeError(`Invalid cron seconds field "${field}": ${reason}`);
11
+ }
12
+ function parseNumber(value, field, label) {
13
+ if (!/^\d+$/.test(value))
14
+ invalidSeconds(field, `${label} must be an integer`);
15
+ return Number(value);
16
+ }
17
+ /** Parse the leading seconds field supported by bunqueue's six-field contract. */
18
+ function parseSeconds(field) {
19
+ const selected = new Set();
20
+ for (const segment of field.split(',')) {
21
+ if (!segment)
22
+ invalidSeconds(field, 'list entries cannot be empty');
23
+ const stepParts = segment.split('/');
24
+ if (stepParts.length > 2 || !stepParts[0] || stepParts[1] === '') {
25
+ invalidSeconds(field, `invalid segment "${segment}"`);
26
+ }
27
+ const base = stepParts[0];
28
+ const hasStep = stepParts.length === 2;
29
+ const step = hasStep ? parseNumber(stepParts[1], field, 'step') : 1;
30
+ if (step < 1 || step > 60)
31
+ invalidSeconds(field, 'step must be between 1 and 60');
32
+ let start;
33
+ let end;
34
+ if (base === '*') {
35
+ start = 0;
36
+ end = 59;
37
+ }
38
+ else if (base.includes('-')) {
39
+ const range = base.split('-');
40
+ if (range.length !== 2 || !range[0] || !range[1]) {
41
+ invalidSeconds(field, `invalid range "${base}"`);
42
+ }
43
+ start = parseNumber(range[0], field, 'range start');
44
+ end = parseNumber(range[1], field, 'range end');
45
+ }
46
+ else {
47
+ if (hasStep) {
48
+ invalidSeconds(field, 'steps require a wildcard or range');
49
+ }
50
+ start = parseNumber(base, field, 'value');
51
+ end = start;
52
+ }
53
+ if (start < 0 || start > 59 || end < 0 || end > 59) {
54
+ invalidSeconds(field, 'values must be between 0 and 59');
55
+ }
56
+ if (start > end)
57
+ invalidSeconds(field, 'range start must not exceed range end');
58
+ for (let value = start; value <= end; value += step)
59
+ selected.add(value);
60
+ }
61
+ return [...selected].sort((a, b) => a - b);
62
+ }
63
+ function parseCronExpression(expression) {
64
+ const trimmed = expression.trim();
65
+ if (trimmed.startsWith('@') && !/\s/.test(trimmed)) {
66
+ return { calendar: trimmed, seconds: null };
67
+ }
68
+ const fields = trimmed ? trimmed.split(/\s+/) : [];
69
+ if (fields.length === CRON_FIELD_COUNT) {
70
+ return { calendar: fields.join(' '), seconds: null };
71
+ }
72
+ if (fields.length === CRON_WITH_SECONDS_FIELD_COUNT) {
73
+ return { calendar: fields.slice(1).join(' '), seconds: parseSeconds(fields[0]) };
74
+ }
75
+ throw new TypeError('Cron expression must have 5 fields, or 6 fields with leading seconds');
76
+ }
77
+ function parseNativeCron(calendar, fromTime, timezone) {
78
+ return Bun.cron.parse(calendar, fromTime, timezone ? { tz: timezone } : undefined);
79
+ }
80
+ function getNextParsedRun(parsed, fromTime, timezone) {
81
+ if (parsed.seconds === null) {
82
+ return parseNativeCron(parsed.calendar, fromTime, timezone)?.getTime() ?? 0;
83
+ }
84
+ const currentMinute = Math.floor(fromTime / MINUTE_MS) * MINUTE_MS;
85
+ let matchingMinute = parseNativeCron(parsed.calendar, currentMinute - 1, timezone);
86
+ while (matchingMinute) {
87
+ const minuteStart = matchingMinute.getTime();
88
+ for (const second of parsed.seconds) {
89
+ const candidate = minuteStart + second * SECOND_MS;
90
+ if (candidate > fromTime)
91
+ return candidate;
92
+ }
93
+ const nextMinute = parseNativeCron(parsed.calendar, minuteStart, timezone);
94
+ if (!nextMinute || nextMinute.getTime() <= minuteStart)
95
+ return 0;
96
+ matchingMinute = nextMinute;
97
+ }
98
+ return 0;
99
+ }
6
100
  /**
7
101
  * Validate a cron expression
8
102
  * @param expression - Cron expression (5 or 6 fields)
@@ -11,7 +105,10 @@ import { Cron } from 'croner';
11
105
  */
12
106
  export function validateCronExpression(expression, timezone) {
13
107
  try {
14
- new Cron(expression, { timezone });
108
+ const parsed = parseCronExpression(expression);
109
+ if (getNextParsedRun(parsed, Date.now(), timezone) === 0) {
110
+ return 'Cron expression has no future occurrences';
111
+ }
15
112
  return null;
16
113
  }
17
114
  catch (err) {
@@ -26,9 +123,7 @@ export function validateCronExpression(expression, timezone) {
26
123
  * @returns Next run timestamp in milliseconds
27
124
  */
28
125
  export function getNextCronRun(expression, fromTime = Date.now(), timezone) {
29
- const cron = new Cron(expression, { timezone });
30
- const nextDate = cron.nextRun(new Date(fromTime));
31
- return nextDate ? nextDate.getTime() : 0;
126
+ return getNextParsedRun(parseCronExpression(expression), fromTime, timezone);
32
127
  }
33
128
  /**
34
129
  * Calculate next run time from repeatEvery interval
@@ -72,20 +167,58 @@ export function expandCronShortcut(expression) {
72
167
  */
73
168
  export function describeCron(expression) {
74
169
  const expanded = expandCronShortcut(expression);
75
- const parts = expanded.split(/\s+/);
76
- if (parts.length < 5) {
170
+ const rawParts = expanded.trim().split(/\s+/);
171
+ const hasSeconds = rawParts.length === CRON_WITH_SECONDS_FIELD_COUNT;
172
+ const secondsField = hasSeconds ? rawParts[0] : null;
173
+ let seconds = null;
174
+ if (secondsField) {
175
+ try {
176
+ seconds = parseSeconds(secondsField);
177
+ }
178
+ catch {
179
+ return 'Invalid cron expression';
180
+ }
181
+ }
182
+ const parts = hasSeconds ? rawParts.slice(1) : rawParts;
183
+ if (parts.length !== CRON_FIELD_COUNT) {
77
184
  return 'Invalid cron expression';
78
185
  }
79
186
  const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
187
+ const everyCalendarMinute = parts.every((part) => part === '*');
188
+ if (secondsField === '*' && everyCalendarMinute)
189
+ return 'Every second';
190
+ if (secondsField &&
191
+ /^\d+$/.test(secondsField) &&
192
+ /^\d+$/.test(minute) &&
193
+ /^\d+$/.test(hour) &&
194
+ dayOfMonth === '*' &&
195
+ month === '*' &&
196
+ dayOfWeek === '*') {
197
+ return `Every day at ${hour.padStart(2, '0')}:${minute.padStart(2, '0')}:${secondsField.padStart(2, '0')}`;
198
+ }
199
+ const usesCalendarShortcuts = seconds === null || (seconds.length === 1 && seconds[0] === 0);
80
200
  // Common patterns
81
- if (minute === '0' && hour === '0' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
201
+ if (usesCalendarShortcuts &&
202
+ minute === '0' &&
203
+ hour === '0' &&
204
+ dayOfMonth === '*' &&
205
+ month === '*' &&
206
+ dayOfWeek === '*') {
82
207
  return 'Every day at midnight';
83
208
  }
84
- if (minute === '0' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
209
+ if (usesCalendarShortcuts &&
210
+ minute === '0' &&
211
+ hour === '*' &&
212
+ dayOfMonth === '*' &&
213
+ month === '*' &&
214
+ dayOfWeek === '*') {
85
215
  return 'Every hour';
86
216
  }
87
- if (minute === '*' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
217
+ if (usesCalendarShortcuts && everyCalendarMinute) {
88
218
  return 'Every minute';
89
219
  }
220
+ if (secondsField) {
221
+ return `At second ${secondsField}, minute ${minute}, hour ${hour} on day ${dayOfMonth} of ${month}, day of week ${dayOfWeek}`;
222
+ }
90
223
  return `At ${minute} ${hour} on day ${dayOfMonth} of ${month}, day of week ${dayOfWeek}`;
91
224
  }
@@ -5,7 +5,6 @@
5
5
  * agent, stats interval, crash handlers and graceful shutdown are always on.
6
6
  */
7
7
  import { type BunqueueConfig, type ResolvedConfig } from '../../config';
8
- /** Return a startup error when an enabled file backup has no file to snapshot. */
9
- export declare function backupStartupError(config: Pick<ResolvedConfig, 's3BackupEnabled' | 'dataPath'>): string | null;
8
+ export { backupStartupError } from './storageManager';
10
9
  /** Boot the full server from resolved configuration. Runs until shutdown. */
11
- export declare function bootServer(fileConfig: BunqueueConfig | null, config: ResolvedConfig): void;
10
+ export declare function bootServer(fileConfig: BunqueueConfig | null, config: ResolvedConfig): Promise<void>;
@@ -4,24 +4,17 @@
4
4
  * `bunqueue start` via the CLI) so they cannot drift: S3 backup, cloud
5
5
  * agent, stats interval, crash handlers and graceful shutdown are always on.
6
6
  */
7
- import { QueueManager } from '../../application/queueManager';
8
7
  import { createTcpServer } from './tcp';
9
8
  import { createHttpServer } from './http';
10
9
  import { Logger, serverLog, statsLog } from '../../shared/logger';
11
- import { stopRateLimiter } from './rateLimiter';
12
10
  import { VERSION } from '../../shared/version';
13
11
  import { S3BackupManager } from '../backup';
14
12
  import { CloudAgent } from '../cloud';
15
13
  import { SHARD_COUNT } from '../../shared/hash';
16
14
  import { resolveCloudConfig, resolveBackupConfig, resolveTlsServerOptions, } from '../../config';
17
- /** Return a startup error when an enabled file backup has no file to snapshot. */
18
- export function backupStartupError(config) {
19
- if (config.s3BackupEnabled && !config.dataPath) {
20
- return ('S3 backup requires persistent SQLite storage. Set BUNQUEUE_DATA_PATH ' +
21
- 'or storage.dataPath before enabling S3_BACKUP_ENABLED.');
22
- }
23
- return null;
24
- }
15
+ import { backupStartupError, createServerQueueManager, shutdownServerQueueManager, storageDisplay, storageStartupError, } from './storageManager';
16
+ import { createServerShutdown } from './shutdownCoordinator';
17
+ export { backupStartupError } from './storageManager';
25
18
  /** Print startup banner */
26
19
  function printBanner(config, cloudUrl) {
27
20
  const dim = '\x1b[2m';
@@ -46,9 +39,7 @@ function printBanner(config, cloudUrl) {
46
39
  const socketDisplay = hasUnixSockets
47
40
  ? `${green}enabled${reset} ${dim}(${config.tcpSocketPath ? 'TCP' : ''}${config.tcpSocketPath && config.httpSocketPath ? '+' : ''}${config.httpSocketPath ? 'HTTP' : ''})${reset}`
48
41
  : `${dim}disabled${reset}`;
49
- const storageDisplay = config.dataPath
50
- ? `${bold}SQLite${reset} ${dim}·${reset} ${config.dataPath}`
51
- : `in-memory ${dim}· ephemeral${reset}`;
42
+ const persistenceDisplay = storageDisplay(config);
52
43
  console.log(`
53
44
  ${magenta} (\\(\\ ${reset}
54
45
  ${magenta} ( -.-) ${bold}bunqueue${reset} ${dim}v${VERSION}${reset}
@@ -59,7 +50,7 @@ ${dim}────────────────────────
59
50
  ${row(active, 'TCP', tcpDisplay)}
60
51
  ${row(active, 'HTTP', httpDisplay)}
61
52
  ${row(hasUnixSockets ? active : inactive, 'Unix socket', socketDisplay)}
62
- ${row(info, 'Storage', storageDisplay)}
53
+ ${row(info, 'Storage', persistenceDisplay)}
63
54
  ${row(config.s3BackupEnabled ? active : inactive, 'S3 Backup', config.s3BackupEnabled ? `${green}enabled${reset}` : `${dim}disabled${reset}`)}
64
55
  ${row(config.tlsCertFile ? active : inactive, 'TLS', config.tlsCertFile ? `${green}enabled${reset}` : `${dim}disabled${reset}`)}
65
56
  ${row(config.authTokens.length > 0 ? active : inactive, 'Auth', config.authTokens.length > 0 ? `${green}enabled${reset}` : `${dim}disabled${reset}`)}
@@ -71,7 +62,7 @@ ${dim}────────────────────────
71
62
  `);
72
63
  }
73
64
  /** Boot the full server from resolved configuration. Runs until shutdown. */
74
- export function bootServer(fileConfig, config) {
65
+ export async function bootServer(fileConfig, config) {
75
66
  // Apply logging config before anything else
76
67
  const logFormat = fileConfig?.logging?.format ?? Bun.env.LOG_FORMAT;
77
68
  const logLevel = fileConfig?.logging?.level ?? Bun.env.LOG_LEVEL?.toLowerCase();
@@ -82,9 +73,9 @@ export function bootServer(fileConfig, config) {
82
73
  if (validLevels.includes(logLevel))
83
74
  Logger.setLevel(logLevel);
84
75
  }
85
- const backupError = backupStartupError(config);
86
- if (backupError) {
87
- serverLog.error(backupError);
76
+ const startupError = storageStartupError(config) ?? backupStartupError(config);
77
+ if (startupError) {
78
+ serverLog.error(startupError);
88
79
  process.exitCode = 1;
89
80
  return;
90
81
  }
@@ -99,12 +90,18 @@ export function bootServer(fileConfig, config) {
99
90
  serverLog.error(err instanceof Error ? err.message : String(err));
100
91
  process.exit(1);
101
92
  }
93
+ let queueManager;
94
+ try {
95
+ queueManager = await createServerQueueManager(config);
96
+ }
97
+ catch (error) {
98
+ serverLog.error('Failed to initialize storage', {
99
+ error: error instanceof Error ? error.message : String(error),
100
+ });
101
+ process.exitCode = 1;
102
+ return;
103
+ }
102
104
  printBanner(config, cloudConfig?.url);
103
- // Create queue manager
104
- const queueManager = new QueueManager({
105
- dataPath: config.dataPath,
106
- maxPrometheusQueues: config.maxPrometheusQueues,
107
- });
108
105
  // Start TCP + HTTP servers; a bind failure must not leave a half-started process
109
106
  let tcpServer;
110
107
  let httpServer;
@@ -129,12 +126,12 @@ export function bootServer(fileConfig, config) {
129
126
  catch (err) {
130
127
  const msg = err instanceof Error ? err.message : 'Unknown error';
131
128
  console.error(`Failed to start server: ${msg}`);
132
- queueManager.shutdown();
129
+ await shutdownServerQueueManager(queueManager);
133
130
  process.exit(1);
134
131
  }
135
132
  // Initialize S3 backup manager
136
133
  let backupManager = null;
137
- if (config.dataPath) {
134
+ if (config.storageDriver === 'sqlite' && config.dataPath) {
138
135
  const backupConfig = resolveBackupConfig(fileConfig, config.dataPath);
139
136
  backupManager = new S3BackupManager({
140
137
  ...backupConfig,
@@ -171,40 +168,17 @@ export function bootServer(fileConfig, config) {
171
168
  httpPort: config.httpPort,
172
169
  shards: SHARD_COUNT,
173
170
  });
174
- // Graceful shutdown
175
- let shuttingDown = false;
176
- const shutdown = async (signal) => {
177
- if (shuttingDown)
178
- return;
179
- shuttingDown = true;
180
- serverLog.info(`Received ${signal}, shutting down...`);
181
- // Stop stats interval immediately
182
- clearInterval(statsInterval);
183
- tcpServer.stop();
184
- httpServer.stop();
185
- const shutdownTimeout = config.shutdownTimeoutMs;
186
- const start = Date.now();
187
- while (Date.now() - start < shutdownTimeout) {
188
- const stats = queueManager.getStats();
189
- if (stats.active === 0)
190
- break;
191
- serverLog.info(`Waiting for ${stats.active} active jobs...`);
192
- await Bun.sleep(1000);
193
- }
194
- // Stop backup manager
195
- if (backupManager) {
196
- backupManager.stop();
197
- }
198
- // Stop Cloud agent (sends final shutdown snapshot)
199
- if (cloudAgent) {
200
- await cloudAgent.stop();
201
- }
202
- queueManager.emitDashboardEvent('server:shutdown', { signal });
203
- queueManager.shutdown();
204
- stopRateLimiter();
205
- serverLog.info('Shutdown complete');
206
- process.exit(0);
207
- };
171
+ const shutdown = createServerShutdown({
172
+ shutdownTimeoutMs: config.shutdownTimeoutMs,
173
+ stopStats: () => clearInterval(statsInterval),
174
+ stopTcp: () => tcpServer.stop(),
175
+ stopHttp: () => httpServer.stop(),
176
+ getActiveJobs: () => queueManager.getStats().active,
177
+ ...(backupManager && { stopBackup: () => backupManager.stop() }),
178
+ emitShutdown: (signal) => queueManager.emitDashboardEvent('server:shutdown', { signal }),
179
+ ...(cloudAgent && { stopCloud: () => cloudAgent.stop() }),
180
+ shutdownStorage: () => shutdownServerQueueManager(queueManager),
181
+ });
208
182
  process.on('SIGINT', () => void shutdown('SIGINT'));
209
183
  process.on('SIGTERM', () => void shutdown('SIGTERM'));
210
184
  process.on('uncaughtException', (err) => {
@@ -0,0 +1,4 @@
1
+ /** Preserve actionable domain errors while redacting storage and network diagnostics. */
2
+ export declare function sanitizeServerError(error: unknown): string;
3
+ /** Keep bounded local diagnostics for redacted infrastructure failures. */
4
+ export declare function serverErrorDiagnostics(error: unknown): Record<string, unknown>;
@@ -0,0 +1,54 @@
1
+ const SQLSTATE_CODE = /^(?:[0-9][0-9A-Z]|F0|HV|P0|XX)[0-9A-Z]{3}$/;
2
+ const INFRASTRUCTURE_CODE = /^(?:SQLITE|POSTGRES|ECONN|EAI_|ENET|EHOST|ETIMEDOUT|EPIPE|ENOTFOUND|EADDR|UND_ERR)/i;
3
+ const INFRASTRUCTURE_MESSAGE = /(?:SQLITE|Postgres(?:SQL)?|SQLSTATE|database|duplicate key|\bconstraint\b|\brelation\b|\bdriver\b|connection (?:refused|reset|terminated)|getaddrinfo|\b(?:ECONN|EAI_|ENET|EHOST|ETIMEDOUT|EPIPE|ENOTFOUND|EADDR|UND_ERR)[A-Z_]*)/i;
4
+ const HOST_ENDPOINT_MESSAGE = /(?:\b(?:localhost|[a-z0-9-]+(?:\.[a-z0-9-]+)+|\d{1,3}(?:\.\d{1,3}){3}):\d{2,5}\b|\[[0-9a-f:]+\]:\d{2,5})/i;
5
+ const MAX_DIAGNOSTIC_LENGTH = 2_000;
6
+ function errorField(error, field) {
7
+ if (typeof error !== 'object' || error === null || !(field in error))
8
+ return '';
9
+ const value = error[field];
10
+ return typeof value === 'string' ? value : '';
11
+ }
12
+ function bounded(value) {
13
+ return value.length <= MAX_DIAGNOSTIC_LENGTH
14
+ ? value
15
+ : `${value.slice(0, MAX_DIAGNOSTIC_LENGTH)}...`;
16
+ }
17
+ function errorMessage(error) {
18
+ if (error instanceof Error)
19
+ return error.message;
20
+ if (typeof error === 'string')
21
+ return error;
22
+ if (typeof error !== 'object' || error === null || !('message' in error))
23
+ return 'Unknown error';
24
+ const message = error['message'];
25
+ return typeof message === 'string' ? message : 'Unknown error';
26
+ }
27
+ /** Preserve actionable domain errors while redacting storage and network diagnostics. */
28
+ export function sanitizeServerError(error) {
29
+ const message = errorMessage(error);
30
+ const code = errorField(error, 'code');
31
+ const name = errorField(error, 'name');
32
+ const infrastructureError = SQLSTATE_CODE.test(code) ||
33
+ INFRASTRUCTURE_CODE.test(code) ||
34
+ /^(?:(?:Database|Postgres|SQLite|SQL).*Error|ConnectionError)$/i.test(name) ||
35
+ INFRASTRUCTURE_MESSAGE.test(message) ||
36
+ HOST_ENDPOINT_MESSAGE.test(message);
37
+ return infrastructureError ? 'Internal server error' : message;
38
+ }
39
+ /** Keep bounded local diagnostics for redacted infrastructure failures. */
40
+ export function serverErrorDiagnostics(error) {
41
+ const diagnostic = { error: bounded(errorMessage(error)) };
42
+ for (const [source, target] of [
43
+ ['name', 'name'],
44
+ ['code', 'code'],
45
+ ['errno', 'sqlState'],
46
+ ['where', 'where'],
47
+ ['routine', 'routine'],
48
+ ]) {
49
+ const value = errorField(error, source);
50
+ if (value)
51
+ diagnostic[target] = bounded(value);
52
+ }
53
+ return diagnostic;
54
+ }
@@ -1,8 +1,8 @@
1
1
  import type { Command } from '../../../domain/types/command';
2
2
  import type { Response } from '../../../domain/types/response';
3
3
  import type { HandlerContext } from '../types';
4
- export declare function routeQueueControlCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
5
- export declare function routeDlqCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
6
- export declare function routeRateLimitCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
7
- export declare function routeCronCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
8
- export declare function routeConfigCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
4
+ export declare function routeQueueControlCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
5
+ export declare function routeDlqCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
6
+ export declare function routeRateLimitCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
7
+ export declare function routeCronCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
8
+ export declare function routeConfigCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
@@ -1,7 +1,8 @@
1
1
  import { handleClean, handleClearConcurrency, handleGetDlqConfig, handleGetStallConfig, handleIsPaused, handleListQueues, handleObliterate, handleRateLimit, handleRateLimitClear, handleSetConcurrency, handleSetDlqConfig, handleSetStallConfig, } from '../handlers/advanced';
2
2
  import { handleCron, handleCronDelete, handleCronGet, handleCronList } from '../handlers/cron';
3
- import { handleDlq, handleGetDlqStats, handlePurgeDlq, handleRetryCompleted, handleRetryDlq, } from '../handlers/dlq';
3
+ import { handleDlq, handleGetDlqStats, handlePurgeDlq, handleRemoveDlqJob, handleRetryCompleted, handleRetryDlq, } from '../handlers/dlq';
4
4
  import { handleDrain, handlePause, handleResume } from '../handlers/management';
5
+ import { handleGroupCommand, handleGroupsJobsCount } from '../handlers/groups';
5
6
  export function routeQueueControlCommand(command, context, requestId) {
6
7
  switch (command.cmd) {
7
8
  case 'Pause':
@@ -32,6 +33,8 @@ export function routeDlqCommand(command, context, requestId) {
32
33
  return handleRetryDlq(command, context, requestId);
33
34
  case 'PurgeDlq':
34
35
  return handlePurgeDlq(command, context, requestId);
36
+ case 'RemoveDlqJob':
37
+ return handleRemoveDlqJob(command, context, requestId);
35
38
  case 'RetryCompleted':
36
39
  return handleRetryCompleted(command, context, requestId);
37
40
  default:
@@ -40,6 +43,18 @@ export function routeDlqCommand(command, context, requestId) {
40
43
  }
41
44
  export function routeRateLimitCommand(command, context, requestId) {
42
45
  switch (command.cmd) {
46
+ case 'GetGroupsJobsCount':
47
+ return handleGroupsJobsCount(command, context, requestId);
48
+ case 'GetGroupJobsCount':
49
+ case 'GetGroupActiveCount':
50
+ case 'SetGroupRateLimit':
51
+ case 'GetGroupRateLimit':
52
+ case 'RemoveGroupRateLimit':
53
+ case 'GetGroupRateLimitTtl':
54
+ case 'SetGroupConcurrency':
55
+ case 'GetGroupConcurrency':
56
+ case 'RemoveGroupConcurrency':
57
+ return handleGroupCommand(command, context, requestId);
43
58
  case 'RateLimit':
44
59
  return handleRateLimit(command, context, requestId);
45
60
  case 'RateLimitClear':
@@ -1,5 +1,5 @@
1
1
  import type { Command } from '../../../domain/types/command';
2
2
  import type { Response } from '../../../domain/types/response';
3
3
  import type { HandlerContext } from '../types';
4
- export declare function routeMonitoringCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
5
- export declare function routeDashboardCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
4
+ export declare function routeMonitoringCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
5
+ export declare function routeDashboardCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import * as resp from '../../domain/types/response';
6
6
  import { constantTimeEqual } from '../../shared/hash';
7
+ import { sanitizeServerError } from './errors';
7
8
  import { routeCoreCommand, routeQueryCommand, routeManagementCommand, routeQueueControlCommand, routeDlqCommand, routeRateLimitCommand, routeConfigCommand, routeCronCommand, routeMonitoringCommand, routeDashboardCommand, } from './handlerRoutes';
8
9
  /**
9
10
  * Handle authentication command
@@ -43,33 +44,30 @@ export async function handleCommand(cmd, ctx) {
43
44
  result = await routeManagementCommand(cmd, ctx, reqId);
44
45
  if (result)
45
46
  return result;
46
- result = routeQueueControlCommand(cmd, ctx, reqId);
47
+ result = await routeQueueControlCommand(cmd, ctx, reqId);
47
48
  if (result)
48
49
  return result;
49
- result = routeDlqCommand(cmd, ctx, reqId);
50
+ result = await routeDlqCommand(cmd, ctx, reqId);
50
51
  if (result)
51
52
  return result;
52
- result = routeRateLimitCommand(cmd, ctx, reqId);
53
+ result = await routeRateLimitCommand(cmd, ctx, reqId);
53
54
  if (result)
54
55
  return result;
55
- result = routeConfigCommand(cmd, ctx, reqId);
56
+ result = await routeConfigCommand(cmd, ctx, reqId);
56
57
  if (result)
57
58
  return result;
58
- result = routeCronCommand(cmd, ctx, reqId);
59
+ result = await routeCronCommand(cmd, ctx, reqId);
59
60
  if (result)
60
61
  return result;
61
- result = routeMonitoringCommand(cmd, ctx, reqId);
62
+ result = await routeMonitoringCommand(cmd, ctx, reqId);
62
63
  if (result)
63
64
  return result;
64
- result = routeDashboardCommand(cmd, ctx, reqId);
65
+ result = await routeDashboardCommand(cmd, ctx, reqId);
65
66
  if (result)
66
67
  return result;
67
68
  return resp.error(`Unknown command: ${cmd.cmd}`, reqId);
68
69
  }
69
70
  catch (err) {
70
- // Sanitize error messages to avoid leaking internal details to clients
71
- const raw = err instanceof Error ? err.message : 'Unknown error';
72
- const message = raw.includes('SQLITE') || raw.includes('database') ? 'Internal server error' : raw;
73
- return resp.error(message, reqId);
71
+ return resp.error(sanitizeServerError(err), reqId);
74
72
  }
75
73
  }