@db3.ai/app 0.1.0-beta.1 → 0.1.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 +75 -17
- package/agent-instructions.md +76 -0
- package/dist/ai/AIErrors.d.ts +7 -1
- package/dist/ai/AIErrors.js +8 -1
- package/dist/ai/AIFailover.d.ts +10 -0
- package/dist/ai/AIFailover.js +3 -2
- package/dist/ai/AIProviderAdmission.d.ts +101 -0
- package/dist/ai/AIProviderAdmission.js +336 -0
- package/dist/ai/AIRateLimiter.d.ts +11 -2
- package/dist/ai/AIRateLimiter.js +125 -10
- package/dist/ai/AIRequestTrackingError.d.ts +22 -0
- package/dist/ai/AIRequestTrackingError.js +33 -0
- package/dist/ai/Agent.d.ts +7 -7
- package/dist/ai/Agent.js +84 -56
- package/dist/ai/Ai.d.ts +11 -3
- package/dist/ai/Ai.js +94 -44
- package/dist/ai/OpenAIProviderError.js +1 -1
- package/dist/ai/ProviderSseObserver.d.ts +19 -0
- package/dist/ai/ProviderSseObserver.js +98 -0
- package/dist/ai/aiServiceTier.d.ts +5 -0
- package/dist/ai/aiServiceTier.js +14 -0
- package/dist/ai/chunkEmbeddingText.d.ts +14 -0
- package/dist/ai/chunkEmbeddingText.js +147 -0
- package/dist/ai/contracts/AI.d.ts +16 -0
- package/dist/ai/contracts/AIRequestErrorDiagnostics.d.ts +12 -0
- package/dist/ai/contracts/AIRequestErrorDiagnostics.js +1 -0
- package/dist/ai/contracts/AIServiceTier.d.ts +2 -0
- package/dist/ai/contracts/AIServiceTier.js +1 -0
- package/dist/ai/contracts/Agent.d.ts +3 -0
- package/dist/ai/contracts/EmbeddingTextChunk.d.ts +25 -0
- package/dist/ai/contracts/EmbeddingTextChunk.js +1 -0
- package/dist/ai/contracts/Pricing.d.ts +7 -1
- package/dist/ai/contracts/Providers.d.ts +2 -0
- package/dist/ai/contracts/index.d.ts +2 -0
- package/dist/ai/contracts/index.js +1 -0
- package/dist/ai/index.d.ts +8 -2
- package/dist/ai/index.js +5 -2
- package/dist/ai/{Models.d.ts → modelPricing.d.ts} +2 -2
- package/dist/ai/{Models.js → modelPricing.js} +38 -4
- package/dist/apps/AppInstallation.d.ts +21 -0
- package/dist/apps/AppInstallation.js +16 -0
- package/dist/apps/AppRouteError.d.ts +6 -0
- package/dist/apps/AppRouteError.js +10 -0
- package/dist/apps/AppService.d.ts +24 -0
- package/dist/apps/AppService.js +21 -0
- package/dist/apps/Apps.d.ts +59 -0
- package/dist/apps/Apps.js +566 -0
- package/dist/apps/appsPlugin.d.ts +3 -0
- package/dist/apps/appsPlugin.js +36 -0
- package/dist/apps/commands/disableApp.d.ts +2 -0
- package/dist/apps/commands/disableApp.js +5 -0
- package/dist/apps/commands/enableApp.d.ts +2 -0
- package/dist/apps/commands/enableApp.js +5 -0
- package/dist/apps/commands/index.d.ts +10 -0
- package/dist/apps/commands/index.js +27 -0
- package/dist/apps/commands/installApp.d.ts +2 -0
- package/dist/apps/commands/installApp.js +5 -0
- package/dist/apps/commands/listApps.d.ts +2 -0
- package/dist/apps/commands/listApps.js +5 -0
- package/dist/apps/commands/makeAppMigration.d.ts +2 -0
- package/dist/apps/commands/makeAppMigration.js +9 -0
- package/dist/apps/commands/rollbackApp.d.ts +2 -0
- package/dist/apps/commands/rollbackApp.js +5 -0
- package/dist/apps/commands/uninstallApp.d.ts +2 -0
- package/dist/apps/commands/uninstallApp.js +5 -0
- package/dist/apps/contracts/AppAction.d.ts +2 -0
- package/dist/apps/contracts/AppAction.js +1 -0
- package/dist/apps/contracts/AppDefinition.d.ts +45 -0
- package/dist/apps/contracts/AppDefinition.js +1 -0
- package/dist/apps/contracts/AppDescription.d.ts +32 -0
- package/dist/apps/contracts/AppDescription.js +1 -0
- package/dist/apps/contracts/AppManifest.d.ts +30 -0
- package/dist/apps/contracts/AppManifest.js +1 -0
- package/dist/apps/contracts/AppNavigation.d.ts +9 -0
- package/dist/apps/contracts/AppNavigation.js +1 -0
- package/dist/apps/contracts/AppNavigationBadge.d.ts +6 -0
- package/dist/apps/contracts/AppNavigationBadge.js +1 -0
- package/dist/apps/contracts/AppNavigationContext.d.ts +8 -0
- package/dist/apps/contracts/AppNavigationContext.js +1 -0
- package/dist/apps/contracts/AppNavigationResult.d.ts +12 -0
- package/dist/apps/contracts/AppNavigationResult.js +1 -0
- package/dist/apps/contracts/AppRoute.d.ts +19 -0
- package/dist/apps/contracts/AppRoute.js +1 -0
- package/dist/apps/contracts/DiscoveredApp.d.ts +10 -0
- package/dist/apps/contracts/DiscoveredApp.js +1 -0
- package/dist/apps/contracts/InstalledApps.d.ts +3 -0
- package/dist/apps/contracts/InstalledApps.js +1 -0
- package/dist/apps/contracts/index.d.ts +11 -0
- package/dist/apps/contracts/index.js +1 -0
- package/dist/apps/defineApp.d.ts +7 -0
- package/dist/apps/defineApp.js +8 -0
- package/dist/apps/discoverApps.d.ts +3 -0
- package/dist/apps/discoverApps.js +62 -0
- package/dist/apps/fastify.d.ts +1 -0
- package/dist/apps/fastify.js +1 -0
- package/dist/apps/index.d.ts +7 -0
- package/dist/apps/index.js +7 -0
- package/dist/apps/normalizeAppNavigation.d.ts +3 -0
- package/dist/apps/normalizeAppNavigation.js +30 -0
- package/dist/apps/registerAppRoutes.d.ts +6 -0
- package/dist/apps/registerAppRoutes.js +52 -0
- package/dist/apps/validateAppDefinitions.d.ts +3 -0
- package/dist/apps/validateAppDefinitions.js +65 -0
- package/dist/apps/vite.d.ts +1 -0
- package/dist/apps/vite.js +1 -0
- package/dist/apps/writeAppTypes.d.ts +2 -0
- package/dist/apps/writeAppTypes.js +13 -0
- package/dist/auth/Auth.d.ts +18 -0
- package/dist/auth/Auth.js +89 -13
- package/dist/auth/PasswordLoginAttempt.d.ts +50 -0
- package/dist/auth/PasswordLoginAttempt.js +60 -0
- package/dist/auth/PasswordSuspendedError.d.ts +10 -0
- package/dist/auth/PasswordSuspendedError.js +13 -0
- package/dist/auth/contracts/PasswordAuthProviderOptions.d.ts +26 -0
- package/dist/auth/contracts/PasswordAuthProviderOptions.js +1 -0
- package/dist/auth/contracts/PasswordSuspension.d.ts +7 -0
- package/dist/auth/contracts/PasswordSuspension.js +1 -0
- package/dist/auth/contracts/PasswordSuspensionOptions.d.ts +10 -0
- package/dist/auth/contracts/PasswordSuspensionOptions.js +1 -0
- package/dist/auth/contracts/index.d.ts +3 -0
- package/dist/auth/contracts/index.js +3 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/providers/PasswordAuthProvider.d.ts +19 -23
- package/dist/auth/providers/PasswordAuthProvider.js +96 -8
- package/dist/auth/providers/resolveAuthProviders.js +1 -0
- package/dist/cli/Cli.d.ts +2 -0
- package/dist/cli/Cli.js +8 -0
- package/dist/cli/commands/repl.js +2 -1
- package/dist/cli/contracts/CliCommand.d.ts +2 -0
- package/dist/cli/contracts/CliCommandInfo.d.ts +12 -0
- package/dist/cli/contracts/CliCommandInfo.js +1 -0
- package/dist/cli/contracts/index.d.ts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/listDb3Commands.d.ts +8 -0
- package/dist/cli/listDb3Commands.js +10 -0
- package/dist/cli/runDb3.d.ts +3 -6
- package/dist/cli/runDb3.js +5 -59
- package/dist/cli/withDb3Cli.d.ts +4 -0
- package/dist/cli/withDb3Cli.js +60 -0
- package/dist/db/ActiveQueryBuilder.d.ts +8 -0
- package/dist/db/ActiveQueryBuilder.js +11 -0
- package/dist/db/ActiveRecord.d.ts +6 -0
- package/dist/db/ActiveRecord.js +8 -0
- package/dist/db/connection.js +14 -2
- package/dist/db/fields/TimestampField.d.ts +1 -1
- package/dist/db/fields/TimestampField.js +7 -12
- package/dist/db/migrations/DatabaseMigrationManager.d.ts +3 -1
- package/dist/db/migrations/DatabaseMigrationManager.js +9 -3
- package/dist/db/migrations/contracts/DatabaseMigration.d.ts +7 -0
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +1 -1
- package/dist/db/migrations/{KnexMigrationRenderer.d.ts → renderKnexMigration.d.ts} +4 -3
- package/dist/db/migrations/{KnexMigrationRenderer.js → renderKnexMigration.js} +7 -6
- package/dist/flows/FlowCompiler.js +1 -1
- package/dist/flows/Flows.js +1 -1
- package/dist/flows/index.d.ts +1 -1
- package/dist/flows/index.js +1 -1
- package/dist/health/Health.d.ts +41 -0
- package/dist/health/Health.js +153 -0
- package/dist/health/contracts/Health.d.ts +36 -0
- package/dist/health/contracts/Health.js +1 -0
- package/dist/health/contracts/HealthHttp.d.ts +15 -0
- package/dist/health/contracts/HealthHttp.js +1 -0
- package/dist/health/contracts/index.d.ts +2 -0
- package/dist/health/contracts/index.js +1 -0
- package/dist/health/databaseHealthCheck.d.ts +15 -0
- package/dist/health/databaseHealthCheck.js +18 -0
- package/dist/health/fastify.d.ts +13 -0
- package/dist/health/fastify.js +44 -0
- package/dist/health/index.d.ts +3 -0
- package/dist/health/index.js +2 -0
- package/dist/in-app/InApp.d.ts +33 -0
- package/dist/in-app/InApp.js +191 -0
- package/dist/in-app/InAppError.d.ts +6 -0
- package/dist/in-app/InAppError.js +10 -0
- package/dist/in-app/InAppRecord.d.ts +36 -0
- package/dist/in-app/InAppRecord.js +24 -0
- package/dist/in-app/contracts/InAppInbox.d.ts +45 -0
- package/dist/in-app/contracts/InAppInbox.js +1 -0
- package/dist/in-app/contracts/InAppMessage.d.ts +22 -0
- package/dist/in-app/contracts/InAppMessage.js +1 -0
- package/dist/in-app/contracts/InAppScope.d.ts +17 -0
- package/dist/in-app/contracts/InAppScope.js +1 -0
- package/dist/in-app/contracts/index.d.ts +3 -0
- package/dist/in-app/contracts/index.js +3 -0
- package/dist/in-app/index.d.ts +4 -0
- package/dist/in-app/index.js +4 -0
- package/dist/in-app/validation.d.ts +21 -0
- package/dist/in-app/validation.js +50 -0
- package/dist/index.d.ts +2 -14
- package/dist/index.js +2 -5
- package/dist/logging/DevelopmentConsole.d.ts +42 -0
- package/dist/logging/DevelopmentConsole.js +162 -0
- package/dist/logging/Log.d.ts +3 -1
- package/dist/logging/Log.js +5 -2
- package/dist/logging/PrettyLogFormatter.d.ts +16 -0
- package/dist/logging/PrettyLogFormatter.js +151 -0
- package/dist/logging/acquireDevelopmentConsole.d.ts +6 -0
- package/dist/logging/acquireDevelopmentConsole.js +21 -0
- package/dist/logging/contracts/DevelopmentConsoleOptions.d.ts +16 -0
- package/dist/logging/contracts/DevelopmentConsoleOptions.js +1 -0
- package/dist/logging/contracts/EmailLogOptions.d.ts +8 -0
- package/dist/logging/contracts/EmailLogOptions.js +1 -0
- package/dist/logging/contracts/HttpExchangeMonitorOptions.d.ts +12 -0
- package/dist/logging/contracts/HttpExchangeMonitorOptions.js +1 -0
- package/dist/logging/contracts/LogTransportOptions.d.ts +19 -0
- package/dist/logging/contracts/LogTransportOptions.js +1 -0
- package/dist/logging/contracts/LoggingOptions.d.ts +12 -1
- package/dist/logging/contracts/index.d.ts +4 -0
- package/dist/logging/contracts/index.js +4 -0
- package/dist/logging/drivers/PinoLoggerDriver.d.ts +3 -1
- package/dist/logging/drivers/PinoLoggerDriver.js +75 -16
- package/dist/logging/formatLogEmail.d.ts +4 -0
- package/dist/logging/formatLogEmail.js +21 -0
- package/dist/logging/highlightQueueMessage.d.ts +11 -0
- package/dist/logging/highlightQueueMessage.js +36 -0
- package/dist/logging/httpBodyCapture.d.ts +24 -0
- package/dist/logging/httpBodyCapture.js +321 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.js +2 -0
- package/dist/logging/redactLogEmail.d.ts +2 -0
- package/dist/logging/redactLogEmail.js +12 -0
- package/dist/logging/registerHttpExchangeMonitor.d.ts +10 -0
- package/dist/logging/registerHttpExchangeMonitor.js +43 -0
- package/dist/logging/transports/EmailLogTransport.d.ts +18 -0
- package/dist/logging/transports/EmailLogTransport.js +47 -0
- package/dist/mail/Mail.d.ts +2 -0
- package/dist/mail/Mail.js +3 -0
- package/dist/mail/MailDeliveryError.d.ts +9 -0
- package/dist/mail/MailDeliveryError.js +16 -0
- package/dist/mail/index.d.ts +1 -0
- package/dist/mail/index.js +1 -0
- package/dist/mail/transports/MailgunTransport.js +2 -0
- package/dist/mail/transports/ResendTransport.d.ts +3 -2
- package/dist/mail/transports/ResendTransport.js +22 -14
- package/dist/media/MediaManager.d.ts +4 -2
- package/dist/media/MediaManager.js +40 -8
- package/dist/network/BlockedUrlError.d.ts +16 -0
- package/dist/network/BlockedUrlError.js +19 -0
- package/dist/network/assertPublicUrl.d.ts +22 -0
- package/dist/network/assertPublicUrl.js +67 -0
- package/dist/network/contracts/GuardedFetch.d.ts +35 -0
- package/dist/network/contracts/GuardedFetch.js +1 -0
- package/dist/network/contracts/PublicUrlOptions.d.ts +17 -0
- package/dist/network/contracts/PublicUrlOptions.js +1 -0
- package/dist/network/contracts/index.d.ts +2 -0
- package/dist/network/contracts/index.js +2 -0
- package/dist/network/guardedFetch.d.ts +29 -0
- package/dist/network/guardedFetch.js +133 -0
- package/dist/network/index.d.ts +6 -0
- package/dist/network/index.js +6 -0
- package/dist/network/playwright/GuardedBrowserProxy.d.ts +32 -0
- package/dist/network/playwright/GuardedBrowserProxy.js +172 -0
- package/dist/network/playwright/contracts/GuardedBrowserContextOptions.d.ts +37 -0
- package/dist/network/playwright/contracts/GuardedBrowserContextOptions.js +1 -0
- package/dist/network/playwright/contracts/index.d.ts +1 -0
- package/dist/network/playwright/contracts/index.js +1 -0
- package/dist/network/playwright/index.d.ts +2 -0
- package/dist/network/playwright/index.js +2 -0
- package/dist/network/playwright/newGuardedBrowserContext.d.ts +21 -0
- package/dist/network/playwright/newGuardedBrowserContext.js +86 -0
- package/dist/network/privateNetworkAddresses.d.ts +44 -0
- package/dist/network/privateNetworkAddresses.js +157 -0
- package/dist/network/publicAddressLookup.d.ts +26 -0
- package/dist/network/publicAddressLookup.js +42 -0
- package/dist/notifications/Notifications.d.ts +27 -0
- package/dist/notifications/Notifications.js +99 -0
- package/dist/notifications/contracts/Notification.d.ts +56 -0
- package/dist/notifications/contracts/Notification.js +1 -0
- package/dist/notifications/contracts/NotificationDelivery.d.ts +11 -0
- package/dist/notifications/contracts/NotificationDelivery.js +1 -0
- package/dist/notifications/contracts/index.d.ts +2 -0
- package/dist/notifications/contracts/index.js +2 -0
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.js +2 -0
- package/dist/queue/JobRunAttempt.d.ts +16 -0
- package/dist/queue/JobRunAttempt.js +17 -0
- package/dist/queue/JobRunEvent.d.ts +15 -0
- package/dist/queue/JobRunEvent.js +16 -0
- package/dist/queue/JobRunStoppedError.d.ts +6 -0
- package/dist/queue/JobRunStoppedError.js +10 -0
- package/dist/queue/Queue.d.ts +19 -4
- package/dist/queue/Queue.js +122 -9
- package/dist/queue/QueueTerminalError.d.ts +9 -0
- package/dist/queue/QueueTerminalError.js +12 -0
- package/dist/queue/QueueWorker.d.ts +12 -5
- package/dist/queue/QueueWorker.js +94 -9
- package/dist/queue/QueueWorkerReporter.d.ts +17 -0
- package/dist/queue/QueueWorkerReporter.js +71 -0
- package/dist/queue/QueueableJob.d.ts +2 -2
- package/dist/queue/RunnableJob.d.ts +78 -0
- package/dist/queue/RunnableJob.js +284 -0
- package/dist/queue/console.d.ts +8 -0
- package/dist/queue/console.js +53 -19
- package/dist/queue/contracts/QueueDriver.d.ts +8 -0
- package/dist/queue/contracts/QueueJobRunner.d.ts +4 -0
- package/dist/queue/contracts/QueueJobRunner.js +1 -0
- package/dist/queue/contracts/QueuePayload.d.ts +1 -1
- package/dist/queue/contracts/QueueSelection.d.ts +11 -0
- package/dist/queue/contracts/QueueSelection.js +1 -0
- package/dist/queue/contracts/QueueService.d.ts +18 -4
- package/dist/queue/contracts/QueueWorkerLifecycle.d.ts +2 -2
- package/dist/queue/contracts/QueueWorkerSnapshot.d.ts +14 -0
- package/dist/queue/contracts/QueueWorkerSnapshot.js +1 -0
- package/dist/queue/contracts/QueueableJob.d.ts +7 -3
- package/dist/queue/contracts/QueueableJobContract.d.ts +12 -0
- package/dist/queue/contracts/QueueableJobContract.js +1 -0
- package/dist/queue/contracts/index.d.ts +4 -0
- package/dist/queue/contracts/index.js +2 -0
- package/dist/queue/drivers/DatabaseQueueDriver.d.ts +4 -0
- package/dist/queue/drivers/DatabaseQueueDriver.js +27 -3
- package/dist/queue/drivers/RedisQueueDriver.d.ts +7 -0
- package/dist/queue/drivers/RedisQueueDriver.js +37 -0
- package/dist/queue/index.d.ts +5 -0
- package/dist/queue/index.js +5 -0
- package/dist/queue/jobEnvelopeHasPrefix.d.ts +3 -0
- package/dist/queue/jobEnvelopeHasPrefix.js +4 -0
- package/dist/scheduler/ScheduledEvent.d.ts +8 -4
- package/dist/scheduler/ScheduledEvent.js +17 -3
- package/dist/scheduler/ScheduledJob.d.ts +1 -1
- package/dist/scheduler/Scheduler.d.ts +5 -0
- package/dist/scheduler/Scheduler.js +92 -28
- package/dist/scheduler/SchedulerCheckpointRecord.d.ts +16 -0
- package/dist/scheduler/SchedulerCheckpointRecord.js +53 -0
- package/dist/scheduler/SchedulerWorker.d.ts +3 -5
- package/dist/scheduler/SchedulerWorker.js +58 -14
- package/dist/scheduler/console.js +3 -1
- package/dist/scheduler/contracts/Schedule.d.ts +19 -0
- package/dist/scheduler/contracts/Schedule.js +1 -0
- package/dist/scheduler/contracts/Scheduler.d.ts +18 -6
- package/dist/scheduler/contracts/SchedulerCheckpoint.d.ts +23 -0
- package/dist/scheduler/contracts/SchedulerCheckpoint.js +1 -0
- package/dist/scheduler/contracts/SchedulerConsole.d.ts +5 -0
- package/dist/scheduler/contracts/SchedulerWorker.d.ts +6 -1
- package/dist/scheduler/contracts/index.d.ts +2 -0
- package/dist/scheduler/contracts/index.js +2 -0
- package/dist/scheduler/index.d.ts +1 -0
- package/dist/scheduler/index.js +1 -0
- package/dist/server/App.d.ts +36 -4
- package/dist/server/App.js +57 -7
- package/dist/server/contracts/HttpErrorHandlerOptions.d.ts +29 -0
- package/dist/server/contracts/HttpErrorHandlerOptions.js +1 -0
- package/dist/server/contracts/index.d.ts +1 -0
- package/dist/server/contracts/index.js +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +4 -0
- package/dist/server/publicServerErrorMessage.d.ts +11 -0
- package/dist/server/publicServerErrorMessage.js +16 -0
- package/dist/server/registerBrowserJsonFormatting.d.ts +16 -0
- package/dist/server/registerBrowserJsonFormatting.js +100 -0
- package/dist/server/registerHttpErrorHandler.d.ts +20 -0
- package/dist/server/registerHttpErrorHandler.js +93 -0
- package/dist/ssr/vite/contracts/ViteSsrOptions.d.ts +2 -0
- package/dist/ssr/vite/fastifyViteSsr.js +4 -3
- package/dist/storage/drivers/FlystorageDisk.js +2 -2
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +2 -2
- package/dist/websocket/WebSocketChannels.d.ts +18 -0
- package/dist/websocket/WebSocketChannels.js +129 -0
- package/dist/websocket/WebSocketConnection.d.ts +25 -0
- package/dist/websocket/WebSocketConnection.js +244 -0
- package/dist/websocket/WebSockets.d.ts +41 -0
- package/dist/websocket/WebSockets.js +194 -0
- package/dist/websocket/client/WebSocketClient.d.ts +21 -0
- package/dist/websocket/client/WebSocketClient.js +230 -0
- package/dist/websocket/client/index.d.ts +3 -0
- package/dist/websocket/client/index.js +1 -0
- package/dist/websocket/contracts/WebSocketChannel.d.ts +20 -0
- package/dist/websocket/contracts/WebSocketChannel.js +1 -0
- package/dist/websocket/contracts/WebSocketClient.d.ts +45 -0
- package/dist/websocket/contracts/WebSocketClient.js +1 -0
- package/dist/websocket/contracts/WebSocketEndpoint.d.ts +43 -0
- package/dist/websocket/contracts/WebSocketEndpoint.js +1 -0
- package/dist/websocket/contracts/WebSocketOptions.d.ts +48 -0
- package/dist/websocket/contracts/WebSocketOptions.js +1 -0
- package/dist/websocket/contracts/WebSocketPresence.d.ts +6 -0
- package/dist/websocket/contracts/WebSocketPresence.js +1 -0
- package/dist/websocket/contracts/WebSocketServiceOptions.d.ts +12 -0
- package/dist/websocket/contracts/WebSocketServiceOptions.js +1 -0
- package/dist/websocket/contracts/index.d.ts +6 -0
- package/dist/websocket/contracts/index.js +1 -0
- package/dist/websocket/defineChannel.d.ts +8 -0
- package/dist/websocket/defineChannel.js +13 -0
- package/dist/websocket/defineWebSocket.d.ts +14 -0
- package/dist/websocket/defineWebSocket.js +4 -0
- package/dist/websocket/fastify.d.ts +11 -0
- package/dist/websocket/fastify.js +38 -0
- package/dist/websocket/index.d.ts +5 -0
- package/dist/websocket/index.js +4 -0
- package/package.json +108 -7
- package/src/ai/AIErrors.ts +10 -0
- package/src/ai/AIFailover.ts +3 -2
- package/src/ai/AIProviderAdmission.ts +329 -0
- package/src/ai/AIRateLimiter.ts +134 -10
- package/src/ai/AIRequestTrackingError.ts +37 -0
- package/src/ai/Agent.ts +85 -71
- package/src/ai/Ai.ts +104 -48
- package/src/ai/OpenAIProviderError.ts +1 -1
- package/src/ai/ProviderSseObserver.ts +89 -0
- package/src/ai/README.md +216 -11
- package/src/ai/aiServiceTier.ts +14 -0
- package/src/ai/chunkEmbeddingText.ts +137 -0
- package/src/ai/contracts/AI.ts +17 -0
- package/src/ai/contracts/AIRequestErrorDiagnostics.ts +12 -0
- package/src/ai/contracts/AIServiceTier.ts +2 -0
- package/src/ai/contracts/Agent.ts +3 -0
- package/src/ai/contracts/EmbeddingTextChunk.ts +26 -0
- package/src/ai/contracts/Pricing.ts +7 -1
- package/src/ai/contracts/Providers.ts +2 -0
- package/src/ai/contracts/index.ts +2 -0
- package/src/ai/examples/prepareDocumentEmbeddings.ts +20 -0
- package/src/ai/index.ts +9 -2
- package/src/ai/{Models.ts → modelPricing.ts} +37 -4
- package/src/apps/AppInstallation.ts +25 -0
- package/src/apps/AppRouteError.ts +8 -0
- package/src/apps/AppService.ts +30 -0
- package/src/apps/Apps.ts +508 -0
- package/src/apps/README.md +354 -0
- package/src/apps/appsPlugin.ts +34 -0
- package/src/apps/commands/disableApp.ts +6 -0
- package/src/apps/commands/enableApp.ts +6 -0
- package/src/apps/commands/index.ts +29 -0
- package/src/apps/commands/installApp.ts +6 -0
- package/src/apps/commands/listApps.ts +6 -0
- package/src/apps/commands/makeAppMigration.ts +9 -0
- package/src/apps/commands/rollbackApp.ts +6 -0
- package/src/apps/commands/uninstallApp.ts +6 -0
- package/src/apps/contracts/AppAction.ts +2 -0
- package/src/apps/contracts/AppDefinition.ts +49 -0
- package/src/apps/contracts/AppDescription.ts +27 -0
- package/src/apps/contracts/AppManifest.ts +32 -0
- package/src/apps/contracts/AppNavigation.ts +10 -0
- package/src/apps/contracts/AppNavigationBadge.ts +6 -0
- package/src/apps/contracts/AppNavigationContext.ts +6 -0
- package/src/apps/contracts/AppNavigationResult.ts +9 -0
- package/src/apps/contracts/AppRoute.ts +18 -0
- package/src/apps/contracts/DiscoveredApp.ts +11 -0
- package/src/apps/contracts/InstalledApps.ts +2 -0
- package/src/apps/contracts/index.ts +11 -0
- package/src/apps/defineApp.ts +10 -0
- package/src/apps/discoverApps.ts +56 -0
- package/src/apps/fastify.ts +1 -0
- package/src/apps/index.ts +7 -0
- package/src/apps/normalizeAppNavigation.ts +29 -0
- package/src/apps/registerAppRoutes.ts +48 -0
- package/src/apps/validateAppDefinitions.ts +52 -0
- package/src/apps/vite.ts +1 -0
- package/src/apps/writeAppTypes.ts +14 -0
- package/src/auth/Auth.ts +89 -23
- package/src/auth/PasswordLoginAttempt.ts +72 -0
- package/src/auth/PasswordSuspendedError.ts +13 -0
- package/src/auth/README.md +94 -4
- package/src/auth/contracts/PasswordAuthProviderOptions.ts +30 -0
- package/src/auth/contracts/PasswordSuspension.ts +7 -0
- package/src/auth/contracts/PasswordSuspensionOptions.ts +10 -0
- package/src/auth/contracts/index.ts +3 -0
- package/src/auth/examples/runPasswordAuth.ts +6 -6
- package/src/auth/index.ts +2 -0
- package/src/auth/providers/PasswordAuthProvider.ts +96 -30
- package/src/auth/providers/resolveAuthProviders.ts +1 -0
- package/src/cli/Cli.ts +9 -0
- package/src/cli/README.md +19 -0
- package/src/cli/commands/repl.ts +2 -1
- package/src/cli/contracts/CliCommand.ts +2 -0
- package/src/cli/contracts/CliCommandInfo.ts +13 -0
- package/src/cli/contracts/index.ts +1 -0
- package/src/cli/index.ts +1 -0
- package/src/cli/listDb3Commands.ts +12 -0
- package/src/cli/runDb3.ts +5 -51
- package/src/cli/withDb3Cli.ts +55 -0
- package/src/db/ActiveQueryBuilder.ts +12 -0
- package/src/db/ActiveRecord.ts +9 -0
- package/src/db/README.md +45 -1
- package/src/db/connection.ts +12 -1
- package/src/db/examples/FieldNote.ts +7 -13
- package/src/db/examples/KnowledgeNote.ts +7 -13
- package/src/db/examples/runNoteMigrations.ts +11 -11
- package/src/db/fields/TimestampField.ts +8 -17
- package/src/db/migrations/DatabaseMigrationManager.ts +12 -4
- package/src/db/migrations/contracts/DatabaseMigration.ts +9 -0
- package/src/db/migrations/index.ts +1 -1
- package/src/db/migrations/{KnexMigrationRenderer.ts → renderKnexMigration.ts} +8 -14
- package/src/flows/FlowCompiler.ts +1 -1
- package/src/flows/Flows.ts +1 -1
- package/src/flows/index.ts +1 -1
- package/src/health/Health.ts +163 -0
- package/src/health/README.md +46 -0
- package/src/health/contracts/Health.ts +47 -0
- package/src/health/contracts/HealthHttp.ts +19 -0
- package/src/health/contracts/index.ts +2 -0
- package/src/health/databaseHealthCheck.ts +21 -0
- package/src/health/fastify.ts +49 -0
- package/src/health/index.ts +3 -0
- package/src/in-app/InApp.ts +173 -0
- package/src/in-app/InAppError.ts +8 -0
- package/src/in-app/InAppRecord.ts +41 -0
- package/src/in-app/README.md +193 -0
- package/src/in-app/contracts/InAppInbox.ts +51 -0
- package/src/in-app/contracts/InAppMessage.ts +19 -0
- package/src/in-app/contracts/InAppScope.ts +12 -0
- package/src/in-app/contracts/index.ts +3 -0
- package/src/in-app/examples/sendReportReady.ts +14 -0
- package/src/in-app/index.ts +4 -0
- package/src/in-app/validation.ts +49 -0
- package/src/index.ts +2 -22
- package/src/logging/DevelopmentConsole.ts +154 -0
- package/src/logging/Log.ts +5 -2
- package/src/logging/PrettyLogFormatter.ts +165 -0
- package/src/logging/README.md +52 -109
- package/src/logging/acquireDevelopmentConsole.ts +19 -0
- package/src/logging/contracts/DevelopmentConsoleOptions.ts +13 -0
- package/src/logging/contracts/EmailLogOptions.ts +9 -0
- package/src/logging/contracts/HttpExchangeMonitorOptions.ts +13 -0
- package/src/logging/contracts/LogTransportOptions.ts +10 -0
- package/src/logging/contracts/LoggingOptions.ts +12 -1
- package/src/logging/contracts/index.ts +4 -0
- package/src/logging/development/README.md +88 -0
- package/src/logging/drivers/PinoLoggerDriver.ts +72 -20
- package/src/logging/examples/runDevelopmentLogs.ts +36 -0
- package/src/logging/formatLogEmail.ts +24 -0
- package/src/logging/highlightQueueMessage.ts +38 -0
- package/src/logging/httpBodyCapture.ts +393 -0
- package/src/logging/index.ts +2 -0
- package/src/logging/redactLogEmail.ts +12 -0
- package/src/logging/registerHttpExchangeMonitor.ts +45 -0
- package/src/logging/transports/EmailLogTransport.ts +46 -0
- package/src/mail/Mail.ts +5 -0
- package/src/mail/MailDeliveryError.ts +8 -0
- package/src/mail/README.md +11 -1
- package/src/mail/index.ts +1 -0
- package/src/mail/transports/MailgunTransport.ts +2 -0
- package/src/mail/transports/ResendTransport.ts +24 -22
- package/src/media/MediaManager.ts +31 -10
- package/src/media/README.md +4 -0
- package/src/media/examples/runPrivateFiles.ts +2 -2
- package/src/network/BlockedUrlError.ts +19 -0
- package/src/network/README.md +133 -0
- package/src/network/assertPublicUrl.ts +77 -0
- package/src/network/contracts/GuardedFetch.ts +40 -0
- package/src/network/contracts/PublicUrlOptions.ts +17 -0
- package/src/network/contracts/index.ts +2 -0
- package/src/network/examples/runGuardedFetch.ts +35 -0
- package/src/network/guardedFetch.ts +150 -0
- package/src/network/index.ts +6 -0
- package/src/network/playwright/GuardedBrowserProxy.ts +172 -0
- package/src/network/playwright/contracts/GuardedBrowserContextOptions.ts +43 -0
- package/src/network/playwright/contracts/index.ts +1 -0
- package/src/network/playwright/index.ts +2 -0
- package/src/network/playwright/newGuardedBrowserContext.ts +74 -0
- package/src/network/privateNetworkAddresses.ts +166 -0
- package/src/network/publicAddressLookup.ts +49 -0
- package/src/notifications/Notifications.ts +105 -0
- package/src/notifications/README.md +59 -0
- package/src/notifications/contracts/Notification.ts +65 -0
- package/src/notifications/contracts/NotificationDelivery.ts +11 -0
- package/src/notifications/contracts/index.ts +2 -0
- package/src/notifications/examples/ReportReadyNotification.ts +31 -0
- package/src/notifications/index.ts +2 -0
- package/src/queue/JobRunAttempt.ts +17 -0
- package/src/queue/JobRunEvent.ts +16 -0
- package/src/queue/JobRunStoppedError.ts +8 -0
- package/src/queue/Queue.ts +112 -11
- package/src/queue/QueueTerminalError.ts +12 -0
- package/src/queue/QueueWorker.ts +86 -7
- package/src/queue/QueueWorkerReporter.ts +59 -0
- package/src/queue/QueueableJob.ts +2 -2
- package/src/queue/README.md +142 -1
- package/src/queue/RunnableJob.ts +272 -0
- package/src/queue/console.ts +54 -21
- package/src/queue/contracts/QueueDriver.ts +9 -0
- package/src/queue/contracts/QueueJobRunner.ts +4 -0
- package/src/queue/contracts/QueuePayload.ts +1 -1
- package/src/queue/contracts/QueueSelection.ts +11 -0
- package/src/queue/contracts/QueueService.ts +19 -4
- package/src/queue/contracts/QueueWorkerLifecycle.ts +2 -2
- package/src/queue/contracts/QueueWorkerSnapshot.ts +15 -0
- package/src/queue/contracts/QueueableJob.ts +7 -3
- package/src/queue/contracts/QueueableJobContract.ts +13 -0
- package/src/queue/contracts/index.ts +6 -0
- package/src/queue/drivers/DatabaseQueueDriver.ts +25 -3
- package/src/queue/drivers/RedisQueueDriver.ts +36 -0
- package/src/queue/examples/runQueueSelection.ts +34 -0
- package/src/queue/index.ts +7 -0
- package/src/queue/jobEnvelopeHasPrefix.ts +6 -0
- package/src/scheduler/README.md +50 -9
- package/src/scheduler/ScheduledEvent.ts +20 -5
- package/src/scheduler/ScheduledJob.ts +1 -1
- package/src/scheduler/Scheduler.ts +73 -27
- package/src/scheduler/SchedulerCheckpointRecord.ts +51 -0
- package/src/scheduler/SchedulerWorker.ts +54 -19
- package/src/scheduler/console.ts +3 -1
- package/src/scheduler/contracts/Schedule.ts +22 -0
- package/src/scheduler/contracts/Scheduler.ts +21 -6
- package/src/scheduler/contracts/SchedulerCheckpoint.ts +23 -0
- package/src/scheduler/contracts/SchedulerConsole.ts +5 -0
- package/src/scheduler/contracts/SchedulerWorker.ts +6 -1
- package/src/scheduler/contracts/index.ts +2 -0
- package/src/scheduler/examples/runSchedulerWindow.ts +54 -0
- package/src/scheduler/index.ts +1 -0
- package/src/server/App.ts +73 -10
- package/src/server/README.md +95 -2
- package/src/server/contracts/HttpErrorHandlerOptions.ts +29 -0
- package/src/server/contracts/index.ts +1 -0
- package/src/server/examples/createFirstServer.ts +5 -3
- package/src/server/index.ts +4 -0
- package/src/server/publicServerErrorMessage.ts +17 -0
- package/src/server/registerBrowserJsonFormatting.ts +94 -0
- package/src/server/registerHttpErrorHandler.ts +96 -0
- package/src/ssr/README.md +6 -0
- package/src/ssr/vite/contracts/ViteSsrOptions.ts +2 -0
- package/src/ssr/vite/fastifyViteSsr.ts +4 -3
- package/src/storage/drivers/FlystorageDisk.ts +2 -2
- package/src/storage/index.ts +2 -2
- package/src/websocket/README.md +458 -0
- package/src/websocket/WebSocketChannels.ts +99 -0
- package/src/websocket/WebSocketConnection.ts +162 -0
- package/src/websocket/WebSockets.ts +157 -0
- package/src/websocket/client/WebSocketClient.ts +188 -0
- package/src/websocket/client/index.ts +3 -0
- package/src/websocket/contracts/WebSocketChannel.ts +23 -0
- package/src/websocket/contracts/WebSocketClient.ts +48 -0
- package/src/websocket/contracts/WebSocketEndpoint.ts +46 -0
- package/src/websocket/contracts/WebSocketOptions.ts +50 -0
- package/src/websocket/contracts/WebSocketPresence.ts +6 -0
- package/src/websocket/contracts/WebSocketServiceOptions.ts +12 -0
- package/src/websocket/contracts/index.ts +6 -0
- package/src/websocket/defineChannel.ts +14 -0
- package/src/websocket/defineWebSocket.ts +15 -0
- package/src/websocket/examples/board/LiveBoard.ts +97 -0
- package/src/websocket/examples/board/SummarizeBoardJob.ts +52 -0
- package/src/websocket/examples/board/boardSnapshot.ts +16 -0
- package/src/websocket/examples/board/createBoardClient.ts +43 -0
- package/src/websocket/examples/board/createBoardStore.ts +58 -0
- package/src/websocket/examples/board/registerBoardRoutes.ts +31 -0
- package/src/websocket/examples/createChannelSync.ts +66 -0
- package/src/websocket/examples/deploymentEndpoint.ts +13 -0
- package/src/websocket/examples/deploymentSnapshot.ts +36 -0
- package/src/websocket/examples/resourceChannels.ts +18 -0
- package/src/websocket/examples/userEndpoint.ts +27 -0
- package/src/websocket/fastify.ts +39 -0
- package/src/websocket/index.ts +5 -0
- package/templates/AGENTS.md +33 -2
- /package/dist/flows/{FlowValueValidator.d.ts → flowValueValidator.d.ts} +0 -0
- /package/dist/flows/{FlowValueValidator.js → flowValueValidator.js} +0 -0
- /package/dist/storage/{mime.d.ts → mimeTypeFromPath.d.ts} +0 -0
- /package/dist/storage/{mime.js → mimeTypeFromPath.js} +0 -0
- /package/dist/storage/{contents.d.ts → storageContentsToBuffer.d.ts} +0 -0
- /package/dist/storage/{contents.js → storageContentsToBuffer.js} +0 -0
- /package/src/flows/{FlowValueValidator.ts → flowValueValidator.ts} +0 -0
- /package/src/storage/{mime.ts → mimeTypeFromPath.ts} +0 -0
- /package/src/storage/{contents.ts → storageContentsToBuffer.ts} +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { InAppMessage, InAppScope } from '../../in-app';
|
|
2
|
+
import type { MailMessage } from '../../mail';
|
|
3
|
+
|
|
4
|
+
/** Framework delivery channels currently supported by notification classes. */
|
|
5
|
+
export type NotificationChannel = 'inApp' | 'mail';
|
|
6
|
+
|
|
7
|
+
/** Minimum persisted identity accepted by the notification service. */
|
|
8
|
+
export interface NotificationUser {
|
|
9
|
+
/** Stable application identity used for delivery ownership and deduplication. */
|
|
10
|
+
id: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Application-owned notification content and routing policy.
|
|
15
|
+
*
|
|
16
|
+
* Channel renderers return data only. They must not persist messages, contact
|
|
17
|
+
* providers or depend on an active HTTP request.
|
|
18
|
+
*/
|
|
19
|
+
export interface Notification<TUser extends NotificationUser = NotificationUser> {
|
|
20
|
+
/** Stable application-owned type used for inbox filtering and deduplication. */
|
|
21
|
+
readonly type: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Selects delivery channels for one resolved user.
|
|
25
|
+
*
|
|
26
|
+
* @param user - Persisted recipient, available for future preference checks.
|
|
27
|
+
* @returns Distinct supported channels to deliver.
|
|
28
|
+
*/
|
|
29
|
+
via(user: TUser): readonly NotificationChannel[];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolves recipient scope for authorization and inbox queries.
|
|
33
|
+
*
|
|
34
|
+
* @param user - Persisted recipient receiving this occurrence.
|
|
35
|
+
* @returns Account scope by default, or an application-owned scope.
|
|
36
|
+
*/
|
|
37
|
+
scope?(user: TUser): InAppScope;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns a stable business identity used to deduplicate retries.
|
|
41
|
+
*
|
|
42
|
+
* @param user - Persisted recipient receiving this occurrence.
|
|
43
|
+
* @returns Application-owned occurrence key, or undefined for no deduplication.
|
|
44
|
+
*/
|
|
45
|
+
key?(user: TUser): string | undefined;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Renders durable in-app content when `via()` selects `inApp`.
|
|
49
|
+
*
|
|
50
|
+
* @param user - Persisted recipient receiving this occurrence.
|
|
51
|
+
* @returns Validated content stored in the recipient's inbox.
|
|
52
|
+
*/
|
|
53
|
+
toInApp?(user: TUser): InAppMessage;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Renders email content when `via()` selects `mail`.
|
|
57
|
+
*
|
|
58
|
+
* @param user - Persisted recipient receiving this occurrence.
|
|
59
|
+
* @returns Provider-neutral mail content ready for the configured transport.
|
|
60
|
+
*/
|
|
61
|
+
toMail?(user: TUser): MailMessage;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** User model or stable ID accepted by notification delivery. */
|
|
65
|
+
export type NotificationRecipient<TUser extends NotificationUser> = TUser | string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InAppAcceptance } from '../../in-app';
|
|
2
|
+
import type { MailDelivery } from '../../mail';
|
|
3
|
+
/** Accepted channel outcomes for one resolved user. */
|
|
4
|
+
export interface NotificationDelivery {
|
|
5
|
+
/** Stable recipient identity. */
|
|
6
|
+
userId: string;
|
|
7
|
+
/** Durable in-app acceptance, or null when the notification omitted the channel. */
|
|
8
|
+
inApp: InAppAcceptance | null;
|
|
9
|
+
/** Mail transport acceptance, or null when the renderer omitted the channel. */
|
|
10
|
+
mail: MailDelivery | null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Notification } from '@db3.ai/app/notifications';
|
|
2
|
+
import type { UserIdentity } from '@db3.ai/app/auth';
|
|
3
|
+
|
|
4
|
+
/** Example code-owned notification with explicit in-app and email renderers. */
|
|
5
|
+
export class ReportReadyNotification implements Notification<UserIdentity> {
|
|
6
|
+
readonly type = 'report.ready';
|
|
7
|
+
|
|
8
|
+
/** Creates a report-ready notification for one report occurrence. */
|
|
9
|
+
constructor(private readonly reportId: string) {}
|
|
10
|
+
|
|
11
|
+
/** Selects durable in-app and email delivery for this recipient. */
|
|
12
|
+
via(_user: UserIdentity) {
|
|
13
|
+
return ['inApp', 'mail'] as const;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Returns the report occurrence identity used to deduplicate retries. */
|
|
17
|
+
key(_user: UserIdentity) {
|
|
18
|
+
return this.reportId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Returns the durable inbox content. */
|
|
22
|
+
toInApp(_user: UserIdentity) {
|
|
23
|
+
return { title: 'Your report is ready', body: 'Open the report to review it.', presentation: 'banner' as const };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Returns provider-neutral email content. */
|
|
27
|
+
toMail(user: UserIdentity) {
|
|
28
|
+
if (!user.email) throw new Error('Report notification requires a recipient email.');
|
|
29
|
+
return { to: user.email, subject: 'Your report is ready', text: 'Open the app to review it.' };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ActiveRecord } from '../db/ActiveRecord';
|
|
2
|
+
|
|
3
|
+
/** Retains each execution boundary, including attempts interrupted before a handler could fail itself. */
|
|
4
|
+
export class JobRunAttempt extends ActiveRecord.define({
|
|
5
|
+
table: 'job_run_attempts',
|
|
6
|
+
fields: field => ({
|
|
7
|
+
id: field.ulid(),
|
|
8
|
+
runId: field.string({ column: 'run_id', required: true, length: 26, index: true }),
|
|
9
|
+
tenantId: field.string({ column: 'tenant_id', required: true, length: 100, index: true }),
|
|
10
|
+
attempt: field.integer({ required: true }),
|
|
11
|
+
deliveryId: field.string({ column: 'delivery_id', required: true, length: 100 }),
|
|
12
|
+
status: field.string({ required: true, length: 30 }),
|
|
13
|
+
error: field.text(),
|
|
14
|
+
startedAt: field.timestamp({ column: 'started_at', required: true }),
|
|
15
|
+
completedAt: field.timestamp({ column: 'completed_at' }),
|
|
16
|
+
}),
|
|
17
|
+
}) {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActiveRecord } from '../db/ActiveRecord';
|
|
2
|
+
|
|
3
|
+
/** Append-only diagnostic evidence for a run; applications control access and retention. */
|
|
4
|
+
export class JobRunEvent extends ActiveRecord.define({
|
|
5
|
+
table: 'job_run_events',
|
|
6
|
+
fields: field => ({
|
|
7
|
+
id: field.ulid(),
|
|
8
|
+
runId: field.string({ column: 'run_id', required: true, length: 26, index: true }),
|
|
9
|
+
tenantId: field.string({ column: 'tenant_id', required: true, length: 100, index: true }),
|
|
10
|
+
attempt: field.integer({ required: true, default: 0 }),
|
|
11
|
+
level: field.choice({ choices: ['info', 'warning', 'error'], required: true, default: 'info' }),
|
|
12
|
+
message: field.string({ required: true, length: 1024 }),
|
|
13
|
+
data: field.json<Record<string, unknown>>(),
|
|
14
|
+
createdAt: field.timestamp({ column: 'created_at', auto: 'create' }),
|
|
15
|
+
}),
|
|
16
|
+
}) {}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Cooperative stop requested by an operator or a job-owned blocking policy. */
|
|
2
|
+
export class JobRunStoppedError extends Error {
|
|
3
|
+
/** Captures a durable stop reason without treating expected stops as provider exceptions. */
|
|
4
|
+
constructor(readonly status: 'blocked' | 'cancelled', message: string) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'JobRunStoppedError';
|
|
7
|
+
}
|
|
8
|
+
}
|
package/src/queue/Queue.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { RunnableJob } from './RunnableJob';
|
|
2
|
+
import type { QueueableJobContract as QueueableJob } from './contracts/QueueableJobContract';
|
|
3
|
+
import { QueueTerminalError } from './QueueTerminalError';
|
|
1
4
|
import { randomUUID } from 'node:crypto';
|
|
2
5
|
|
|
3
6
|
import type { Database } from '../db';
|
|
@@ -7,7 +10,7 @@ import { enableDefaultQueueMonitor, type QueueMonitor } from './queueMonitor';
|
|
|
7
10
|
import { QueueEvents } from './QueueEvents';
|
|
8
11
|
import { queueRetryDeadlineReached, queueRetryDelaySeconds, queueRetryUntil, resolveQueueRetryBackoff, type QueueRetryBackoffDefaults } from './RetryPolicy';
|
|
9
12
|
import { QueueWorker } from './QueueWorker';
|
|
10
|
-
import { isQueueableJob, isQueueableJobClass, queueableJobFromJSON, queueableJobName
|
|
13
|
+
import { isQueueableJob, isQueueableJobClass, queueableJobFromJSON, queueableJobName } from './QueueableJob';
|
|
11
14
|
import { isQueueRetryLaterError } from './QueueRetryLaterError';
|
|
12
15
|
import type * as queue from './contracts';
|
|
13
16
|
|
|
@@ -20,6 +23,10 @@ interface QueueJobExecution {
|
|
|
20
23
|
handle(): Promise<void>;
|
|
21
24
|
onRetry?(error: unknown, delaySeconds: number): Promise<void>;
|
|
22
25
|
onFinalFailure?(error: unknown): Promise<void>;
|
|
26
|
+
/** Runnable records retain their own terminal history. */
|
|
27
|
+
retainsFailure?: boolean;
|
|
28
|
+
/** Reuses the jitter sample used to persist a retained run outcome. */
|
|
29
|
+
retryDelaySeconds?: number;
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
/**
|
|
@@ -36,7 +43,9 @@ type QueueJobExecutionFactory = (
|
|
|
36
43
|
* callers can either process one job at a time or start a worker loop over the same API.
|
|
37
44
|
*/
|
|
38
45
|
export class Queue implements queue.QueueService {
|
|
46
|
+
#jobClasses = new Map<string, queue.QueueableJobClass>();
|
|
39
47
|
private readonly handlers = new Map<string, QueueJobExecutionFactory>();
|
|
48
|
+
readonly #ownedJobs = new Map<string, { Job: queue.QueueableJobClass; run: queue.QueueJobRunner }>();
|
|
40
49
|
private readonly driver: queue.QueueDriver;
|
|
41
50
|
private readonly monitor: QueueMonitor | null;
|
|
42
51
|
readonly events: QueueEvents;
|
|
@@ -59,12 +68,45 @@ export class Queue implements queue.QueueService {
|
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
/**
|
|
62
|
-
* Returns the
|
|
71
|
+
* Returns the configured transport name for atomic database dispatch.
|
|
63
72
|
*/
|
|
73
|
+
get driverName(): string { return this.driver.name; }
|
|
74
|
+
|
|
75
|
+
/** Reports worker presence through the application-owned sink without changing delivery semantics. */
|
|
76
|
+
async reportWorker(snapshot: queue.QueueWorkerSnapshot): Promise<void> {
|
|
77
|
+
await this.options.onWorkerHeartbeat?.(snapshot);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Returns the optional queue lifecycle monitor. */
|
|
64
81
|
get queueMonitor(): QueueMonitor | null {
|
|
65
82
|
return this.monitor;
|
|
66
83
|
}
|
|
67
84
|
|
|
85
|
+
/** Registers app-owned stable job names atomically and returns cleanup for this process only. */
|
|
86
|
+
registerJobs(owner: string, jobs: readonly queue.QueueableJobClass[], run: queue.QueueJobRunner = operation => operation()): () => void {
|
|
87
|
+
if (!/^[a-z][a-z0-9_]{0,39}$/.test(owner)) throw new Error('Invalid queue job owner.');
|
|
88
|
+
const names = new Set<string>();
|
|
89
|
+
for (const Job of jobs) {
|
|
90
|
+
const name = queueableJobName(Job);
|
|
91
|
+
if (!isQueueableJobClass(Job) || !Job.jobName?.startsWith(`${owner}.`) || names.has(name) || this.handlers.has(name)) throw new Error(`Invalid or duplicate owned job "${name}"; use an explicit ${owner}. jobName.`);
|
|
92
|
+
names.add(name);
|
|
93
|
+
}
|
|
94
|
+
for (const Job of jobs) { this.registerJob(Job); this.#ownedJobs.set(queueableJobName(Job), { Job, run }); }
|
|
95
|
+
let closed = false;
|
|
96
|
+
return () => {
|
|
97
|
+
if (closed) return;
|
|
98
|
+
closed = true;
|
|
99
|
+
for (const name of names) { this.handlers.delete(name); this.#jobClasses.delete(name); this.#ownedJobs.delete(name); }
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Checks active, delayed, reserved and chained payloads after producers have been quiesced. */
|
|
104
|
+
async hasPendingJobs(jobPrefix: string): Promise<boolean> {
|
|
105
|
+
if (!jobPrefix.trim()) throw new Error('A non-empty job prefix is required.');
|
|
106
|
+
if (!this.driver.hasPendingJobs) throw new Error(`Queue driver "${this.driver.name}" cannot inspect outstanding app work.`);
|
|
107
|
+
return this.driver.hasPendingJobs(jobPrefix);
|
|
108
|
+
}
|
|
109
|
+
|
|
68
110
|
/**
|
|
69
111
|
* Returns recent terminal failures from the configured queue driver.
|
|
70
112
|
*
|
|
@@ -130,6 +172,7 @@ export class Queue implements queue.QueueService {
|
|
|
130
172
|
name: string,
|
|
131
173
|
handler: queue.JobHandler<TData>,
|
|
132
174
|
): void {
|
|
175
|
+
if (this.#ownedJobs.has(name)) throw new Error(`Job "${name}" belongs to an owned registration.`);
|
|
133
176
|
this.handlers.set(name, job => {
|
|
134
177
|
return {
|
|
135
178
|
handle: () => handler(job as queue.QueueJob<TData>),
|
|
@@ -147,12 +190,29 @@ export class Queue implements queue.QueueService {
|
|
|
147
190
|
throw new Error('Queue.registerJob requires a QueueableJob class.');
|
|
148
191
|
}
|
|
149
192
|
|
|
150
|
-
this.
|
|
151
|
-
|
|
193
|
+
const owned = this.#ownedJobs.get(queueableJobName(Job));
|
|
194
|
+
if (owned) {
|
|
195
|
+
if (owned.Job !== Job) throw new Error(`Job "${queueableJobName(Job)}" belongs to another registration.`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.#jobClasses.set(queueableJobName(Job), Job);
|
|
199
|
+
this.handlers.set(queueableJobName(Job), async job => {
|
|
200
|
+
const instance = await queueableJobFromJSON(Job, job.payload.data);
|
|
152
201
|
const context = this.createQueueableJobContext(job);
|
|
202
|
+
let retryDelay: number | undefined;
|
|
203
|
+
context.willRetry = error => {
|
|
204
|
+
retryDelay ??= queueRetryDelaySeconds(job.payload.backoff ?? this.resolveRetryBackoff(), job.attempts);
|
|
205
|
+
return isQueueRetryLaterError(error) || !(error instanceof QueueTerminalError) && job.attempts < job.payload.maxTries && !queueRetryDeadlineReached(job.payload.retryUntil, retryDelay, unixTimestamp());
|
|
206
|
+
};
|
|
207
|
+
context.assertOwnership = async () => {
|
|
208
|
+
const error = await this.confirmLease(job, this.retryAfterSeconds(), 'checkpoint');
|
|
209
|
+
if (error) throw error;
|
|
210
|
+
};
|
|
153
211
|
|
|
154
212
|
return {
|
|
155
|
-
|
|
213
|
+
retainsFailure: instance instanceof RunnableJob,
|
|
214
|
+
get retryDelaySeconds() { return retryDelay; },
|
|
215
|
+
handle: () => instance instanceof RunnableJob ? instance.execute(context) : instance.handle(context),
|
|
156
216
|
onRetry: (error, delaySeconds) => instance.onRetry({
|
|
157
217
|
...context,
|
|
158
218
|
error,
|
|
@@ -185,6 +245,18 @@ export class Queue implements queue.QueueService {
|
|
|
185
245
|
dataOrOptions: Record<string, unknown> | queue.DispatchOptions = {},
|
|
186
246
|
maybeOptions: queue.DispatchOptions = {},
|
|
187
247
|
): Promise<queue.QueueJobId> {
|
|
248
|
+
const dispatchedName = typeof job === 'string' ? job : queueableJobName(job.constructor as queue.QueueableJobClass);
|
|
249
|
+
const Registered = this.#jobClasses.get(dispatchedName);
|
|
250
|
+
// Applications can register a retained implementation of a framework job.
|
|
251
|
+
if (Registered?.prototype instanceof RunnableJob && !(job instanceof RunnableJob)) {
|
|
252
|
+
const input = typeof job === 'string' ? dataOrOptions : job.serialize().data;
|
|
253
|
+
return this.dispatch(new Registered(input), typeof job === 'string' ? maybeOptions : dataOrOptions as queue.DispatchOptions);
|
|
254
|
+
}
|
|
255
|
+
if (job instanceof RunnableJob && !job.publishingDelivery) {
|
|
256
|
+
const parent = RunnableJob.current();
|
|
257
|
+
if (parent) { await parent.assertActive(); job.parentRunId ??= parent.id; }
|
|
258
|
+
return job.enqueue(this, dataOrOptions as queue.DispatchOptions);
|
|
259
|
+
}
|
|
188
260
|
const serialized = isQueueableJob(job) ? job.serialize() : null;
|
|
189
261
|
const name = resolveDispatchedJobName(job, serialized?.job);
|
|
190
262
|
const options = serialized ? dataOrOptions as queue.DispatchOptions : maybeOptions;
|
|
@@ -265,6 +337,23 @@ export class Queue implements queue.QueueService {
|
|
|
265
337
|
|
|
266
338
|
if (!job) return null;
|
|
267
339
|
|
|
340
|
+
const owned = this.#ownedJobs.get(job.payload.job);
|
|
341
|
+
if (!owned) return this.#workClaimedJob(job, retryAfterSeconds, options);
|
|
342
|
+
let entered = false;
|
|
343
|
+
try {
|
|
344
|
+
return await owned.run(async () => { entered = true; return this.#workClaimedJob(job, retryAfterSeconds, options); });
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (entered) throw error;
|
|
347
|
+
// Admission was refused before app code ran. Retain the job without consuming an attempt.
|
|
348
|
+
if (!(await this.driver.defer(job, 30))) return this.recordLostLease(job, performance.now(), this.lostLeaseError(job, 'defer'));
|
|
349
|
+
const deferred = { ...job, attempts: Math.max(0, job.attempts - 1) };
|
|
350
|
+
await this.publishJobEvent('deferred', deferred, { delaySeconds: 30, error });
|
|
351
|
+
return { job: deferred, status: 'deferred', delaySeconds: 30, error };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** Processes an already claimed job and completes its durable transition and hooks before releasing ownership. */
|
|
356
|
+
async #workClaimedJob(job: queue.QueueJob, retryAfterSeconds: number, options: queue.QueueWorkOptions): Promise<queue.QueueProcessResult> {
|
|
268
357
|
options.onClaimed?.(job);
|
|
269
358
|
await this.publishJobEvent('claimed', job);
|
|
270
359
|
|
|
@@ -346,7 +435,7 @@ export class Queue implements queue.QueueService {
|
|
|
346
435
|
};
|
|
347
436
|
}
|
|
348
437
|
|
|
349
|
-
const delaySeconds = queueRetryDelaySeconds(
|
|
438
|
+
const delaySeconds = execution?.retryDelaySeconds ?? queueRetryDelaySeconds(
|
|
350
439
|
job.payload.backoff ?? this.resolveRetryBackoff(),
|
|
351
440
|
job.attempts,
|
|
352
441
|
);
|
|
@@ -356,8 +445,10 @@ export class Queue implements queue.QueueService {
|
|
|
356
445
|
unixTimestamp(),
|
|
357
446
|
);
|
|
358
447
|
|
|
359
|
-
if (job.attempts >= job.payload.maxTries || retryDeadlineReached) {
|
|
360
|
-
const failed =
|
|
448
|
+
if (error instanceof QueueTerminalError || job.attempts >= job.payload.maxTries || retryDeadlineReached) {
|
|
449
|
+
const failed = execution?.retainsFailure
|
|
450
|
+
? await this.driver.delete(job)
|
|
451
|
+
: await this.driver.fail(job, error);
|
|
361
452
|
|
|
362
453
|
if (!failed) {
|
|
363
454
|
return await this.recordLostLease(
|
|
@@ -549,17 +640,27 @@ export class Queue implements queue.QueueService {
|
|
|
549
640
|
}
|
|
550
641
|
|
|
551
642
|
/**
|
|
552
|
-
*
|
|
643
|
+
* Discovers named queues without reading or changing job payloads.
|
|
644
|
+
* @throws When a custom driver does not implement wildcard discovery.
|
|
645
|
+
*/
|
|
646
|
+
async queueNames(): Promise<string[]> {
|
|
647
|
+
if (!this.driver.queueNames) throw new Error(`Queue driver "${this.driver.name}" does not support wildcard queue discovery.`);
|
|
648
|
+
return this.driver.queueNames();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Starts a polling worker for a named queue or selection unless disabled.
|
|
653
|
+
* @example queue.startWorker({ queues: '*', excludeQueues: ['articles'] });
|
|
553
654
|
*/
|
|
554
655
|
startWorker(
|
|
555
|
-
queue = process.env.QUEUE_NAME || this.options.queue || 'default',
|
|
656
|
+
queue: string | queue.QueueSelection = process.env.QUEUE_NAME || this.options.queue || 'default',
|
|
556
657
|
options: queue.QueueWorkerOptions = {},
|
|
557
658
|
): QueueWorker | null {
|
|
558
659
|
if (this.workerDisabled() && !options.force) return null;
|
|
559
660
|
|
|
560
661
|
const worker = new QueueWorker(
|
|
561
662
|
this,
|
|
562
|
-
queue,
|
|
663
|
+
typeof queue === 'string' ? queue : { ...queue, queues: queue.queues ?? [this.options.queue || 'default'] },
|
|
563
664
|
options.intervalMs ?? this.workerIntervalMs(),
|
|
564
665
|
options.maxJobsPerTick ?? this.maxJobsPerTick(),
|
|
565
666
|
options.logger,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stops a queue delivery immediately, preserving its failed-job record and hooks.
|
|
3
|
+
* Use for a durable failure that ordinary retries cannot repair. Applications
|
|
4
|
+
* still own partial output and progress; explicit replay remains a separate act.
|
|
5
|
+
*/
|
|
6
|
+
export class QueueTerminalError extends Error {
|
|
7
|
+
/** Creates an immediately terminal queue failure with a safe explanation. */
|
|
8
|
+
constructor(message: string) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'QueueTerminalError';
|
|
11
|
+
}
|
|
12
|
+
}
|
package/src/queue/QueueWorker.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { QueueWorkerReporter } from './QueueWorkerReporter';
|
|
1
2
|
import { unknownErrorMessage } from '@db3.ai/pure';
|
|
2
3
|
|
|
3
4
|
import type { Queue } from './Queue';
|
|
@@ -11,27 +12,43 @@ const DEFAULT_MAX_JOBS_PER_TICK = 5;
|
|
|
11
12
|
const MIN_MAX_JOBS_PER_TICK = 1;
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* Polls
|
|
15
|
+
* Polls eligible named queues in round-robin order, executing one job at a time.
|
|
15
16
|
*
|
|
16
17
|
* The worker intentionally owns only timer and lifecycle behavior. It delegates claims,
|
|
17
18
|
* handler execution, retries, and failure recording back to Queue.
|
|
18
19
|
*/
|
|
19
20
|
export class QueueWorker implements queue.QueueWorkerLifecycle {
|
|
21
|
+
readonly #presence: QueueWorkerReporter;
|
|
20
22
|
#activeTick: Promise<void> | null = null;
|
|
23
|
+
#activeJob: Promise<queue.QueueProcessResult | null> | null = null;
|
|
24
|
+
#lastQueue: string | null = null;
|
|
25
|
+
#stopping = false;
|
|
26
|
+
readonly #queues: readonly string[] | '*';
|
|
27
|
+
readonly #excludeQueues: ReadonlySet<string>;
|
|
28
|
+
private readonly queue: string;
|
|
21
29
|
private timer: NodeJS.Timeout | null = null;
|
|
22
30
|
private readonly intervalMs: number;
|
|
23
31
|
private readonly maxJobsPerTick: number;
|
|
24
32
|
|
|
33
|
+
/** Creates a worker without starting its polling loop; workOnce can be used for bounded execution. */
|
|
25
34
|
constructor(
|
|
26
35
|
private readonly queueManager: Queue,
|
|
27
|
-
|
|
36
|
+
selection: string | queue.QueueSelection = 'default',
|
|
28
37
|
intervalMs = Number(process.env.QUEUE_WORKER_INTERVAL_MS || DEFAULT_WORKER_INTERVAL_MS),
|
|
29
38
|
maxJobsPerTick = Number(process.env.QUEUE_WORKER_MAX_JOBS_PER_TICK || DEFAULT_MAX_JOBS_PER_TICK),
|
|
30
39
|
private readonly logger?: queue.QueueLogger,
|
|
31
40
|
private readonly verbose = false,
|
|
32
41
|
) {
|
|
42
|
+
const requested = typeof selection === 'string' ? { queues: [selection] } : selection;
|
|
43
|
+
this.#queues = requested.queues === '*' ? '*' : normalizeQueueNames(requested.queues ?? ['default']);
|
|
44
|
+
if (this.#queues !== '*' && this.#queues.length === 0) throw new Error('A worker must select at least one queue.');
|
|
45
|
+
this.#excludeQueues = new Set(normalizeQueueNames(requested.excludeQueues ?? []));
|
|
46
|
+
if (this.#queues !== '*' && this.#queues.every(name => this.#excludeQueues.has(name))) throw new Error('Worker queue exclusions leave no eligible queues.');
|
|
47
|
+
this.queue = (this.#queues === '*' ? '*' : this.#queues.join(', '))
|
|
48
|
+
+ (this.#excludeQueues.size ? ` excluding ${[...this.#excludeQueues].join(', ')}` : '');
|
|
33
49
|
this.intervalMs = normalizeWorkerIntervalMs(intervalMs);
|
|
34
50
|
this.maxJobsPerTick = normalizeMaxJobsPerTick(maxJobsPerTick);
|
|
51
|
+
this.#presence = new QueueWorkerReporter({ queues: this.#queues, excludeQueues: [...this.#excludeQueues] }, snapshot => this.queueManager.reportWorker(snapshot), this.logger);
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
/**
|
|
@@ -39,20 +56,24 @@ export class QueueWorker implements queue.QueueWorkerLifecycle {
|
|
|
39
56
|
*/
|
|
40
57
|
start(): void {
|
|
41
58
|
if (this.timer) return;
|
|
59
|
+
this.#stopping = false;
|
|
42
60
|
|
|
43
61
|
this.logger?.info(this.verbose
|
|
44
62
|
? `[queue] Worker started for "${this.queue}" (interval ${this.intervalMs}ms, max ${this.maxJobsPerTick} jobs/tick).`
|
|
45
63
|
: `[queue] Worker started for "${this.queue}".`);
|
|
46
|
-
this.
|
|
64
|
+
this.#presence.start();
|
|
47
65
|
this.timer = setInterval(() => {
|
|
48
66
|
this.startTick();
|
|
49
67
|
}, this.intervalMs);
|
|
68
|
+
this.startTick();
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
/**
|
|
53
|
-
* Stops future
|
|
72
|
+
* Stops future claims. An already-running job is allowed to finish.
|
|
54
73
|
*/
|
|
55
74
|
stop(): void {
|
|
75
|
+
this.#stopping = true;
|
|
76
|
+
if (this.#activeJob) this.#presence.update('stopping'); else void this.#presence.stop();
|
|
56
77
|
if (!this.timer) return;
|
|
57
78
|
|
|
58
79
|
clearInterval(this.timer);
|
|
@@ -61,7 +82,7 @@ export class QueueWorker implements queue.QueueWorkerLifecycle {
|
|
|
61
82
|
}
|
|
62
83
|
|
|
63
84
|
/**
|
|
64
|
-
* Stops future
|
|
85
|
+
* Stops future claims and waits for the current job, including timer-free execution.
|
|
65
86
|
*/
|
|
66
87
|
async stopAndDrain(): Promise<void> {
|
|
67
88
|
this.stop();
|
|
@@ -69,6 +90,56 @@ export class QueueWorker implements queue.QueueWorkerLifecycle {
|
|
|
69
90
|
if (this.#activeTick) {
|
|
70
91
|
await this.#activeTick;
|
|
71
92
|
}
|
|
93
|
+
await this.#activeJob;
|
|
94
|
+
await this.#presence.stop();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Processes at most one eligible job, rotating after each claim and skipping empty queues.
|
|
99
|
+
* Wildcard discovery is refreshed on every call, including after an idle poll.
|
|
100
|
+
* Concurrent calls are rejected so one worker cannot occupy multiple execution slots.
|
|
101
|
+
*/
|
|
102
|
+
async workOnce(options: queue.QueueWorkOptions = {}): Promise<queue.QueueProcessResult | null> {
|
|
103
|
+
if (this.#activeJob) throw new Error('This queue worker is already processing a job.');
|
|
104
|
+
if (this.#stopping) return null;
|
|
105
|
+
this.#presence.start();
|
|
106
|
+
const pending = this.#workOnce(options);
|
|
107
|
+
this.#activeJob = pending;
|
|
108
|
+
try {
|
|
109
|
+
return await pending;
|
|
110
|
+
} finally {
|
|
111
|
+
this.#activeJob = null;
|
|
112
|
+
if (this.#stopping || !this.timer) await this.#presence.stop();
|
|
113
|
+
else this.#presence.update('idle');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Resolves eligible names and delegates each atomic claim to the queue service. */
|
|
118
|
+
async #workOnce(options: queue.QueueWorkOptions): Promise<queue.QueueProcessResult | null> {
|
|
119
|
+
const names = (this.#queues === '*' ? [...new Set(await this.queueManager.queueNames())].sort() : [...this.#queues])
|
|
120
|
+
.filter(name => !this.#excludeQueues.has(name));
|
|
121
|
+
let start = 0;
|
|
122
|
+
if (this.#lastQueue !== null) {
|
|
123
|
+
const previous = names.indexOf(this.#lastQueue);
|
|
124
|
+
start = previous >= 0 ? previous + 1 : names.findIndex(name => name > this.#lastQueue!);
|
|
125
|
+
if (start < 0 || start >= names.length) start = 0;
|
|
126
|
+
}
|
|
127
|
+
for (let offset = 0; offset < names.length && !this.#stopping; offset++) {
|
|
128
|
+
const name = names[(start + offset) % names.length]!;
|
|
129
|
+
const result = await this.queueManager.workNextJob(name, {
|
|
130
|
+
...options,
|
|
131
|
+
onClaimed: job => {
|
|
132
|
+
this.#lastQueue = name;
|
|
133
|
+
this.#presence.update('busy', job.queue, String(job.id));
|
|
134
|
+
options.onClaimed?.(job);
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
if (result) {
|
|
138
|
+
this.#lastQueue = name;
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
72
143
|
}
|
|
73
144
|
|
|
74
145
|
/**
|
|
@@ -94,8 +165,8 @@ export class QueueWorker implements queue.QueueWorkerLifecycle {
|
|
|
94
165
|
let processedThisTick = 0;
|
|
95
166
|
|
|
96
167
|
try {
|
|
97
|
-
for (let processed = 0; processed < this.maxJobsPerTick; processed += 1) {
|
|
98
|
-
const result = await this.
|
|
168
|
+
for (let processed = 0; processed < this.maxJobsPerTick && !this.#stopping; processed += 1) {
|
|
169
|
+
const result = await this.workOnce({
|
|
99
170
|
onClaimed: job => this.logClaimed(job),
|
|
100
171
|
});
|
|
101
172
|
|
|
@@ -226,6 +297,14 @@ export class QueueWorker implements queue.QueueWorkerLifecycle {
|
|
|
226
297
|
|
|
227
298
|
export { QueueWorker as DatabaseQueueWorker };
|
|
228
299
|
|
|
300
|
+
/** Validates and copies exact queue names so caller mutations cannot alter worker admission. */
|
|
301
|
+
function normalizeQueueNames(names: readonly string[]): string[] {
|
|
302
|
+
if (!Array.isArray(names) || names.some(name => typeof name !== 'string' || !name.trim() || name !== name.trim() || name === '*')) {
|
|
303
|
+
throw new Error('Queue names must be non-empty exact names; use queues: "*" to select all queues.');
|
|
304
|
+
}
|
|
305
|
+
return [...new Set(names)];
|
|
306
|
+
}
|
|
307
|
+
|
|
229
308
|
/**
|
|
230
309
|
* Formats a singular or plural word for a count.
|
|
231
310
|
*
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { hostname } from 'node:os';
|
|
2
|
+
import { ulid } from '@db3.ai/pure/ulid';
|
|
3
|
+
import type { QueueWorkerSnapshot, QueueSelection, QueueLogger } from './contracts';
|
|
4
|
+
|
|
5
|
+
/** Serializes small presence writes; a slow or unavailable sink cannot build an unbounded backlog or stop jobs. */
|
|
6
|
+
export class QueueWorkerReporter {
|
|
7
|
+
#snapshot: QueueWorkerSnapshot;
|
|
8
|
+
#timer: ReturnType<typeof setInterval> | null = null;
|
|
9
|
+
#pending: Promise<void> | null = null;
|
|
10
|
+
#dirty = false;
|
|
11
|
+
|
|
12
|
+
/** Creates an identity unique to this worker, including multiple workers in the same process. */
|
|
13
|
+
constructor(selection: QueueSelection, private readonly publish: (snapshot: QueueWorkerSnapshot) => Promise<void>, private readonly logger?: QueueLogger) {
|
|
14
|
+
const now = new Date().toISOString();
|
|
15
|
+
this.#snapshot = { id: ulid(), name: process.env.QUEUE_WORKER_NAME || 'queue-worker', hostname: hostname(), pid: process.pid, selection, status: 'idle', currentQueue: null, currentJobId: null, startedAt: now, heartbeatAt: now };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Starts liveness updates, including while a long job is awaiting network work. */
|
|
19
|
+
start(): void {
|
|
20
|
+
if (this.#timer) return;
|
|
21
|
+
this.#timer = setInterval(() => { void this.flush(); }, 10_000);
|
|
22
|
+
this.#timer.unref();
|
|
23
|
+
this.update('idle');
|
|
24
|
+
void this.flush();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Updates the latest state without queuing one database write per job or poll. */
|
|
28
|
+
update(status: QueueWorkerSnapshot['status'], currentQueue: string | null = null, currentJobId: string | null = null): void {
|
|
29
|
+
if (status === 'stopping') { currentQueue = this.#snapshot.currentQueue; currentJobId = this.#snapshot.currentJobId; }
|
|
30
|
+
if (this.#snapshot.status === status && this.#snapshot.currentQueue === currentQueue && this.#snapshot.currentJobId === currentJobId) return;
|
|
31
|
+
this.#snapshot = { ...this.#snapshot, status, currentQueue, currentJobId };
|
|
32
|
+
void this.flush();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Stops timers immediately and flushes a final state when the worker drains. */
|
|
36
|
+
async stop(): Promise<void> {
|
|
37
|
+
if (this.#timer) clearInterval(this.#timer);
|
|
38
|
+
this.#timer = null;
|
|
39
|
+
this.update('stopped');
|
|
40
|
+
await this.flush();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Coalesces concurrent updates and isolates sink errors from claims and handlers. */
|
|
44
|
+
async flush(): Promise<void> {
|
|
45
|
+
this.#dirty = true;
|
|
46
|
+
if (this.#pending) return this.#pending;
|
|
47
|
+
this.#pending = this.#drain();
|
|
48
|
+
try { await this.#pending; } finally { this.#pending = null; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Writes the most recent immutable snapshot, retaining changes made while a write was pending. */
|
|
52
|
+
async #drain(): Promise<void> {
|
|
53
|
+
while (this.#dirty) {
|
|
54
|
+
this.#dirty = false;
|
|
55
|
+
try { await this.publish(structuredClone({ ...this.#snapshot, heartbeatAt: new Date().toISOString() })); }
|
|
56
|
+
catch { this.logger?.warn?.('[queue] Unable to publish worker presence.'); }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -118,7 +118,7 @@ export abstract class QueueableJob<TData extends Record<string, unknown> = Recor
|
|
|
118
118
|
* @param value - Unknown value to inspect.
|
|
119
119
|
* @returns True when the value is a queueable job instance.
|
|
120
120
|
*/
|
|
121
|
-
export function isQueueableJob(value: unknown): value is
|
|
121
|
+
export function isQueueableJob(value: unknown): value is queue.QueueableJobContract {
|
|
122
122
|
return Boolean(
|
|
123
123
|
value
|
|
124
124
|
&& typeof value === 'object'
|
|
@@ -162,6 +162,6 @@ export function queueableJobName(Job: queue.QueueableJobClass | { name: string;
|
|
|
162
162
|
* @param data - JSON payload data stored in the queue envelope.
|
|
163
163
|
* @returns Rehydrated queueable job instance.
|
|
164
164
|
*/
|
|
165
|
-
export function queueableJobFromJSON(Job: queue.QueueableJobClass, data: Record<string, unknown>):
|
|
165
|
+
export function queueableJobFromJSON(Job: queue.QueueableJobClass, data: Record<string, unknown>): queue.QueueableJobContract | Promise<queue.QueueableJobContract> {
|
|
166
166
|
return Job.fromJSON(data);
|
|
167
167
|
}
|