bunqueue 2.8.60 → 2.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -78
- package/dist/application/background/dlq.js +2 -0
- package/dist/application/background/recovery/active.d.ts +1 -0
- package/dist/application/background/recovery/active.js +12 -0
- package/dist/application/background/recovery/index.js +3 -1
- package/dist/application/background/recovery/pending.js +1 -0
- package/dist/application/cleanupTasks.js +4 -16
- package/dist/application/contextFactory.js +8 -0
- package/dist/application/dlqManager.d.ts +10 -0
- package/dist/application/dlqManager.js +48 -9
- package/dist/application/dlqRetry.js +60 -38
- package/dist/application/eventsManager.d.ts +9 -2
- package/dist/application/eventsManager.js +106 -55
- package/dist/application/operations/ackHelpers.d.ts +1 -4
- package/dist/application/operations/ackHelpers.js +39 -14
- package/dist/application/operations/flowPush.js +1 -0
- package/dist/application/operations/flowValidation.js +4 -4
- package/dist/application/operations/jobManagement.d.ts +4 -0
- package/dist/application/operations/jobMoveOperations.js +9 -4
- package/dist/application/operations/pull.d.ts +7 -2
- package/dist/application/operations/pull.js +15 -57
- package/dist/application/operations/pullFinalization.d.ts +6 -0
- package/dist/application/operations/pullFinalization.js +69 -0
- package/dist/application/operations/pullStateTransition.d.ts +8 -7
- package/dist/application/operations/pullStateTransition.js +25 -18
- package/dist/application/operations/push.js +3 -0
- package/dist/application/operations/pushBatch.js +14 -7
- package/dist/application/operations/pushContext.d.ts +2 -1
- 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 +30 -0
- package/dist/application/postgres-queue-manager/control.js +210 -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 +20 -0
- package/dist/application/postgres-queue-manager/delivery.js +83 -0
- package/dist/application/postgres-queue-manager/groupQueries.d.ts +10 -0
- package/dist/application/postgres-queue-manager/groupQueries.js +40 -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/control.js +1 -0
- package/dist/application/queue-manager/delivery.d.ts +5 -4
- package/dist/application/queue-manager/delivery.js +15 -8
- package/dist/application/queue-manager/flow-failures.js +4 -0
- package/dist/application/queue-manager/limits.d.ts +13 -0
- package/dist/application/queue-manager/limits.js +65 -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 +17 -2
- package/dist/application/queueTelemetryJournal.d.ts +2 -0
- package/dist/application/queueTelemetryJournal.js +23 -0
- package/dist/application/types/ack.d.ts +3 -1
- package/dist/cli/commandRouter.d.ts +1 -1
- package/dist/cli/commands/server.js +1 -1
- package/dist/client/flowPush.js +6 -0
- package/dist/client/forwarder.d.ts +6 -4
- package/dist/client/forwarder.js +5 -3
- package/dist/client/groupId.d.ts +1 -0
- package/dist/client/groupId.js +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/jobHelpers.js +1 -0
- 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/bulk.js +4 -4
- package/dist/client/queue/operations/add/payload.d.ts +1 -0
- package/dist/client/queue/operations/add/payload.js +7 -1
- package/dist/client/queue/operations/add/single.js +3 -3
- package/dist/client/queue/operations/groups.d.ts +21 -0
- package/dist/client/queue/operations/groups.js +88 -0
- package/dist/client/queue/runtime/configuration.d.ts +4 -0
- package/dist/client/queue/runtime/configuration.js +8 -0
- package/dist/client/queue/runtime/queries.d.ts +13 -0
- package/dist/client/queue/runtime/queries.js +31 -0
- package/dist/client/queue/runtime/state.d.ts +4 -0
- package/dist/client/queue/runtime/state.js +1 -0
- package/dist/client/types/index.d.ts +2 -2
- package/dist/client/types/options.d.ts +4 -0
- package/dist/client/types/worker.d.ts +8 -0
- package/dist/client/worker/runtime/execution.js +4 -8
- package/dist/client/worker/runtime/options.js +3 -0
- package/dist/client/worker/runtime/polling.d.ts +1 -0
- package/dist/client/worker/runtime/polling.js +9 -0
- package/dist/client/worker/types/options.d.ts +2 -1
- package/dist/client/worker/workerPull.d.ts +2 -0
- package/dist/client/worker/workerPull.js +6 -4
- 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/job/groupFifoOrder.d.ts +9 -0
- package/dist/domain/job/groupFifoOrder.js +26 -0
- package/dist/domain/queue/dlqShard.d.ts +2 -0
- package/dist/domain/queue/dlqShard.js +16 -0
- package/dist/domain/queue/groupLimiterManager.d.ts +23 -0
- package/dist/domain/queue/groupLimiterManager.js +107 -0
- package/dist/domain/queue/groupScheduler.d.ts +31 -0
- package/dist/domain/queue/groupScheduler.js +256 -0
- package/dist/domain/queue/groupSchedulerOrder.d.ts +6 -0
- package/dist/domain/queue/groupSchedulerOrder.js +30 -0
- package/dist/domain/queue/priorityQueue.d.ts +13 -23
- package/dist/domain/queue/priorityQueue.js +53 -42
- package/dist/domain/queue/shard/dlq.d.ts +1 -0
- package/dist/domain/queue/shard/dlq.js +3 -0
- package/dist/domain/queue/shard/keys.d.ts +1 -0
- package/dist/domain/queue/shard/keys.js +25 -2
- package/dist/domain/queue/shard/lifecycle.js +4 -0
- package/dist/domain/queue/shard/limits.d.ts +20 -1
- package/dist/domain/queue/shard/limits.js +56 -0
- package/dist/domain/queue/shard/state.d.ts +5 -0
- package/dist/domain/queue/shard/state.js +9 -1
- package/dist/domain/types/commands/core.d.ts +3 -0
- package/dist/domain/types/commands/dlq.d.ts +5 -0
- package/dist/domain/types/commands/limits.d.ts +41 -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/group.d.ts +21 -0
- package/dist/domain/types/group.js +78 -0
- package/dist/domain/types/jobs/model.d.ts +1 -1
- package/dist/domain/types/priorityQueue.d.ts +1 -0
- 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/jobOptionsBlob.d.ts +1 -0
- package/dist/infrastructure/persistence/jobOptionsBlob.js +10 -2
- package/dist/infrastructure/persistence/migrations.js +11 -0
- package/dist/infrastructure/persistence/postgres/admission.d.ts +18 -0
- package/dist/infrastructure/persistence/postgres/admission.js +196 -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 +128 -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 +14 -0
- package/dist/infrastructure/persistence/postgres/claimBatch.js +69 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/claimSelection.js +140 -0
- package/dist/infrastructure/persistence/postgres/claims.d.ts +10 -0
- package/dist/infrastructure/persistence/postgres/claims.js +153 -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/eventCatchupCursors.d.ts +32 -0
- package/dist/infrastructure/persistence/postgres/eventCatchupCursors.js +66 -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 +267 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.d.ts +26 -0
- package/dist/infrastructure/persistence/postgres/eventPruneWatermarks.js +129 -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 +276 -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/groupClaims.d.ts +15 -0
- package/dist/infrastructure/persistence/postgres/groupClaims.js +95 -0
- package/dist/infrastructure/persistence/postgres/groupSchema.d.ts +2 -0
- package/dist/infrastructure/persistence/postgres/groupSchema.js +155 -0
- package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/groupSchemaFingerprint.js +127 -0
- package/dist/infrastructure/persistence/postgres/groupStateRetention.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/groupStateRetention.js +93 -0
- package/dist/infrastructure/persistence/postgres/groups.d.ts +11 -0
- package/dist/infrastructure/persistence/postgres/groups.js +117 -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 +158 -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 +45 -0
- package/dist/infrastructure/persistence/postgres/runtime.js +238 -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 +261 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.d.ts +3 -0
- package/dist/infrastructure/persistence/postgres/schemaInitialization.js +226 -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 +100 -0
- package/dist/infrastructure/persistence/postgres/store.js +180 -0
- package/dist/infrastructure/persistence/postgres/telemetry.d.ts +8 -0
- package/dist/infrastructure/persistence/postgres/telemetry.js +100 -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 +176 -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/schema.d.ts +2 -2
- package/dist/infrastructure/persistence/schema.js +12 -1
- package/dist/infrastructure/persistence/sqlite/control.d.ts +14 -0
- package/dist/infrastructure/persistence/sqlite/control.js +69 -0
- package/dist/infrastructure/persistence/sqlite/jobs.d.ts +12 -0
- package/dist/infrastructure/persistence/sqlite/jobs.js +38 -0
- package/dist/infrastructure/persistence/sqlite/telemetry.d.ts +2 -1
- package/dist/infrastructure/persistence/sqlite/telemetry.js +5 -64
- package/dist/infrastructure/persistence/sqlite/telemetryWrites.d.ts +4 -0
- package/dist/infrastructure/persistence/sqlite/telemetryWrites.js +123 -0
- package/dist/infrastructure/persistence/sqliteSerializer.js +2 -0
- 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 +16 -1
- package/dist/infrastructure/server/handler-routes/monitoring.d.ts +2 -2
- package/dist/infrastructure/server/handler.js +9 -11
- 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.d.ts +0 -5
- package/dist/infrastructure/server/handlers/core.js +47 -49
- 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/groups.d.ts +11 -0
- package/dist/infrastructure/server/handlers/groups.js +55 -0
- 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 +56 -8
- 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 +18 -7
- package/dist/infrastructure/server/httpRouteQueues.js +11 -1
- package/dist/infrastructure/server/protocol/validation.d.ts +1 -0
- package/dist/infrastructure/server/protocol/validation.js +1 -0
- package/dist/infrastructure/server/protocol.d.ts +1 -1
- package/dist/infrastructure/server/protocol.js +1 -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 +75 -65
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type CronJob } from '../../../domain/types/cron';
|
|
2
|
+
/** Reject persisted Croner-only syntax before any scheduler state is mutated. */
|
|
3
|
+
export declare function assertPersistedCronSupported(cron: CronJob): void;
|
|
4
|
+
/** Validate a persisted collection atomically before loading any definition. */
|
|
5
|
+
export declare function assertPersistedCronsSupported(crons: readonly CronJob[]): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { assertValidCronTiming } from '../../../domain/types/cron';
|
|
2
|
+
import { expandCronShortcut, validateCronExpression } from '../cronParser';
|
|
3
|
+
const NATIVE_CRON_MIGRATION_VERSION = '2.9.0';
|
|
4
|
+
/** Reject persisted Croner-only syntax before any scheduler state is mutated. */
|
|
5
|
+
export function assertPersistedCronSupported(cron) {
|
|
6
|
+
try {
|
|
7
|
+
assertValidCronTiming(cron);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11
|
+
throw new Error(`Persisted cron ${JSON.stringify(cron.name)} has invalid timing: ${message}. ` +
|
|
12
|
+
`Update or remove it before upgrading to bunqueue ${NATIVE_CRON_MIGRATION_VERSION}.`);
|
|
13
|
+
}
|
|
14
|
+
if (!cron.schedule)
|
|
15
|
+
return;
|
|
16
|
+
const error = validateCronExpression(expandCronShortcut(cron.schedule), cron.timezone ?? undefined);
|
|
17
|
+
if (!error)
|
|
18
|
+
return;
|
|
19
|
+
throw new Error(`Persisted cron ${JSON.stringify(cron.name)} uses unsupported schedule ${JSON.stringify(cron.schedule)}. ` +
|
|
20
|
+
`Update or remove it before upgrading to bunqueue ${NATIVE_CRON_MIGRATION_VERSION}. ` +
|
|
21
|
+
`Bun.cron validation failed: ${error}`);
|
|
22
|
+
}
|
|
23
|
+
/** Validate a persisted collection atomically before loading any definition. */
|
|
24
|
+
export function assertPersistedCronsSupported(crons) {
|
|
25
|
+
for (const cron of crons)
|
|
26
|
+
assertPersistedCronSupported(cron);
|
|
27
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createCronJob } from '../../../domain/types/cron';
|
|
2
2
|
import { MinHeap } from '../../../shared/minHeap';
|
|
3
|
-
import { expandCronShortcut, getNextCronRun, getNextIntervalRun
|
|
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>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const SQLSTATE_CODE = /^(?:[0-9][0-9A-Z]|F0|HV|P0|XX)[0-9A-Z]{3}$/;
|
|
2
|
+
const INFRASTRUCTURE_CODE = /^(?:SQLITE|POSTGRES|ECONN|EAI_|ENET|EHOST|ETIMEDOUT|EPIPE|ENOTFOUND|EADDR|UND_ERR)/i;
|
|
3
|
+
const INFRASTRUCTURE_MESSAGE = /(?:SQLITE|Postgres(?:SQL)?|SQLSTATE|database|duplicate key|\bconstraint\b|\brelation\b|\bdriver\b|connection (?:refused|reset|terminated)|getaddrinfo|\b(?:ECONN|EAI_|ENET|EHOST|ETIMEDOUT|EPIPE|ENOTFOUND|EADDR|UND_ERR)[A-Z_]*)/i;
|
|
4
|
+
const HOST_ENDPOINT_MESSAGE = /(?:\b(?:localhost|[a-z0-9-]+(?:\.[a-z0-9-]+)+|\d{1,3}(?:\.\d{1,3}){3}):\d{2,5}\b|\[[0-9a-f:]+\]:\d{2,5})/i;
|
|
5
|
+
const MAX_DIAGNOSTIC_LENGTH = 2_000;
|
|
6
|
+
function errorField(error, field) {
|
|
7
|
+
if (typeof error !== 'object' || error === null || !(field in error))
|
|
8
|
+
return '';
|
|
9
|
+
const value = error[field];
|
|
10
|
+
return typeof value === 'string' ? value : '';
|
|
11
|
+
}
|
|
12
|
+
function bounded(value) {
|
|
13
|
+
return value.length <= MAX_DIAGNOSTIC_LENGTH
|
|
14
|
+
? value
|
|
15
|
+
: `${value.slice(0, MAX_DIAGNOSTIC_LENGTH)}...`;
|
|
16
|
+
}
|
|
17
|
+
function errorMessage(error) {
|
|
18
|
+
if (error instanceof Error)
|
|
19
|
+
return error.message;
|
|
20
|
+
if (typeof error === 'string')
|
|
21
|
+
return error;
|
|
22
|
+
if (typeof error !== 'object' || error === null || !('message' in error))
|
|
23
|
+
return 'Unknown error';
|
|
24
|
+
const message = error['message'];
|
|
25
|
+
return typeof message === 'string' ? message : 'Unknown error';
|
|
26
|
+
}
|
|
27
|
+
/** Preserve actionable domain errors while redacting storage and network diagnostics. */
|
|
28
|
+
export function sanitizeServerError(error) {
|
|
29
|
+
const message = errorMessage(error);
|
|
30
|
+
const code = errorField(error, 'code');
|
|
31
|
+
const name = errorField(error, 'name');
|
|
32
|
+
const infrastructureError = SQLSTATE_CODE.test(code) ||
|
|
33
|
+
INFRASTRUCTURE_CODE.test(code) ||
|
|
34
|
+
/^(?:(?:Database|Postgres|SQLite|SQL).*Error|ConnectionError)$/i.test(name) ||
|
|
35
|
+
INFRASTRUCTURE_MESSAGE.test(message) ||
|
|
36
|
+
HOST_ENDPOINT_MESSAGE.test(message);
|
|
37
|
+
return infrastructureError ? 'Internal server error' : message;
|
|
38
|
+
}
|
|
39
|
+
/** Keep bounded local diagnostics for redacted infrastructure failures. */
|
|
40
|
+
export function serverErrorDiagnostics(error) {
|
|
41
|
+
const diagnostic = { error: bounded(errorMessage(error)) };
|
|
42
|
+
for (const [source, target] of [
|
|
43
|
+
['name', 'name'],
|
|
44
|
+
['code', 'code'],
|
|
45
|
+
['errno', 'sqlState'],
|
|
46
|
+
['where', 'where'],
|
|
47
|
+
['routine', 'routine'],
|
|
48
|
+
]) {
|
|
49
|
+
const value = errorField(error, source);
|
|
50
|
+
if (value)
|
|
51
|
+
diagnostic[target] = bounded(value);
|
|
52
|
+
}
|
|
53
|
+
return diagnostic;
|
|
54
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Command } from '../../../domain/types/command';
|
|
2
2
|
import type { Response } from '../../../domain/types/response';
|
|
3
3
|
import type { HandlerContext } from '../types';
|
|
4
|
-
export declare function routeQueueControlCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
5
|
-
export declare function routeDlqCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
6
|
-
export declare function routeRateLimitCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
7
|
-
export declare function routeCronCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
8
|
-
export declare function routeConfigCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
4
|
+
export declare function routeQueueControlCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
5
|
+
export declare function routeDlqCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
6
|
+
export declare function routeRateLimitCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
7
|
+
export declare function routeCronCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
8
|
+
export declare function routeConfigCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { handleClean, handleClearConcurrency, handleGetDlqConfig, handleGetStallConfig, handleIsPaused, handleListQueues, handleObliterate, handleRateLimit, handleRateLimitClear, handleSetConcurrency, handleSetDlqConfig, handleSetStallConfig, } from '../handlers/advanced';
|
|
2
2
|
import { handleCron, handleCronDelete, handleCronGet, handleCronList } from '../handlers/cron';
|
|
3
|
-
import { handleDlq, handleGetDlqStats, handlePurgeDlq, handleRetryCompleted, handleRetryDlq, } from '../handlers/dlq';
|
|
3
|
+
import { handleDlq, handleGetDlqStats, handlePurgeDlq, handleRemoveDlqJob, handleRetryCompleted, handleRetryDlq, } from '../handlers/dlq';
|
|
4
4
|
import { handleDrain, handlePause, handleResume } from '../handlers/management';
|
|
5
|
+
import { handleGroupCommand, handleGroupsJobsCount } from '../handlers/groups';
|
|
5
6
|
export function routeQueueControlCommand(command, context, requestId) {
|
|
6
7
|
switch (command.cmd) {
|
|
7
8
|
case 'Pause':
|
|
@@ -32,6 +33,8 @@ export function routeDlqCommand(command, context, requestId) {
|
|
|
32
33
|
return handleRetryDlq(command, context, requestId);
|
|
33
34
|
case 'PurgeDlq':
|
|
34
35
|
return handlePurgeDlq(command, context, requestId);
|
|
36
|
+
case 'RemoveDlqJob':
|
|
37
|
+
return handleRemoveDlqJob(command, context, requestId);
|
|
35
38
|
case 'RetryCompleted':
|
|
36
39
|
return handleRetryCompleted(command, context, requestId);
|
|
37
40
|
default:
|
|
@@ -40,6 +43,18 @@ export function routeDlqCommand(command, context, requestId) {
|
|
|
40
43
|
}
|
|
41
44
|
export function routeRateLimitCommand(command, context, requestId) {
|
|
42
45
|
switch (command.cmd) {
|
|
46
|
+
case 'GetGroupsJobsCount':
|
|
47
|
+
return handleGroupsJobsCount(command, context, requestId);
|
|
48
|
+
case 'GetGroupJobsCount':
|
|
49
|
+
case 'GetGroupActiveCount':
|
|
50
|
+
case 'SetGroupRateLimit':
|
|
51
|
+
case 'GetGroupRateLimit':
|
|
52
|
+
case 'RemoveGroupRateLimit':
|
|
53
|
+
case 'GetGroupRateLimitTtl':
|
|
54
|
+
case 'SetGroupConcurrency':
|
|
55
|
+
case 'GetGroupConcurrency':
|
|
56
|
+
case 'RemoveGroupConcurrency':
|
|
57
|
+
return handleGroupCommand(command, context, requestId);
|
|
43
58
|
case 'RateLimit':
|
|
44
59
|
return handleRateLimit(command, context, requestId);
|
|
45
60
|
case 'RateLimitClear':
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from '../../../domain/types/command';
|
|
2
2
|
import type { Response } from '../../../domain/types/response';
|
|
3
3
|
import type { HandlerContext } from '../types';
|
|
4
|
-
export declare function routeMonitoringCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
5
|
-
export declare function routeDashboardCommand(command: Command, context: HandlerContext, requestId?: string): Response | null;
|
|
4
|
+
export declare function routeMonitoringCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
5
|
+
export declare function routeDashboardCommand(command: Command, context: HandlerContext, requestId?: string): Response | Promise<Response> | null;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as resp from '../../domain/types/response';
|
|
6
6
|
import { constantTimeEqual } from '../../shared/hash';
|
|
7
|
+
import { sanitizeServerError } from './errors';
|
|
7
8
|
import { routeCoreCommand, routeQueryCommand, routeManagementCommand, routeQueueControlCommand, routeDlqCommand, routeRateLimitCommand, routeConfigCommand, routeCronCommand, routeMonitoringCommand, routeDashboardCommand, } from './handlerRoutes';
|
|
8
9
|
/**
|
|
9
10
|
* Handle authentication command
|
|
@@ -43,33 +44,30 @@ export async function handleCommand(cmd, ctx) {
|
|
|
43
44
|
result = await routeManagementCommand(cmd, ctx, reqId);
|
|
44
45
|
if (result)
|
|
45
46
|
return result;
|
|
46
|
-
result = routeQueueControlCommand(cmd, ctx, reqId);
|
|
47
|
+
result = await routeQueueControlCommand(cmd, ctx, reqId);
|
|
47
48
|
if (result)
|
|
48
49
|
return result;
|
|
49
|
-
result = routeDlqCommand(cmd, ctx, reqId);
|
|
50
|
+
result = await routeDlqCommand(cmd, ctx, reqId);
|
|
50
51
|
if (result)
|
|
51
52
|
return result;
|
|
52
|
-
result = routeRateLimitCommand(cmd, ctx, reqId);
|
|
53
|
+
result = await routeRateLimitCommand(cmd, ctx, reqId);
|
|
53
54
|
if (result)
|
|
54
55
|
return result;
|
|
55
|
-
result = routeConfigCommand(cmd, ctx, reqId);
|
|
56
|
+
result = await routeConfigCommand(cmd, ctx, reqId);
|
|
56
57
|
if (result)
|
|
57
58
|
return result;
|
|
58
|
-
result = routeCronCommand(cmd, ctx, reqId);
|
|
59
|
+
result = await routeCronCommand(cmd, ctx, reqId);
|
|
59
60
|
if (result)
|
|
60
61
|
return result;
|
|
61
|
-
result = routeMonitoringCommand(cmd, ctx, reqId);
|
|
62
|
+
result = await routeMonitoringCommand(cmd, ctx, reqId);
|
|
62
63
|
if (result)
|
|
63
64
|
return result;
|
|
64
|
-
result = routeDashboardCommand(cmd, ctx, reqId);
|
|
65
|
+
result = await routeDashboardCommand(cmd, ctx, reqId);
|
|
65
66
|
if (result)
|
|
66
67
|
return result;
|
|
67
68
|
return resp.error(`Unknown command: ${cmd.cmd}`, reqId);
|
|
68
69
|
}
|
|
69
70
|
catch (err) {
|
|
70
|
-
|
|
71
|
-
const raw = err instanceof Error ? err.message : 'Unknown error';
|
|
72
|
-
const message = raw.includes('SQLITE') || raw.includes('database') ? 'Internal server error' : raw;
|
|
73
|
-
return resp.error(message, reqId);
|
|
71
|
+
return resp.error(sanitizeServerError(err), reqId);
|
|
74
72
|
}
|
|
75
73
|
}
|