bunqueue 2.8.59 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -77
- package/dist/application/background/dlq.js +2 -0
- package/dist/application/contextFactory.js +5 -0
- package/dist/application/dlqManager.d.ts +11 -1
- package/dist/application/dlqManager.js +48 -10
- package/dist/application/dlqRetry.js +59 -38
- package/dist/application/eventsManager.d.ts +1 -1
- package/dist/application/eventsManager.js +30 -26
- package/dist/application/lockManager.js +1 -1
- package/dist/application/operations/jobManagement.d.ts +4 -0
- package/dist/application/operations/jobMoveOperations.js +9 -4
- package/dist/application/postgres-queue-manager/batchSnapshot.d.ts +16 -0
- package/dist/application/postgres-queue-manager/batchSnapshot.js +17 -0
- package/dist/application/postgres-queue-manager/cloud.d.ts +30 -0
- package/dist/application/postgres-queue-manager/cloud.js +55 -0
- package/dist/application/postgres-queue-manager/config.d.ts +5 -0
- package/dist/application/postgres-queue-manager/config.js +1 -0
- package/dist/application/postgres-queue-manager/control.d.ts +26 -0
- package/dist/application/postgres-queue-manager/control.js +186 -0
- package/dist/application/postgres-queue-manager/deferredWrites.d.ts +13 -0
- package/dist/application/postgres-queue-manager/deferredWrites.js +60 -0
- package/dist/application/postgres-queue-manager/delivery.d.ts +19 -0
- package/dist/application/postgres-queue-manager/delivery.js +80 -0
- package/dist/application/postgres-queue-manager/lease.d.ts +13 -0
- package/dist/application/postgres-queue-manager/lease.js +88 -0
- package/dist/application/postgres-queue-manager/lifetimeMetrics.d.ts +32 -0
- package/dist/application/postgres-queue-manager/lifetimeMetrics.js +53 -0
- package/dist/application/postgres-queue-manager/maintenance.d.ts +14 -0
- package/dist/application/postgres-queue-manager/maintenance.js +100 -0
- package/dist/application/postgres-queue-manager/operationGate.d.ts +26 -0
- package/dist/application/postgres-queue-manager/operationGate.js +96 -0
- package/dist/application/postgres-queue-manager/operations.d.ts +25 -0
- package/dist/application/postgres-queue-manager/operations.js +194 -0
- package/dist/application/postgres-queue-manager/projectionRefreshes.d.ts +43 -0
- package/dist/application/postgres-queue-manager/projectionRefreshes.js +170 -0
- package/dist/application/postgres-queue-manager/queries.d.ts +142 -0
- package/dist/application/postgres-queue-manager/queries.js +231 -0
- package/dist/application/postgres-queue-manager/relationships.d.ts +10 -0
- package/dist/application/postgres-queue-manager/relationships.js +45 -0
- package/dist/application/postgres-queue-manager/services.d.ts +31 -0
- package/dist/application/postgres-queue-manager/services.js +149 -0
- package/dist/application/postgres-queue-manager/snapshot.d.ts +51 -0
- package/dist/application/postgres-queue-manager/snapshot.js +236 -0
- package/dist/application/postgres-queue-manager/snapshotViews.d.ts +15 -0
- package/dist/application/postgres-queue-manager/snapshotViews.js +100 -0
- package/dist/application/postgres-queue-manager/startupEventBuffer.d.ts +12 -0
- package/dist/application/postgres-queue-manager/startupEventBuffer.js +36 -0
- package/dist/application/postgres-queue-manager/state.d.ts +47 -0
- package/dist/application/postgres-queue-manager/state.js +273 -0
- package/dist/application/postgres-queue-manager/terminalDelivery.d.ts +20 -0
- package/dist/application/postgres-queue-manager/terminalDelivery.js +100 -0
- package/dist/application/postgresQueueManager.d.ts +2 -0
- package/dist/application/postgresQueueManager.js +1 -0
- package/dist/application/queue-manager/ack.js +1 -1
- package/dist/application/queue-manager/delivery.js +1 -1
- package/dist/application/queue-manager/flow-failures.js +4 -0
- package/dist/application/queue-manager/limits.d.ts +2 -0
- package/dist/application/queue-manager/limits.js +4 -0
- package/dist/application/queue-manager/observability.js +15 -3
- package/dist/application/queue-manager/queries.d.ts +7 -0
- package/dist/application/queue-manager/queries.js +9 -0
- package/dist/application/queue-manager/services.d.ts +1 -1
- package/dist/application/queue-manager/services.js +4 -2
- package/dist/application/queue-manager/state.js +16 -1
- package/dist/application/statsManager.d.ts +1 -1
- package/dist/application/statsManager.js +1 -1
- package/dist/cli/commands/server.js +1 -1
- package/dist/client/events.js +2 -2
- package/dist/client/flow.js +0 -1
- package/dist/client/forwarder.d.ts +6 -4
- package/dist/client/forwarder.js +5 -3
- package/dist/client/queue/dlq.d.ts +2 -0
- package/dist/client/queue/dlq.js +14 -0
- package/dist/client/queue/dlqOps.d.ts +2 -0
- package/dist/client/queue/dlqOps.js +4 -0
- package/dist/client/queue/helpers.js +2 -0
- package/dist/client/queue/operations/add/single.js +1 -1
- package/dist/client/queue/runtime/configuration.d.ts +4 -0
- package/dist/client/queue/runtime/configuration.js +8 -0
- package/dist/client/sandboxed/runtime/state.js +2 -2
- package/dist/client/tcp/connection.d.ts +1 -1
- package/dist/client/tcp/connection.js +1 -1
- package/dist/client/tcp/runtime/state.js +2 -2
- package/dist/client/types/metrics.d.ts +1 -1
- package/dist/client/worker/runtime/control.js +0 -1
- package/dist/client/worker/runtime/execution.js +3 -8
- package/dist/client/worker/runtime/lifecycle.js +0 -1
- package/dist/client/worker/runtime/polling.d.ts +1 -0
- package/dist/client/worker/runtime/polling.js +9 -0
- package/dist/client/worker/runtime/state.js +3 -3
- package/dist/client/workflow/compensationChild.d.ts +2 -0
- package/dist/client/workflow/compensationChild.js +3 -1
- package/dist/client/workflow/compensationPass.d.ts +1 -0
- package/dist/client/workflow/compensationPass.js +18 -2
- package/dist/client/workflow/compensator.d.ts +2 -1
- package/dist/client/workflow/compensator.js +11 -3
- package/dist/client/workflow/engine.js +1 -1
- package/dist/client/workflow/executionFence.d.ts +15 -0
- package/dist/client/workflow/executionFence.js +29 -0
- package/dist/client/workflow/executor.d.ts +5 -11
- package/dist/client/workflow/executor.js +48 -47
- package/dist/client/workflow/executorLifecycle.d.ts +1 -0
- package/dist/client/workflow/executorLifecycle.js +41 -10
- package/dist/client/workflow/executorNodes.d.ts +1 -0
- package/dist/client/workflow/executorNodes.js +45 -16
- package/dist/client/workflow/executorQueue.d.ts +9 -0
- package/dist/client/workflow/executorQueue.js +15 -0
- package/dist/client/workflow/forEachRunner.d.ts +4 -0
- package/dist/client/workflow/forEachRunner.js +70 -0
- package/dist/client/workflow/loops.d.ts +4 -5
- package/dist/client/workflow/loops.js +27 -93
- package/dist/client/workflow/mapRunner.d.ts +1 -1
- package/dist/client/workflow/mapRunner.js +11 -2
- package/dist/client/workflow/recovery.d.ts +1 -0
- package/dist/client/workflow/recovery.js +24 -18
- package/dist/client/workflow/rollbackControl.d.ts +1 -0
- package/dist/client/workflow/rollbackControl.js +5 -2
- package/dist/client/workflow/runner.d.ts +3 -1
- package/dist/client/workflow/runner.js +26 -3
- package/dist/client/workflow/stepTypes.js +1 -1
- package/dist/client/workflow/subWorkflowRunner.d.ts +1 -0
- package/dist/client/workflow/subWorkflowRunner.js +7 -0
- package/dist/client/workflow/waitFor.d.ts +4 -0
- package/dist/client/workflow/waitFor.js +23 -11
- package/dist/client/workflow/workflowDecisions.d.ts +1 -1
- package/dist/client/workflow/workflowDecisions.js +3 -1
- package/dist/config/resolve.d.ts +14 -0
- package/dist/config/resolve.js +50 -5
- package/dist/config/types.d.ts +18 -0
- package/dist/domain/queue/dlqShard.d.ts +2 -0
- package/dist/domain/queue/dlqShard.js +16 -0
- package/dist/domain/queue/shard/dlq.d.ts +1 -0
- package/dist/domain/queue/shard/dlq.js +3 -0
- package/dist/domain/types/commands/dlq.d.ts +5 -0
- package/dist/domain/types/commands/union.d.ts +1 -1
- package/dist/domain/types/cron.d.ts +5 -0
- package/dist/domain/types/cron.js +14 -3
- package/dist/domain/types/dlq.d.ts +1 -1
- package/dist/domain/types/dlq.js +1 -2
- package/dist/domain/types/jobs/model.d.ts +1 -1
- package/dist/infrastructure/cloud/commandHandler.js +5 -3
- package/dist/infrastructure/cloud/commands/integrations.js +11 -11
- package/dist/infrastructure/cloud/commands/jobs.js +48 -40
- package/dist/infrastructure/cloud/commands/queues.js +61 -75
- package/dist/infrastructure/cloud/queueAdapter/local.d.ts +41 -0
- package/dist/infrastructure/cloud/queueAdapter/local.js +187 -0
- package/dist/infrastructure/cloud/queueAdapter/postgres.d.ts +42 -0
- package/dist/infrastructure/cloud/queueAdapter/postgres.js +188 -0
- package/dist/infrastructure/cloud/queueAdapter/registry.d.ts +4 -0
- package/dist/infrastructure/cloud/queueAdapter/registry.js +15 -0
- package/dist/infrastructure/cloud/queueAdapter/types.d.ts +87 -0
- package/dist/infrastructure/cloud/queueAdapter/types.js +1 -0
- package/dist/infrastructure/cloud/snapshot/analytics.d.ts +2 -0
- package/dist/infrastructure/cloud/snapshot/analytics.js +7 -0
- package/dist/infrastructure/cloud/snapshot/collector.d.ts +4 -0
- package/dist/infrastructure/cloud/snapshot/collector.js +39 -1
- package/dist/infrastructure/cloud/snapshot/config.d.ts +3 -0
- package/dist/infrastructure/cloud/snapshot/config.js +43 -0
- package/dist/infrastructure/cloud/snapshot/jobs.d.ts +3 -0
- package/dist/infrastructure/cloud/snapshot/jobs.js +32 -1
- package/dist/infrastructure/cloud/snapshotCollector.js +20 -35
- package/dist/infrastructure/cloud/snapshotHelpers.d.ts +3 -3
- package/dist/infrastructure/cloud/snapshotHelpers.js +3 -3
- package/dist/infrastructure/cloud/statsRefresh.js +2 -1
- package/dist/infrastructure/cloud/types/command.d.ts +2 -2
- package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/admission.js +193 -0
- package/dist/infrastructure/persistence/postgres/admissionParent.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/admissionParent.js +35 -0
- package/dist/infrastructure/persistence/postgres/admissionResult.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/admissionResult.js +1 -0
- package/dist/infrastructure/persistence/postgres/admissionStore.d.ts +12 -0
- package/dist/infrastructure/persistence/postgres/admissionStore.js +101 -0
- package/dist/infrastructure/persistence/postgres/advisoryLocks.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres/advisoryLocks.js +13 -0
- package/dist/infrastructure/persistence/postgres/batchAdmission.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/batchAdmission.js +110 -0
- package/dist/infrastructure/persistence/postgres/batchCompletion.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/batchCompletion.js +174 -0
- package/dist/infrastructure/persistence/postgres/batchEvents.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/batchEvents.js +72 -0
- package/dist/infrastructure/persistence/postgres/brokerSessions.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/brokerSessions.js +94 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.js +56 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.js +198 -0
- package/dist/infrastructure/persistence/postgres/claims.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/claims.js +137 -0
- package/dist/infrastructure/persistence/postgres/cloudReadModel.d.ts +26 -0
- package/dist/infrastructure/persistence/postgres/cloudReadModel.js +123 -0
- package/dist/infrastructure/persistence/postgres/codec.d.ts +30 -0
- package/dist/infrastructure/persistence/postgres/codec.js +82 -0
- package/dist/infrastructure/persistence/postgres/completionEvents.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/completionEvents.js +21 -0
- package/dist/infrastructure/persistence/postgres/completionLifecycle.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/completionLifecycle.js +145 -0
- package/dist/infrastructure/persistence/postgres/completionOutcome.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/completionOutcome.js +112 -0
- package/dist/infrastructure/persistence/postgres/completionQueries.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/completionQueries.js +87 -0
- package/dist/infrastructure/persistence/postgres/connection.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/connection.js +15 -0
- package/dist/infrastructure/persistence/postgres/context.d.ts +28 -0
- package/dist/infrastructure/persistence/postgres/context.js +74 -0
- package/dist/infrastructure/persistence/postgres/control.d.ts +23 -0
- package/dist/infrastructure/persistence/postgres/control.js +105 -0
- package/dist/infrastructure/persistence/postgres/crons.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/crons.js +205 -0
- package/dist/infrastructure/persistence/postgres/deduplication.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/deduplication.js +47 -0
- package/dist/infrastructure/persistence/postgres/dependencyDestruction.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/dependencyDestruction.js +74 -0
- package/dist/infrastructure/persistence/postgres/dependencyPromotion.d.ts +29 -0
- package/dist/infrastructure/persistence/postgres/dependencyPromotion.js +212 -0
- package/dist/infrastructure/persistence/postgres/destructiveMutations.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/destructiveMutations.js +111 -0
- package/dist/infrastructure/persistence/postgres/dlqLifecycle.d.ts +13 -0
- package/dist/infrastructure/persistence/postgres/dlqLifecycle.js +184 -0
- package/dist/infrastructure/persistence/postgres/dlqMaintenance.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/dlqMaintenance.js +36 -0
- package/dist/infrastructure/persistence/postgres/dlqRetryPlan.d.ts +21 -0
- package/dist/infrastructure/persistence/postgres/dlqRetryPlan.js +92 -0
- package/dist/infrastructure/persistence/postgres/eventCommitGc.d.ts +27 -0
- package/dist/infrastructure/persistence/postgres/eventCommitGc.js +62 -0
- package/dist/infrastructure/persistence/postgres/eventJournal.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/eventJournal.js +76 -0
- package/dist/infrastructure/persistence/postgres/eventJournalSchema.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/eventJournalSchema.js +253 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +24 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +111 -0
- package/dist/infrastructure/persistence/postgres/eventRetention.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/eventRetention.js +76 -0
- package/dist/infrastructure/persistence/postgres/events.d.ts +45 -0
- package/dist/infrastructure/persistence/postgres/events.js +249 -0
- package/dist/infrastructure/persistence/postgres/expiry.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/expiry.js +74 -0
- package/dist/infrastructure/persistence/postgres/flowFailures.d.ts +17 -0
- package/dist/infrastructure/persistence/postgres/flowFailures.js +199 -0
- package/dist/infrastructure/persistence/postgres/jobData.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/jobData.js +32 -0
- package/dist/infrastructure/persistence/postgres/leaseRelease.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/leaseRelease.js +120 -0
- package/dist/infrastructure/persistence/postgres/leaseRenewal.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/leaseRenewal.js +35 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetrics.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetrics.js +46 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/lifetimeMetricsFinalizer.js +13 -0
- package/dist/infrastructure/persistence/postgres/logs.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/logs.js +70 -0
- package/dist/infrastructure/persistence/postgres/maintenance.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/maintenance.js +138 -0
- package/dist/infrastructure/persistence/postgres/maintenanceFlights.d.ts +14 -0
- package/dist/infrastructure/persistence/postgres/maintenanceFlights.js +66 -0
- package/dist/infrastructure/persistence/postgres/metricWrites.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/metricWrites.js +94 -0
- package/dist/infrastructure/persistence/postgres/mutations.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/mutations.js +171 -0
- package/dist/infrastructure/persistence/postgres/outcomes.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/outcomes.js +194 -0
- package/dist/infrastructure/persistence/postgres/policies.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/policies.js +18 -0
- package/dist/infrastructure/persistence/postgres/postCommitMaintenance.d.ts +17 -0
- package/dist/infrastructure/persistence/postgres/postCommitMaintenance.js +117 -0
- package/dist/infrastructure/persistence/postgres/promotion.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/promotion.js +43 -0
- package/dist/infrastructure/persistence/postgres/queries.d.ts +22 -0
- package/dist/infrastructure/persistence/postgres/queries.js +152 -0
- package/dist/infrastructure/persistence/postgres/queueDestruction.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/queueDestruction.js +154 -0
- package/dist/infrastructure/persistence/postgres/queueLifecycle.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/queueLifecycle.js +60 -0
- package/dist/infrastructure/persistence/postgres/rateLimit.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/rateLimit.js +10 -0
- package/dist/infrastructure/persistence/postgres/readModels.d.ts +32 -0
- package/dist/infrastructure/persistence/postgres/readModels.js +72 -0
- package/dist/infrastructure/persistence/postgres/recovery.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/recovery.js +178 -0
- package/dist/infrastructure/persistence/postgres/relationships.d.ts +9 -0
- package/dist/infrastructure/persistence/postgres/relationships.js +201 -0
- package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/removeUnprocessedChildren.js +241 -0
- package/dist/infrastructure/persistence/postgres/repeatMutations.d.ts +5 -0
- package/dist/infrastructure/persistence/postgres/repeatMutations.js +37 -0
- package/dist/infrastructure/persistence/postgres/runtime.d.ts +46 -0
- package/dist/infrastructure/persistence/postgres/runtime.js +236 -0
- package/dist/infrastructure/persistence/postgres/runtimeConfig.d.ts +21 -0
- package/dist/infrastructure/persistence/postgres/runtimeConfig.js +39 -0
- package/dist/infrastructure/persistence/postgres/schema.d.ts +4 -0
- package/dist/infrastructure/persistence/postgres/schema.js +257 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.js +221 -0
- package/dist/infrastructure/persistence/postgres/serialAdmission.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/serialAdmission.js +96 -0
- package/dist/infrastructure/persistence/postgres/snapshotBudget.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/snapshotBudget.js +83 -0
- package/dist/infrastructure/persistence/postgres/storageHealth.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/storageHealth.js +29 -0
- package/dist/infrastructure/persistence/postgres/store.d.ts +90 -0
- package/dist/infrastructure/persistence/postgres/store.js +136 -0
- package/dist/infrastructure/persistence/postgres/telemetry.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/telemetry.js +88 -0
- package/dist/infrastructure/persistence/postgres/terminal.d.ts +8 -0
- package/dist/infrastructure/persistence/postgres/terminal.js +137 -0
- package/dist/infrastructure/persistence/postgres/transactionRetry.d.ts +6 -0
- package/dist/infrastructure/persistence/postgres/transactionRetry.js +42 -0
- package/dist/infrastructure/persistence/postgres/types.d.ts +175 -0
- package/dist/infrastructure/persistence/postgres/types.js +1 -0
- package/dist/infrastructure/persistence/postgres/workers.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/workers.js +126 -0
- package/dist/infrastructure/persistence/postgres.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres.js +1 -0
- package/dist/infrastructure/persistence/sqlite/jobs.js +2 -0
- package/dist/infrastructure/persistence/sqlite.d.ts +1 -1
- package/dist/infrastructure/scheduler/cron/persisted.d.ts +5 -0
- package/dist/infrastructure/scheduler/cron/persisted.js +27 -0
- package/dist/infrastructure/scheduler/cron/runtime.js +5 -10
- package/dist/infrastructure/scheduler/cron/validation.d.ts +3 -0
- package/dist/infrastructure/scheduler/cron/validation.js +11 -0
- package/dist/infrastructure/scheduler/cronParser.d.ts +1 -1
- package/dist/infrastructure/scheduler/cronParser.js +144 -11
- package/dist/infrastructure/server/bootstrap.d.ts +2 -3
- package/dist/infrastructure/server/bootstrap.js +33 -59
- package/dist/infrastructure/server/errors.d.ts +4 -0
- package/dist/infrastructure/server/errors.js +54 -0
- package/dist/infrastructure/server/handler-routes/control.d.ts +5 -5
- package/dist/infrastructure/server/handler-routes/control.js +3 -1
- package/dist/infrastructure/server/handler-routes/jobs.js +0 -3
- package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
- package/dist/infrastructure/server/handler.js +9 -11
- package/dist/infrastructure/server/handlerRoutes.d.ts +2 -2
- package/dist/infrastructure/server/handlerRoutes.js +2 -2
- package/dist/infrastructure/server/handlers/advanced/dependencies.js +7 -6
- package/dist/infrastructure/server/handlers/advanced/jobs.js +19 -6
- package/dist/infrastructure/server/handlers/advanced/queue.d.ts +9 -9
- package/dist/infrastructure/server/handlers/advanced/queue.js +91 -27
- package/dist/infrastructure/server/handlers/core.js +32 -31
- package/dist/infrastructure/server/handlers/cron.d.ts +4 -4
- package/dist/infrastructure/server/handlers/cron.js +60 -59
- package/dist/infrastructure/server/handlers/dashboard.d.ts +1 -1
- package/dist/infrastructure/server/handlers/dashboard.js +21 -8
- package/dist/infrastructure/server/handlers/dlq.d.ts +6 -3
- package/dist/infrastructure/server/handlers/dlq.js +35 -9
- package/dist/infrastructure/server/handlers/flow.js +2 -1
- package/dist/infrastructure/server/handlers/management.d.ts +5 -5
- package/dist/infrastructure/server/handlers/management.js +36 -9
- package/dist/infrastructure/server/handlers/monitoring/health.d.ts +5 -5
- package/dist/infrastructure/server/handlers/monitoring/health.js +45 -16
- package/dist/infrastructure/server/handlers/monitoring/operations.d.ts +1 -1
- package/dist/infrastructure/server/handlers/monitoring/operations.js +8 -1
- package/dist/infrastructure/server/handlers/monitoring/workers.d.ts +3 -3
- package/dist/infrastructure/server/handlers/monitoring/workers.js +31 -7
- package/dist/infrastructure/server/handlers/pushBatchValidation.d.ts +3 -1
- package/dist/infrastructure/server/handlers/pushBatchValidation.js +52 -7
- package/dist/infrastructure/server/handlers/query.d.ts +1 -1
- package/dist/infrastructure/server/handlers/query.js +5 -4
- package/dist/infrastructure/server/http.js +2 -2
- package/dist/infrastructure/server/httpDashboardEndpoints.d.ts +1 -1
- package/dist/infrastructure/server/httpDashboardEndpoints.js +19 -6
- package/dist/infrastructure/server/httpEndpoints.js +20 -7
- package/dist/infrastructure/server/httpRouteQueues.js +11 -1
- package/dist/infrastructure/server/shutdownCoordinator.d.ts +24 -0
- package/dist/infrastructure/server/shutdownCoordinator.js +106 -0
- package/dist/infrastructure/server/sseHandler.js +8 -3
- package/dist/infrastructure/server/storageAdapter.d.ts +29 -0
- package/dist/infrastructure/server/storageAdapter.js +61 -0
- package/dist/infrastructure/server/storageManager.d.ts +12 -0
- package/dist/infrastructure/server/storageManager.js +102 -0
- package/dist/infrastructure/server/tcp.js +2 -3
- package/dist/infrastructure/server/ws/snapshots.d.ts +1 -1
- package/dist/infrastructure/server/ws/snapshots.js +19 -4
- package/dist/infrastructure/server/wsHandler.js +10 -5
- package/dist/main.js +1 -1
- package/dist/mcp/backend/embedded/services.js +2 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +2 -2
- package/dist/shared/storageHealth.d.ts +9 -0
- package/dist/shared/storageHealth.js +12 -0
- package/package.json +85 -74
|
@@ -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();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SqliteLifecycle } from './sqlite/lifecycle';
|
|
2
|
-
export type { SqliteConfig, SqliteCriticalLoss, SqliteCriticalLossCallback
|
|
2
|
+
export type { SqliteConfig, SqliteCriticalLoss, SqliteCriticalLossCallback } from './types/sqlite';
|
|
3
3
|
export type { DurableAdmissionMetadata } from './types/admission';
|
|
4
4
|
/** SQLite persistence façade. Implementation is split by responsibility. */
|
|
5
5
|
export declare class SqliteStorage extends SqliteLifecycle {
|
|
@@ -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
|
|
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
|
-
|
|
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,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,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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
76
|
-
|
|
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 (
|
|
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 (
|
|
209
|
+
if (usesCalendarShortcuts &&
|
|
210
|
+
minute === '0' &&
|
|
211
|
+
hour === '*' &&
|
|
212
|
+
dayOfMonth === '*' &&
|
|
213
|
+
month === '*' &&
|
|
214
|
+
dayOfWeek === '*') {
|
|
85
215
|
return 'Every hour';
|
|
86
216
|
}
|
|
87
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
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',
|
|
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
|
|
86
|
-
if (
|
|
87
|
-
serverLog.error(
|
|
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
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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>;
|