@db3.ai/app 0.1.0-beta.1 → 0.1.0-beta.4
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 +68 -9
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QueueMonitor, QueueMonitorOptions } from '../queueMonitor';
|
|
2
2
|
import type { QueueEvents } from '../QueueEvents';
|
|
3
|
-
import type { QueueableJob } from '
|
|
3
|
+
import type { QueueableJobContract as QueueableJob } from './QueueableJobContract';
|
|
4
4
|
import type { QueueLifecycleErrorHandler } from './QueueEvents';
|
|
5
5
|
import type { DispatchOptions, QueueDriverName, QueueJob, QueueJobId, QueueProcessResult } from './QueuePayload';
|
|
6
6
|
import type { QueueRetryBackoffStrategy } from './QueueRetry';
|
|
@@ -8,6 +8,9 @@ import type { QueueDriver, QueueFailedJob } from './QueueDriver';
|
|
|
8
8
|
import type { QueueableJobClass } from './QueueableJob';
|
|
9
9
|
import type { QueueWorkerLifecycle, QueueWorkerOptions } from './QueueWorkerLifecycle';
|
|
10
10
|
import type { RedisQueueDriverOptions } from './RedisQueueDriver';
|
|
11
|
+
import type { QueueJobRunner } from './QueueJobRunner';
|
|
12
|
+
import type { QueueWorkerSnapshot } from './QueueWorkerSnapshot';
|
|
13
|
+
import type { QueueSelection } from './QueueSelection';
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Function-style handler registered for a durable queued job name.
|
|
@@ -33,6 +36,8 @@ export type QueueJobResolver = (
|
|
|
33
36
|
* Queue service configuration shared by dispatching, processing, and worker startup.
|
|
34
37
|
*/
|
|
35
38
|
export interface QueueOptions {
|
|
39
|
+
/** Receives serialized worker presence every ten seconds and on state changes; errors are isolated from job execution. */
|
|
40
|
+
onWorkerHeartbeat?: (snapshot: QueueWorkerSnapshot) => void | Promise<void>;
|
|
36
41
|
/**
|
|
37
42
|
* Default queue name used when dispatching or processing jobs without an explicit queue.
|
|
38
43
|
*/
|
|
@@ -109,6 +114,10 @@ export interface QueueWorkOptions {
|
|
|
109
114
|
* Public queue service API used by application code and queueable jobs.
|
|
110
115
|
*/
|
|
111
116
|
export interface QueueService {
|
|
117
|
+
/** Registers an owned set of stable names and returns idempotent runtime cleanup. */
|
|
118
|
+
registerJobs(owner: string, jobs: readonly QueueableJobClass[], run?: QueueJobRunner): () => void;
|
|
119
|
+
/** Checks all active storage, including chained work; unsupported drivers reject rather than assume empty. */
|
|
120
|
+
hasPendingJobs(jobPrefix: string): Promise<boolean>;
|
|
112
121
|
/**
|
|
113
122
|
* Always-on asynchronous queue lifecycle event dispatcher.
|
|
114
123
|
*/
|
|
@@ -209,11 +218,17 @@ export interface QueueService {
|
|
|
209
218
|
workNextJob(queue?: string, options?: QueueWorkOptions): Promise<QueueProcessResult | null>;
|
|
210
219
|
|
|
211
220
|
/**
|
|
212
|
-
*
|
|
221
|
+
* Discovers names with pending work in the configured driver namespace.
|
|
222
|
+
* @throws When the driver does not implement wildcard discovery.
|
|
223
|
+
*/
|
|
224
|
+
queueNames(): Promise<string[]>;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Starts a polling worker for a named queue or round-robin selection.
|
|
213
228
|
*
|
|
214
|
-
* @param queue -
|
|
229
|
+
* @param queue - Exact queue name or named-queue selection the worker should poll.
|
|
215
230
|
* @param options - Worker lifecycle and polling options.
|
|
216
231
|
* @returns Worker lifecycle controls, or null when worker startup is disabled.
|
|
217
232
|
*/
|
|
218
|
-
startWorker(queue?: string, options?: QueueWorkerOptions): QueueWorkerLifecycle | null;
|
|
233
|
+
startWorker(queue?: string | QueueSelection, options?: QueueWorkerOptions): QueueWorkerLifecycle | null;
|
|
219
234
|
}
|
|
@@ -60,12 +60,12 @@ export interface QueueWorkerLifecycle {
|
|
|
60
60
|
start(): void;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Stops
|
|
63
|
+
* Stops claiming further work without aborting the current job.
|
|
64
64
|
*/
|
|
65
65
|
stop(): void;
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Stops
|
|
68
|
+
* Stops claiming further work and resolves after the current job has completed.
|
|
69
69
|
*/
|
|
70
70
|
stopAndDrain(): Promise<void>;
|
|
71
71
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { QueueSelection } from './QueueSelection';
|
|
2
|
+
|
|
3
|
+
/** Process-scoped worker presence, independent of the queue transport and job payloads. */
|
|
4
|
+
export interface QueueWorkerSnapshot {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
hostname: string;
|
|
8
|
+
pid: number;
|
|
9
|
+
selection: QueueSelection;
|
|
10
|
+
status: 'idle' | 'busy' | 'stopping' | 'stopped';
|
|
11
|
+
currentQueue: string | null;
|
|
12
|
+
currentJobId: string | null;
|
|
13
|
+
startedAt: string;
|
|
14
|
+
heartbeatAt: string;
|
|
15
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Queue } from '../Queue';
|
|
2
|
-
import type {
|
|
2
|
+
import type { QueueableJobContract } from './QueueableJobContract';
|
|
3
3
|
import type { QueueJob, SerializedQueuedJob } from './QueuePayload';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -15,6 +15,10 @@ export interface QueueableJobContext {
|
|
|
15
15
|
job: QueueJob;
|
|
16
16
|
/** Queue service that is processing this job. */
|
|
17
17
|
queue: Queue;
|
|
18
|
+
/** Confirms this delivery still belongs to the current worker before publishing effects. */
|
|
19
|
+
assertOwnership?(): Promise<void>;
|
|
20
|
+
/** Uses the queue retry policy, including deadline and backoff, for a failed attempt. */
|
|
21
|
+
willRetry?(error: unknown): boolean;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
/**
|
|
@@ -38,7 +42,7 @@ export interface QueueableJobFailureContext extends QueueableJobContext {
|
|
|
38
42
|
/**
|
|
39
43
|
* Constructor contract for serializable queueable job classes.
|
|
40
44
|
*/
|
|
41
|
-
export interface QueueableJobClass<TJob extends
|
|
45
|
+
export interface QueueableJobClass<TJob extends QueueableJobContract = QueueableJobContract> {
|
|
42
46
|
/** Runtime class name used as the default durable job name. */
|
|
43
47
|
readonly name: string;
|
|
44
48
|
/** Optional durable job name override. */
|
|
@@ -52,5 +56,5 @@ export interface QueueableJobClass<TJob extends QueueableJob = QueueableJob> {
|
|
|
52
56
|
* @param data - JSON-safe data stored in the queue payload.
|
|
53
57
|
* @returns Queueable job instance ready to handle.
|
|
54
58
|
*/
|
|
55
|
-
fromJSON(data: Record<string, unknown>): TJob
|
|
59
|
+
fromJSON(data: Record<string, unknown>): TJob | Promise<TJob>;
|
|
56
60
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { QueueableJobContext, QueueableJobFailureContext, QueueableJobRetryContext, SerializedQueueableJob } from './QueueableJob';
|
|
2
|
+
|
|
3
|
+
/** Execution and delivery contract shared by command jobs and persisted runnable records. */
|
|
4
|
+
export interface QueueableJobContract {
|
|
5
|
+
/** Serializes delivery data; persisted runs send only their identity and generation. */
|
|
6
|
+
serialize(): SerializedQueueableJob;
|
|
7
|
+
/** Performs application work after the queue has claimed delivery. */
|
|
8
|
+
handle(context: QueueableJobContext): Promise<void>;
|
|
9
|
+
/** Observes a committed retry; important state must not depend solely on this hook. */
|
|
10
|
+
onRetry(context: QueueableJobRetryContext): Promise<void>;
|
|
11
|
+
/** Observes a committed terminal failure. */
|
|
12
|
+
onFinalFailure(context: QueueableJobFailureContext): Promise<void>;
|
|
13
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './QueueDriver';
|
|
2
|
+
export * from './QueueSelection';
|
|
2
3
|
export * from './QueueEvents';
|
|
3
4
|
export * from './QueuePayload';
|
|
4
5
|
export * from './QueueRetry';
|
|
@@ -7,3 +8,8 @@ export * from './QueueService';
|
|
|
7
8
|
export * from './QueueWorkerLifecycle';
|
|
8
9
|
export * from './RedisQueueDriver';
|
|
9
10
|
export * from './CreateJob';
|
|
11
|
+
export type { QueueJobRunner } from './QueueJobRunner';
|
|
12
|
+
|
|
13
|
+
export * from './QueueableJobContract';
|
|
14
|
+
|
|
15
|
+
export type { QueueWorkerSnapshot } from './QueueWorkerSnapshot';
|
|
@@ -2,6 +2,7 @@ import { ActiveRecord, RecordValidationError, withQueryMonitorCategory, type Dat
|
|
|
2
2
|
import { FailedJob } from '../FailedJob';
|
|
3
3
|
import { QueuedJob } from '../QueuedJob';
|
|
4
4
|
import type * as queue from '../contracts';
|
|
5
|
+
import { jobEnvelopeHasPrefix } from '../jobEnvelopeHasPrefix';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Queue driver that stores pending and failed jobs in the app database.
|
|
@@ -16,6 +17,26 @@ export class DatabaseQueueDriver implements queue.QueueDriver {
|
|
|
16
17
|
*/
|
|
17
18
|
constructor(private readonly db: Database) {}
|
|
18
19
|
|
|
20
|
+
/** Discovers queue names from the queue index, including delayed and reserved work. */
|
|
21
|
+
async queueNames(): Promise<string[]> {
|
|
22
|
+
return withQueueQueryCategory(() => ActiveRecord.withDb(this.db.knex, async () => {
|
|
23
|
+
const rows = await QueuedJob.query().toKnex().distinct('queue').orderBy('queue');
|
|
24
|
+
return rows.map(row => String(row.queue));
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Scans active work in bounded pages during quiesced maintenance, including delayed and chained payloads. */
|
|
29
|
+
async hasPendingJobs(jobPrefix: string): Promise<boolean> {
|
|
30
|
+
if (!(await this.db.knex.schema.hasTable(QueuedJob.table))) return false;
|
|
31
|
+
let after: number | string = 0;
|
|
32
|
+
while (true) {
|
|
33
|
+
const rows = await this.db.knex(QueuedJob.table).select('id', 'payload').where('id', '>', after).orderBy('id').limit(100);
|
|
34
|
+
for (const row of rows) if (jobEnvelopeHasPrefix(typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload, jobPrefix)) return true;
|
|
35
|
+
if (rows.length < 100) return false;
|
|
36
|
+
after = rows[rows.length - 1].id;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
19
40
|
/**
|
|
20
41
|
* Persists a job in the `jobs` table.
|
|
21
42
|
*
|
|
@@ -25,7 +46,8 @@ export class DatabaseQueueDriver implements queue.QueueDriver {
|
|
|
25
46
|
async push<TData extends Record<string, unknown>>(
|
|
26
47
|
job: queue.QueueDriverPushInput<TData>,
|
|
27
48
|
): Promise<queue.QueueJobId> {
|
|
28
|
-
|
|
49
|
+
const connection = ActiveRecord.getScopedDb() ?? this.db.knex;
|
|
50
|
+
return withQueueQueryCategory(() => ActiveRecord.withDb(connection, async () => {
|
|
29
51
|
const record = QueuedJob.create({
|
|
30
52
|
queue: job.queue,
|
|
31
53
|
payload: job.payload,
|
|
@@ -45,7 +67,7 @@ export class DatabaseQueueDriver implements queue.QueueDriver {
|
|
|
45
67
|
isInsert: true,
|
|
46
68
|
onlyDirty: false,
|
|
47
69
|
});
|
|
48
|
-
const [jobId] = await
|
|
70
|
+
const [jobId] = await connection(QueuedJob.table).insert(row);
|
|
49
71
|
|
|
50
72
|
return Number(jobId);
|
|
51
73
|
}));
|
|
@@ -109,7 +131,7 @@ export class DatabaseQueueDriver implements queue.QueueDriver {
|
|
|
109
131
|
* @returns True when the current attempt still owns the job.
|
|
110
132
|
*/
|
|
111
133
|
async touch(job: queue.QueueJob, _retryAfterSeconds: number): Promise<boolean> {
|
|
112
|
-
return withQueueQueryCategory(() => ActiveRecord.withDb(this.db.knex, async () => {
|
|
134
|
+
return withQueueQueryCategory(() => ActiveRecord.withDb(ActiveRecord.getScopedDb() ?? this.db.knex, async () => {
|
|
113
135
|
const updated = await QueuedJob
|
|
114
136
|
.where('id', job.id)
|
|
115
137
|
.where('attempts', job.attempts)
|
|
@@ -2,6 +2,7 @@ import { createClient, type RedisArgument, type RedisClientOptions, type RedisCl
|
|
|
2
2
|
import { unknownErrorMessage } from '@db3.ai/pure';
|
|
3
3
|
|
|
4
4
|
import type * as queue from '../contracts';
|
|
5
|
+
import { jobEnvelopeHasPrefix } from '../jobEnvelopeHasPrefix';
|
|
5
6
|
|
|
6
7
|
type RedisCommandArgument = string | number;
|
|
7
8
|
type ResolvedRedisQueueDriverOptions = Required<Pick<queue.RedisQueueDriverOptions, 'host' | 'port' | 'keyPrefix' | 'connectionTimeoutMs'>> & queue.RedisQueueDriverOptions;
|
|
@@ -208,6 +209,41 @@ export class RedisQueueDriver implements queue.QueueDriver {
|
|
|
208
209
|
return String(id);
|
|
209
210
|
}
|
|
210
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Discovers queue names from ready/delayed/reserved keys without reading payloads.
|
|
214
|
+
* SCAN includes existing queues written before this capability was introduced.
|
|
215
|
+
*/
|
|
216
|
+
async queueNames(): Promise<string[]> {
|
|
217
|
+
const prefix = `${this.#key('queues')}:`;
|
|
218
|
+
const pattern = prefix.replace(/[\\*?\[\]]/g, '\\$&') + '*';
|
|
219
|
+
const names = new Set<string>();
|
|
220
|
+
let cursor = '0';
|
|
221
|
+
do {
|
|
222
|
+
const [next, keys] = await this.#command<[string, string[]]>(['SCAN', cursor, 'MATCH', pattern, 'COUNT', 100]);
|
|
223
|
+
cursor = String(next);
|
|
224
|
+
for (const key of keys) {
|
|
225
|
+
if (!key.startsWith(prefix)) continue;
|
|
226
|
+
const suffix = key.slice(prefix.length).match(/^(.*):(ready|delayed|reserved)$/);
|
|
227
|
+
if (suffix?.[1]) names.add(suffix[1]);
|
|
228
|
+
}
|
|
229
|
+
} while (cursor !== '0');
|
|
230
|
+
return [...names].sort();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Scans active Redis payloads during quiesced maintenance; includes delayed, reserved and chained jobs. */
|
|
234
|
+
async hasPendingJobs(jobPrefix: string): Promise<boolean> {
|
|
235
|
+
let cursor = '0';
|
|
236
|
+
do {
|
|
237
|
+
const result = await this.#command<[string, string[]]>(['HSCAN', this.#jobsKey(), cursor, 'COUNT', 100]);
|
|
238
|
+
cursor = String(result[0]);
|
|
239
|
+
for (let index = 1; index < result[1].length; index += 2) {
|
|
240
|
+
const record = JSON.parse(result[1][index]) as RedisQueuedJobRecord;
|
|
241
|
+
if (jobEnvelopeHasPrefix(record.payload, jobPrefix)) return true;
|
|
242
|
+
}
|
|
243
|
+
} while (cursor !== '0');
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
211
247
|
/**
|
|
212
248
|
* Claims the next available job from a Redis queue.
|
|
213
249
|
*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { createGeneratedTestDatabase } from '@db3.ai/app/db/test/db';
|
|
3
|
+
import { FailedJob, QueuedJob, QueueWorker } from '@db3.ai/app/queue';
|
|
4
|
+
import { App } from '@db3.ai/app/server';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Demonstrates protected and shared workers using real durable named queues.
|
|
8
|
+
* Creates a disposable database and closes it even when processing fails.
|
|
9
|
+
* Handlers represent local work only; no external services are contacted.
|
|
10
|
+
*/
|
|
11
|
+
export async function runQueueSelection() {
|
|
12
|
+
const database = await createGeneratedTestDatabase('queue_selection');
|
|
13
|
+
const application = new App({ db: database.db, queue: { queueMonitor: false } });
|
|
14
|
+
try {
|
|
15
|
+
await application.db.install(QueuedJob, FailedJob);
|
|
16
|
+
const handled: string[] = [];
|
|
17
|
+
application.queue.registerHandler('example.work', async job => { handled.push(job.queue); });
|
|
18
|
+
for (const queue of ['articles', 'article-images', 'default']) {
|
|
19
|
+
await application.queue.dispatch('example.work', {}, { queue });
|
|
20
|
+
}
|
|
21
|
+
const general = new QueueWorker(application.queue, { queues: '*', excludeQueues: ['articles', 'article-images'] });
|
|
22
|
+
const shared = new QueueWorker(application.queue, { queues: '*' });
|
|
23
|
+
await general.workOnce();
|
|
24
|
+
const protectedIdle = await general.workOnce() === null;
|
|
25
|
+
while (await shared.workOnce()) { /* Drain remaining eligible work. */ }
|
|
26
|
+
return { handled, protectedIdle, remaining: await QueuedJob.query().count() };
|
|
27
|
+
} finally {
|
|
28
|
+
try { await application.close(); } finally { await database.destroy(); }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
33
|
+
console.log(JSON.stringify(await runQueueSelection(), null, 2));
|
|
34
|
+
}
|
package/src/queue/index.ts
CHANGED
|
@@ -11,3 +11,10 @@ export * from './drivers/DatabaseQueueDriver';
|
|
|
11
11
|
export * from './drivers/RedisQueueDriver';
|
|
12
12
|
export * from './contracts';
|
|
13
13
|
export * from './createJob';
|
|
14
|
+
|
|
15
|
+
export * from './QueueTerminalError';
|
|
16
|
+
|
|
17
|
+
export * from './RunnableJob';
|
|
18
|
+
export * from './JobRunAttempt';
|
|
19
|
+
export * from './JobRunEvent';
|
|
20
|
+
export * from './JobRunStoppedError';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SerializedQueuedJob } from './contracts';
|
|
2
|
+
|
|
3
|
+
/** Checks active work and nested chained continuations without inspecting application-owned payload data. */
|
|
4
|
+
export function jobEnvelopeHasPrefix(envelope: Pick<SerializedQueuedJob, 'job' | 'chained'>, prefix: string): boolean {
|
|
5
|
+
return envelope.job.startsWith(prefix) || Boolean(envelope.chained?.some(job => jobEnvelopeHasPrefix(job, prefix)));
|
|
6
|
+
}
|
package/src/scheduler/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Scheduler
|
|
2
2
|
|
|
3
3
|
The framework scheduler keeps recurring definitions in application code and
|
|
4
|
-
stores one durable occurrence for every due named minute. It supports daily
|
|
4
|
+
stores one durable occurrence for every due named minute. It supports minute and daily
|
|
5
5
|
jobs and short inline calls:
|
|
6
6
|
|
|
7
7
|
```ts
|
|
@@ -28,10 +28,10 @@ A `QueueableJob` class uses its durable job name as the schedule name, so
|
|
|
28
28
|
stable name because function identity is not durable. Names must be unique
|
|
29
29
|
within one application schedule.
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
`dailyAt('HH:mm')
|
|
33
|
-
timezone. The
|
|
34
|
-
|
|
31
|
+
Use `everyMinute()` for bounded background collection, or `daily()` and
|
|
32
|
+
`dailyAt('HH:mm')` for daily work. Minute schedules are independent of timezone;
|
|
33
|
+
daily times use UTC unless `.timezone(...)` selects an IANA timezone. The worker covers every elapsed UTC minute in order. Applications
|
|
34
|
+
can provide durable progress to preserve that coverage across downtime.
|
|
35
35
|
|
|
36
36
|
## Runtime
|
|
37
37
|
|
|
@@ -51,8 +51,12 @@ There are two execution boundaries:
|
|
|
51
51
|
|
|
52
52
|
- `scheduler.runDue()` evaluates one minute and is suitable for cron or a
|
|
53
53
|
one-off framework command.
|
|
54
|
-
- `SchedulerWorker`
|
|
55
|
-
|
|
54
|
+
- `SchedulerWorker` starts with the current minute, then evaluates every minute
|
|
55
|
+
through the current clock time without overlapping ticks in one process.
|
|
56
|
+
All tasks due in a minute retain that original due time, even if dispatching
|
|
57
|
+
them crosses several later minutes. Those later minutes are evaluated next.
|
|
58
|
+
Late or early timers cannot discard minutes. Catch-up yields after sixty
|
|
59
|
+
evaluations to allow shutdown and other work; it does not truncate the backlog.
|
|
56
60
|
|
|
57
61
|
Run one dedicated scheduler process in production. Multiple processes are
|
|
58
62
|
still safe: `scheduled_occurrences` has a unique `(name, scheduled_for)` claim,
|
|
@@ -62,6 +66,30 @@ Scheduled jobs are the preferred path. The scheduler only creates and
|
|
|
62
66
|
dispatches the job; queue workers perform the expensive work. `call()` runs
|
|
63
67
|
inside the scheduler process, so reserve it for short operations.
|
|
64
68
|
|
|
69
|
+
### Durable worker progress
|
|
70
|
+
|
|
71
|
+
Register `SchedulerCheckpointRecord` in the application's migration model registry,
|
|
72
|
+
then pass `SchedulerCheckpointRecord.checkpoint('my-scheduler')` as `checkpoint`
|
|
73
|
+
to `SchedulerWorker` or `runSchedulerConsole`. It persists restart progress in
|
|
74
|
+
`scheduler_checkpoints`, with atomic initialization and monotonic updates.
|
|
75
|
+
A custom `SchedulerCheckpoint` can supply another backend. Its operations are:
|
|
76
|
+
|
|
77
|
+
- `load(firstMinute)` atomically establishes the minute immediately before
|
|
78
|
+
`firstMinute` on first use, or returns the existing last evaluated UTC minute.
|
|
79
|
+
- `save(evaluatedFor)` durably advances that minute after the complete batch.
|
|
80
|
+
Concurrent writers must never move it backwards.
|
|
81
|
+
|
|
82
|
+
Coverage is saved before dispatch; replacement workers resume after the cursor.
|
|
83
|
+
Occurrence claims deduplicate replay. Without a checkpoint, startup begins in the
|
|
84
|
+
current minute. A liveness timestamp cannot establish which minute was evaluated.
|
|
85
|
+
|
|
86
|
+
An incomplete evaluation or failed checkpoint write retries the same minute
|
|
87
|
+
after one second. Individual event failures are recorded as failed occurrences;
|
|
88
|
+
they do not prevent the rest of that batch or later minutes from being evaluated.
|
|
89
|
+
Catch-up uses currently registered definitions and invokes factories at the time
|
|
90
|
+
of dispatch. Business-date-sensitive jobs must define their own date policy.
|
|
91
|
+
One-off `scheduler:run`, list and history commands leave the worker cursor alone.
|
|
92
|
+
|
|
65
93
|
## Occurrence History
|
|
66
94
|
|
|
67
95
|
`ScheduledOccurrence` owns the `scheduled_occurrences` table. Its lifecycle
|
|
@@ -132,8 +160,21 @@ or runs an application's historical schedules. The website includes the exact
|
|
|
132
160
|
consumer-copyable test. Framework contributors run
|
|
133
161
|
`npm run test:service --workspace packages/app -- scheduler`.
|
|
134
162
|
|
|
163
|
+
The [elapsed-window runner](./examples/runSchedulerWindow.ts) demonstrates a
|
|
164
|
+
boundary crossed during a heartbeat and a slow batch spanning the next minute,
|
|
165
|
+
using the real SQL queue and a controlled clock. Run
|
|
166
|
+
`npx tsx examples/runSchedulerWindow.ts` with the same dedicated test credentials.
|
|
167
|
+
|
|
135
168
|
A daily local time may not occur during a spring DST transition and may map to
|
|
136
169
|
two UTC minutes during an autumn transition. Claims are keyed by UTC minute,
|
|
137
|
-
not by a local business date.
|
|
138
|
-
|
|
170
|
+
not by a local business date. A nonexistent DST wall-clock minute is not a
|
|
171
|
+
missed UTC evaluation. There is no exactly-once side-effect guarantee.
|
|
172
|
+
Claims and queue dispatch are separate operations; monitor and
|
|
139
173
|
reconcile occurrences left claimed by an interrupted dispatch.
|
|
174
|
+
|
|
175
|
+
`SchedulerWorkerOptions.onTick` (also accepted by the scheduler console) lets
|
|
176
|
+
an application persist a successful tick heartbeat, with `result.evaluatedFor`
|
|
177
|
+
identifying the covered minute. It runs only when `runDue` returns without
|
|
178
|
+
failures. Hook failures are logged and cause the same minute to be reconsidered.
|
|
179
|
+
An independent monitor must detect stale heartbeats; scheduler self-logging
|
|
180
|
+
cannot detect a scheduler process that has stopped.
|
|
@@ -7,7 +7,7 @@ import { dailyScheduleIsDue, normalizeDailyTime, normalizeScheduleTimezone } fro
|
|
|
7
7
|
export abstract class ScheduledEvent {
|
|
8
8
|
abstract readonly kind: scheduler.ScheduledTaskKind;
|
|
9
9
|
protected configuredName: string | null = null;
|
|
10
|
-
protected configuredFrequency: scheduler.
|
|
10
|
+
protected configuredFrequency: scheduler.ScheduleFrequency | null = null;
|
|
11
11
|
protected configuredTimezone = 'UTC';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -32,6 +32,18 @@ export abstract class ScheduledEvent {
|
|
|
32
32
|
return this;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** Schedules one occurrence for each evaluated UTC minute, independent of timezone. */
|
|
36
|
+
everyMinute(): this {
|
|
37
|
+
this.configuredFrequency = { type: 'minute' };
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Schedules one occurrence at minute zero of each UTC hour, independent of timezone. */
|
|
42
|
+
hourly(): this {
|
|
43
|
+
this.configuredFrequency = { type: 'hourly' };
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
/**
|
|
36
48
|
* Schedules this task every day at local midnight.
|
|
37
49
|
*
|
|
@@ -72,11 +84,14 @@ export abstract class ScheduledEvent {
|
|
|
72
84
|
* Returns whether this event is due at one canonical UTC minute.
|
|
73
85
|
*
|
|
74
86
|
* @param instant - UTC minute being evaluated.
|
|
75
|
-
* @returns True when the configured
|
|
87
|
+
* @returns True when the configured frequency matches a valid minute.
|
|
76
88
|
*/
|
|
77
89
|
isDue(instant: Date): boolean {
|
|
78
90
|
const frequency = this.frequency();
|
|
79
91
|
|
|
92
|
+
if (frequency.type === 'minute') return Number.isFinite(instant.getTime());
|
|
93
|
+
if (frequency.type === 'hourly') return Number.isFinite(instant.getTime()) && instant.getUTCMinutes() === 0;
|
|
94
|
+
|
|
80
95
|
return dailyScheduleIsDue(
|
|
81
96
|
instant,
|
|
82
97
|
frequency.time,
|
|
@@ -119,11 +134,11 @@ export abstract class ScheduledEvent {
|
|
|
119
134
|
}
|
|
120
135
|
|
|
121
136
|
/**
|
|
122
|
-
* Returns the
|
|
137
|
+
* Returns the frequency or throws when none was configured.
|
|
123
138
|
*
|
|
124
|
-
* @returns Configured
|
|
139
|
+
* @returns Configured frequency.
|
|
125
140
|
*/
|
|
126
|
-
private frequency(): scheduler.
|
|
141
|
+
private frequency(): scheduler.ScheduleFrequency {
|
|
127
142
|
if (!this.configuredFrequency) {
|
|
128
143
|
throw new Error(`Scheduled event "${this.resolvedName()}" requires a frequency.`);
|
|
129
144
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isQueueableJob, isQueueableJobClass, queueableJobName, type QueueableJob } from '../queue';
|
|
1
|
+
import { isQueueableJob, isQueueableJobClass, queueableJobName, type QueueableJobContract as QueueableJob } from '../queue';
|
|
2
2
|
import type * as scheduler from './contracts';
|
|
3
3
|
import { ScheduledEvent } from './ScheduledEvent';
|
|
4
4
|
|
|
@@ -14,6 +14,8 @@ import { utcMinute } from './scheduleTime';
|
|
|
14
14
|
*/
|
|
15
15
|
export class Scheduler {
|
|
16
16
|
readonly #events: ScheduledEvent[] = [];
|
|
17
|
+
readonly #owners = new Map<ScheduledEvent, { owner: string; run: scheduler.ScheduleRunner }>();
|
|
18
|
+
readonly #groups = new Set<string>();
|
|
17
19
|
readonly #unsubscribeQueueEvents: () => void;
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -61,13 +63,47 @@ export class Scheduler {
|
|
|
61
63
|
return event;
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Atomically registers an owned schedule group. Declarations are synchronous and do not execute jobs.
|
|
68
|
+
* Names are prefixed with the owner unless already namespaced. Failed declarations register nothing.
|
|
69
|
+
*/
|
|
70
|
+
register(owner: string, configure: (schedule: scheduler.Schedule) => void, run: scheduler.ScheduleRunner = operation => operation()): scheduler.ScheduleRegistration {
|
|
71
|
+
if (!/^[a-z][a-z0-9_]{0,39}$/.test(owner) || this.#groups.has(owner)) throw new Error(`Invalid or duplicate schedule owner "${owner}".`);
|
|
72
|
+
const events: ScheduledEvent[] = [];
|
|
73
|
+
const declaration = configure({
|
|
74
|
+
/** Collects a queued declaration without attaching it to the running scheduler yet. */
|
|
75
|
+
job: source => { const event = new ScheduledJob(source); events.push(event); return event; },
|
|
76
|
+
/** Collects a short callback under the same group ownership. */
|
|
77
|
+
call: handler => { const event = new ScheduledCall(handler); events.push(event); return event; },
|
|
78
|
+
}) as unknown;
|
|
79
|
+
if (declaration && typeof (declaration as Promise<unknown>).then === 'function') throw new Error('Schedule declarations must be synchronous.');
|
|
80
|
+
const names = new Set(this.definitions().map(definition => definition.name));
|
|
81
|
+
const definitions = events.map(event => {
|
|
82
|
+
const name = event.definition().name;
|
|
83
|
+
if (!name.startsWith(`${owner}.`)) event.name(`${owner}.${name}`);
|
|
84
|
+
const definition = { ...event.definition(), owner };
|
|
85
|
+
if (names.has(definition.name)) throw new Error(`Duplicate scheduled event name "${definition.name}".`);
|
|
86
|
+
names.add(definition.name);
|
|
87
|
+
return definition;
|
|
88
|
+
});
|
|
89
|
+
this.#groups.add(owner);
|
|
90
|
+
for (const event of events) { this.#events.push(event); this.#owners.set(event, { owner, run }); }
|
|
91
|
+
let closed = false;
|
|
92
|
+
return { definitions, close: () => {
|
|
93
|
+
if (closed) return;
|
|
94
|
+
closed = true;
|
|
95
|
+
for (const event of events) { this.#events.splice(this.#events.indexOf(event), 1); this.#owners.delete(event); }
|
|
96
|
+
this.#groups.delete(owner);
|
|
97
|
+
} };
|
|
98
|
+
}
|
|
99
|
+
|
|
64
100
|
/**
|
|
65
101
|
* Returns validated normalized definitions in registration order.
|
|
66
102
|
*
|
|
67
103
|
* @returns Registered scheduler definitions.
|
|
68
104
|
*/
|
|
69
105
|
definitions(): scheduler.ScheduledTaskDefinition[] {
|
|
70
|
-
const definitions = this.#events.map(event => event.definition());
|
|
106
|
+
const definitions = this.#events.map(event => ({ ...event.definition(), ...(this.#owners.has(event) ? { owner: this.#owners.get(event)!.owner } : {}) }));
|
|
71
107
|
const names = new Set<string>();
|
|
72
108
|
|
|
73
109
|
for (const definition of definitions) {
|
|
@@ -111,37 +147,47 @@ export class Scheduler {
|
|
|
111
147
|
};
|
|
112
148
|
|
|
113
149
|
for (const event of dueEvents) {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
150
|
+
const owner = this.#owners.get(event);
|
|
151
|
+
/** Runs a complete occurrence inside its registration owner's lifecycle gate. */
|
|
152
|
+
const execute = async (): Promise<void> => {
|
|
153
|
+
if (!this.#events.includes(event)) { result.skipped++; return; }
|
|
154
|
+
const definition = event.definition();
|
|
155
|
+
const occurrence = await ActiveRecord.withDb(this.database.knex, () => {
|
|
156
|
+
return ScheduledOccurrence.claim({
|
|
157
|
+
name: definition.name,
|
|
158
|
+
kind: definition.kind,
|
|
159
|
+
scheduledFor: evaluatedFor,
|
|
160
|
+
jobName: definition.jobName,
|
|
161
|
+
});
|
|
121
162
|
});
|
|
122
|
-
});
|
|
123
163
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
164
|
+
if (!occurrence) {
|
|
165
|
+
result.skipped += 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
128
168
|
|
|
129
|
-
|
|
169
|
+
result.claimed += 1;
|
|
130
170
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
171
|
+
try {
|
|
172
|
+
if (event instanceof ScheduledJob) {
|
|
173
|
+
await this.dispatchJob(event, occurrence);
|
|
174
|
+
result.dispatched += 1;
|
|
175
|
+
} else if (event instanceof ScheduledCall) {
|
|
176
|
+
await this.runCall(event, occurrence);
|
|
177
|
+
result.completed += 1;
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
await this.failOccurrence(occurrence, error);
|
|
181
|
+
result.failures.push({
|
|
182
|
+
name: definition.name,
|
|
183
|
+
error: errorDetails(error),
|
|
184
|
+
});
|
|
138
185
|
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
});
|
|
186
|
+
};
|
|
187
|
+
if (!owner) await execute();
|
|
188
|
+
else {
|
|
189
|
+
try { await owner.run(execute); }
|
|
190
|
+
catch (error) { result.failures.push({ name: event.definition().name, error: errorDetails(error) }); }
|
|
145
191
|
}
|
|
146
192
|
}
|
|
147
193
|
|