bunqueue 2.8.60 → 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 (324) hide show
  1. package/README.md +108 -76
  2. package/dist/application/background/dlq.js +2 -0
  3. package/dist/application/contextFactory.js +5 -0
  4. package/dist/application/dlqManager.d.ts +10 -0
  5. package/dist/application/dlqManager.js +47 -9
  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/operations/jobManagement.d.ts +4 -0
  10. package/dist/application/operations/jobMoveOperations.js +9 -4
  11. package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
  12. package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
  13. package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
  14. package/dist/application/postgres-queue-manager/cloud.js +55 -0
  15. package/dist/application/postgres-queue-manager/config.d.ts +5 -0
  16. package/dist/application/postgres-queue-manager/config.js +1 -0
  17. package/dist/application/postgres-queue-manager/control.d.ts +26 -0
  18. package/dist/application/postgres-queue-manager/control.js +186 -0
  19. package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
  20. package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
  21. package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
  22. package/dist/application/postgres-queue-manager/delivery.js +80 -0
  23. package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
  24. package/dist/application/postgres-queue-manager/lease.js +88 -0
  25. package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
  26. package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
  27. package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
  28. package/dist/application/postgres-queue-manager/maintenance.js +100 -0
  29. package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
  30. package/dist/application/postgres-queue-manager/operationGate.js +96 -0
  31. package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
  32. package/dist/application/postgres-queue-manager/operations.js +194 -0
  33. package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
  34. package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
  35. package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
  36. package/dist/application/postgres-queue-manager/queries.js +231 -0
  37. package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
  38. package/dist/application/postgres-queue-manager/relationships.js +45 -0
  39. package/dist/application/postgres-queue-manager/services.d.ts +31 -0
  40. package/dist/application/postgres-queue-manager/services.js +149 -0
  41. package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
  42. package/dist/application/postgres-queue-manager/snapshot.js +236 -0
  43. package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
  44. package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
  45. package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
  46. package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
  47. package/dist/application/postgres-queue-manager/state.d.ts +47 -0
  48. package/dist/application/postgres-queue-manager/state.js +273 -0
  49. package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
  50. package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
  51. package/dist/application/postgresQueueManager.d.ts +2 -0
  52. package/dist/application/postgresQueueManager.js +1 -0
  53. package/dist/application/queue-manager/flow-failures.js +4 -0
  54. package/dist/application/queue-manager/limits.d.ts +2 -0
  55. package/dist/application/queue-manager/limits.js +4 -0
  56. package/dist/application/queue-manager/observability.js +15 -3
  57. package/dist/application/queue-manager/queries.d.ts +7 -0
  58. package/dist/application/queue-manager/queries.js +9 -0
  59. package/dist/application/queue-manager/services.d.ts +1 -1
  60. package/dist/application/queue-manager/services.js +4 -2
  61. package/dist/application/queue-manager/state.js +16 -1
  62. package/dist/cli/commands/server.js +1 -1
  63. package/dist/client/forwarder.d.ts +6 -4
  64. package/dist/client/forwarder.js +5 -3
  65. package/dist/client/queue/dlq.d.ts +2 -0
  66. package/dist/client/queue/dlq.js +14 -0
  67. package/dist/client/queue/dlqOps.d.ts +2 -0
  68. package/dist/client/queue/dlqOps.js +4 -0
  69. package/dist/client/queue/helpers.js +2 -0
  70. package/dist/client/queue/runtime/configuration.d.ts +4 -0
  71. package/dist/client/queue/runtime/configuration.js +8 -0
  72. package/dist/client/worker/runtime/execution.js +3 -8
  73. package/dist/client/worker/runtime/polling.d.ts +1 -0
  74. package/dist/client/worker/runtime/polling.js +9 -0
  75. package/dist/config/resolve.d.ts +14 -0
  76. package/dist/config/resolve.js +50 -5
  77. package/dist/config/types.d.ts +18 -0
  78. package/dist/domain/queue/dlqShard.d.ts +2 -0
  79. package/dist/domain/queue/dlqShard.js +16 -0
  80. package/dist/domain/queue/shard/dlq.d.ts +1 -0
  81. package/dist/domain/queue/shard/dlq.js +3 -0
  82. package/dist/domain/types/commands/dlq.d.ts +5 -0
  83. package/dist/domain/types/commands/union.d.ts +1 -1
  84. package/dist/domain/types/cron.d.ts +5 -0
  85. package/dist/domain/types/cron.js +14 -3
  86. package/dist/domain/types/dlq.d.ts +1 -1
  87. package/dist/domain/types/dlq.js +1 -2
  88. package/dist/domain/types/jobs/model.d.ts +1 -1
  89. package/dist/infrastructure/cloud/commandHandler.js +5 -3
  90. package/dist/infrastructure/cloud/commands/integrations.js +11 -11
  91. package/dist/infrastructure/cloud/commands/jobs.js +48 -40
  92. package/dist/infrastructure/cloud/commands/queues.js +61 -75
  93. package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
  94. package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
  95. package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
  96. package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
  97. package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
  98. package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
  99. package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
  100. package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
  101. package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
  102. package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
  103. package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
  104. package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
  105. package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
  106. package/dist/infrastructure/cloud/snapshot/config.js +43 -0
  107. package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
  108. package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
  109. package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
  110. package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
  111. package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
  112. package/dist/infrastructure/cloud/statsRefresh.js +2 -1
  113. package/dist/infrastructure/cloud/types/command.d.ts +2 -2
  114. package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
  115. package/dist/infrastructure/persistence/postgres/admission.js +193 -0
  116. package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
  117. package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
  118. package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
  119. package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
  120. package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
  121. package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
  122. package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
  123. package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
  124. package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
  125. package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
  126. package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
  127. package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
  128. package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
  129. package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
  130. package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
  131. package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
  132. package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
  133. package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
  134. package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
  135. package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
  136. package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
  137. package/dist/infrastructure/persistence/postgres/claims.js +137 -0
  138. package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
  139. package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
  140. package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
  141. package/dist/infrastructure/persistence/postgres/codec.js +82 -0
  142. package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
  143. package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
  144. package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
  145. package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
  146. package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
  147. package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
  148. package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
  149. package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
  150. package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
  151. package/dist/infrastructure/persistence/postgres/connection.js +15 -0
  152. package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
  153. package/dist/infrastructure/persistence/postgres/context.js +74 -0
  154. package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
  155. package/dist/infrastructure/persistence/postgres/control.js +105 -0
  156. package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
  157. package/dist/infrastructure/persistence/postgres/crons.js +205 -0
  158. package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
  159. package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
  160. package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
  161. package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
  162. package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
  163. package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
  164. package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
  165. package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
  166. package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
  167. package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
  168. package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
  169. package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
  170. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
  171. package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
  172. package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
  173. package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
  174. package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
  175. package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
  176. package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
  177. package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
  178. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
  179. package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
  180. package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
  181. package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
  182. package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
  183. package/dist/infrastructure/persistence/postgres/events.js +249 -0
  184. package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
  185. package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
  186. package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
  187. package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
  188. package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
  189. package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
  190. package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
  191. package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
  192. package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
  193. package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
  194. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
  195. package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
  196. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
  197. package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
  198. package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
  199. package/dist/infrastructure/persistence/postgres/logs.js +70 -0
  200. package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
  201. package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
  202. package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
  203. package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
  204. package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
  205. package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
  206. package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
  207. package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
  208. package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
  209. package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
  210. package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
  211. package/dist/infrastructure/persistence/postgres/policies.js +18 -0
  212. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
  213. package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
  214. package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
  215. package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
  216. package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
  217. package/dist/infrastructure/persistence/postgres/queries.js +152 -0
  218. package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
  219. package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
  220. package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
  221. package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
  222. package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
  223. package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
  224. package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
  225. package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
  226. package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
  227. package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
  228. package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
  229. package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
  230. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
  231. package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
  232. package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
  233. package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
  234. package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
  235. package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
  236. package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
  237. package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
  238. package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
  239. package/dist/infrastructure/persistence/postgres/schema.js +257 -0
  240. package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
  241. package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
  242. package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
  243. package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
  244. package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
  245. package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
  246. package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
  247. package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
  248. package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
  249. package/dist/infrastructure/persistence/postgres/store.js +136 -0
  250. package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
  251. package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
  252. package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
  253. package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
  254. package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
  255. package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
  256. package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
  257. package/dist/infrastructure/persistence/postgres/types.js +1 -0
  258. package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
  259. package/dist/infrastructure/persistence/postgres/workers.js +126 -0
  260. package/dist/infrastructure/persistence/postgres.d.ts +2 -0
  261. package/dist/infrastructure/persistence/postgres.js +1 -0
  262. package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
  263. package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
  264. package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
  265. package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
  266. package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
  267. package/dist/infrastructure/scheduler/cron/validation.js +11 -0
  268. package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
  269. package/dist/infrastructure/scheduler/cronParser.js +144 -11
  270. package/dist/infrastructure/server/bootstrap.d.ts +2 -3
  271. package/dist/infrastructure/server/bootstrap.js +33 -59
  272. package/dist/infrastructure/server/errors.d.ts +4 -0
  273. package/dist/infrastructure/server/errors.js +54 -0
  274. package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
  275. package/dist/infrastructure/server/handler-routes/control.js +3 -1
  276. package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
  277. package/dist/infrastructure/server/handler.js +9 -11
  278. package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
  279. package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
  280. package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
  281. package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
  282. package/dist/infrastructure/server/handlers/core.js +32 -31
  283. package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
  284. package/dist/infrastructure/server/handlers/cron.js +60 -59
  285. package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
  286. package/dist/infrastructure/server/handlers/dashboard.js +21 -8
  287. package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
  288. package/dist/infrastructure/server/handlers/dlq.js +35 -9
  289. package/dist/infrastructure/server/handlers/flow.js +2 -1
  290. package/dist/infrastructure/server/handlers/management.d.ts +5 -5
  291. package/dist/infrastructure/server/handlers/management.js +36 -9
  292. package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
  293. package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
  294. package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
  295. package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
  296. package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
  297. package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
  298. package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
  299. package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
  300. package/dist/infrastructure/server/handlers/query.d.ts +1 -1
  301. package/dist/infrastructure/server/handlers/query.js +5 -4
  302. package/dist/infrastructure/server/http.js +2 -2
  303. package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
  304. package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
  305. package/dist/infrastructure/server/httpEndpoints.js +18 -7
  306. package/dist/infrastructure/server/httpRouteQueues.js +11 -1
  307. package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
  308. package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
  309. package/dist/infrastructure/server/sseHandler.js +8 -3
  310. package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
  311. package/dist/infrastructure/server/storageAdapter.js +61 -0
  312. package/dist/infrastructure/server/storageManager.d.ts +12 -0
  313. package/dist/infrastructure/server/storageManager.js +102 -0
  314. package/dist/infrastructure/server/tcp.js +2 -3
  315. package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
  316. package/dist/infrastructure/server/ws/snapshots.js +19 -4
  317. package/dist/infrastructure/server/wsHandler.js +10 -5
  318. package/dist/main.js +1 -1
  319. package/dist/mcp/backend/embedded/services.js +2 -1
  320. package/dist/mcp/index.d.ts +2 -2
  321. package/dist/mcp/index.js +2 -2
  322. package/dist/shared/storageHealth.d.ts +9 -0
  323. package/dist/shared/storageHealth.js +12 -0
  324. package/package.json +74 -65
@@ -0,0 +1,126 @@
1
+ import { decodePostgresValue, encodePostgresValue } from './codec';
2
+ import { databaseNow } from './context';
3
+ import { lockPostgresBrokerSession } from './brokerSessions';
4
+ export async function savePostgresWorker(ctx, worker) {
5
+ await ctx.sql.begin(async (tx) => {
6
+ await lockPostgresBrokerSession(tx, ctx);
7
+ worker.lastSeen = await databaseNow(tx);
8
+ await tx `
9
+ INSERT INTO bunqueue_workers
10
+ (namespace, id, broker_id, broker_session_id, client_id, queues, payload, last_seen)
11
+ VALUES
12
+ (${ctx.config.namespace}, ${worker.id}, ${ctx.config.brokerId},
13
+ ${ctx.config.brokerSessionId}, ${worker.clientId},
14
+ ${tx.array(worker.queues, 'TEXT')}, ${encodePostgresValue(worker)}, ${worker.lastSeen})
15
+ ON CONFLICT (namespace, id) DO UPDATE SET
16
+ broker_id = excluded.broker_id,
17
+ broker_session_id = excluded.broker_session_id,
18
+ client_id = excluded.client_id,
19
+ queues = excluded.queues,
20
+ payload = excluded.payload,
21
+ last_seen = excluded.last_seen
22
+ `;
23
+ });
24
+ }
25
+ export async function removePostgresWorker(ctx, id, clientId) {
26
+ const expectedClientId = clientId ?? null;
27
+ const rows = await ctx.sql `
28
+ DELETE FROM bunqueue_workers
29
+ WHERE namespace = ${ctx.config.namespace} AND id = ${id}
30
+ AND broker_id = ${ctx.config.brokerId}
31
+ AND broker_session_id = ${ctx.config.brokerSessionId}
32
+ AND (${expectedClientId}::text IS NULL OR client_id IS NOT DISTINCT FROM ${expectedClientId})
33
+ RETURNING id
34
+ `;
35
+ return rows.length > 0;
36
+ }
37
+ export async function heartbeatPostgresWorker(ctx, id, stats, clientId) {
38
+ return await ctx.sql.begin(async (tx) => {
39
+ await lockPostgresBrokerSession(tx, ctx);
40
+ const expectedClientId = clientId ?? null;
41
+ const rows = await tx `
42
+ SELECT id, payload FROM bunqueue_workers
43
+ WHERE namespace = ${ctx.config.namespace} AND id = ${id}
44
+ AND broker_id = ${ctx.config.brokerId}
45
+ AND broker_session_id = ${ctx.config.brokerSessionId}
46
+ AND (${expectedClientId}::text IS NULL OR client_id IS NOT DISTINCT FROM ${expectedClientId})
47
+ FOR UPDATE
48
+ `;
49
+ const row = rows[0];
50
+ if (!row)
51
+ return false;
52
+ const worker = decodePostgresValue(row.payload, null, `postgresWorker:${row.id}`);
53
+ if (!worker)
54
+ throw new Error(`Corrupt PostgreSQL worker payload for ${row.id}`);
55
+ worker.lastSeen = await databaseNow(tx);
56
+ if (stats?.activeJobs !== undefined)
57
+ worker.activeJobs = stats.activeJobs;
58
+ if (stats?.processed !== undefined)
59
+ worker.processedJobs = stats.processed;
60
+ if (stats?.failed !== undefined)
61
+ worker.failedJobs = stats.failed;
62
+ if (clientId !== undefined)
63
+ worker.clientId = clientId;
64
+ await tx `
65
+ UPDATE bunqueue_workers
66
+ SET broker_id = ${ctx.config.brokerId},
67
+ broker_session_id = ${ctx.config.brokerSessionId}, client_id = ${worker.clientId},
68
+ payload = ${encodePostgresValue(worker)}, last_seen = ${worker.lastSeen}
69
+ WHERE namespace = ${ctx.config.namespace} AND id = ${id}
70
+ AND broker_id = ${ctx.config.brokerId}
71
+ AND broker_session_id = ${ctx.config.brokerSessionId}
72
+ AND (${expectedClientId}::text IS NULL OR client_id IS NOT DISTINCT FROM ${expectedClientId})
73
+ `;
74
+ return true;
75
+ });
76
+ }
77
+ export async function removePostgresClientWorkers(ctx, clientId) {
78
+ const rows = await ctx.sql `
79
+ DELETE FROM bunqueue_workers
80
+ WHERE namespace = ${ctx.config.namespace}
81
+ AND broker_id = ${ctx.config.brokerId} AND client_id = ${clientId}
82
+ AND broker_session_id = ${ctx.config.brokerSessionId}
83
+ RETURNING id
84
+ `;
85
+ return rows.length;
86
+ }
87
+ export async function removePostgresBrokerWorkers(ctx) {
88
+ const rows = await ctx.sql `
89
+ DELETE FROM bunqueue_workers
90
+ WHERE namespace = ${ctx.config.namespace} AND broker_id = ${ctx.config.brokerId}
91
+ AND broker_session_id = ${ctx.config.brokerSessionId}
92
+ RETURNING id
93
+ `;
94
+ return rows.length;
95
+ }
96
+ export async function listPostgresWorkers(ctx) {
97
+ const rows = await ctx.sql `
98
+ SELECT id, payload FROM bunqueue_workers
99
+ WHERE namespace = ${ctx.config.namespace}
100
+ ORDER BY id
101
+ `;
102
+ return rows.flatMap((row) => {
103
+ const worker = decodePostgresValue(row.payload, null, `postgresWorker:${row.id}`);
104
+ return worker ? [worker] : [];
105
+ });
106
+ }
107
+ export async function hasActivePostgresWorker(sql, ctx, queue, now, timeoutMs = 30_000) {
108
+ const rows = await sql `
109
+ SELECT EXISTS (
110
+ SELECT 1 FROM bunqueue_workers
111
+ WHERE namespace = ${ctx.config.namespace}
112
+ AND ${queue} = ANY(queues)
113
+ AND last_seen > ${now - timeoutMs}
114
+ ) AS found
115
+ `;
116
+ return rows[0]?.found ?? false;
117
+ }
118
+ export async function purgeStalePostgresWorkers(ctx, requestedNow, staleMs = 90_000) {
119
+ const now = requestedNow ?? (await databaseNow(ctx.sql));
120
+ const rows = await ctx.sql `
121
+ DELETE FROM bunqueue_workers
122
+ WHERE namespace = ${ctx.config.namespace} AND last_seen <= ${now - staleMs}
123
+ RETURNING id
124
+ `;
125
+ return rows.length;
126
+ }
@@ -0,0 +1,2 @@
1
+ export { PostgresQueueStore } from './postgres/store';
2
+ export type { ClaimedPostgresJob, PostgresCompletionResult, PostgresCounts, PostgresDeliveredStoreEvent, PostgresFailureInput, PostgresQueueState, PostgresStorageConfig, PostgresStorageHealth, PostgresStoreEvent, PostgresStoredJob, PostgresJobState, } from './postgres/types';
@@ -0,0 +1 @@
1
+ export { PostgresQueueStore } from './postgres/store';
@@ -132,6 +132,7 @@ export class SqliteJobs extends SqliteAdmission {
132
132
  const clearDlq = this.statements.get('clearDlqQueue');
133
133
  const deleteJob = this.statements.get('deleteJob');
134
134
  const deleteResult = this.statements.get('deleteJobResult');
135
+ const deleteFlowFailures = this.db.prepare('DELETE FROM flow_failures WHERE parent_id = ?');
135
136
  const transaction = this.db.transaction(() => {
136
137
  if (clearQueue)
137
138
  clearDlq.run(queue);
@@ -141,6 +142,7 @@ export class SqliteJobs extends SqliteAdmission {
141
142
  for (const jobId of terminalJobIds) {
142
143
  deleteJob.run(jobId);
143
144
  deleteResult.run(jobId);
145
+ deleteFlowFailures.run(jobId);
144
146
  }
145
147
  });
146
148
  transaction();
@@ -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>;