@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ActiveRecord, RecordValidationError, withQueryMonitorCategory } from '../../db/index.js';
|
|
2
2
|
import { FailedJob } from '../FailedJob.js';
|
|
3
3
|
import { QueuedJob } from '../QueuedJob.js';
|
|
4
|
+
import { jobEnvelopeHasPrefix } from '../jobEnvelopeHasPrefix.js';
|
|
4
5
|
/**
|
|
5
6
|
* Queue driver that stores pending and failed jobs in the app database.
|
|
6
7
|
*/
|
|
@@ -15,6 +16,28 @@ export class DatabaseQueueDriver {
|
|
|
15
16
|
constructor(db) {
|
|
16
17
|
this.db = db;
|
|
17
18
|
}
|
|
19
|
+
/** Discovers queue names from the queue index, including delayed and reserved work. */
|
|
20
|
+
async queueNames() {
|
|
21
|
+
return withQueueQueryCategory(() => ActiveRecord.withDb(this.db.knex, async () => {
|
|
22
|
+
const rows = await QueuedJob.query().toKnex().distinct('queue').orderBy('queue');
|
|
23
|
+
return rows.map(row => String(row.queue));
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
/** Scans active work in bounded pages during quiesced maintenance, including delayed and chained payloads. */
|
|
27
|
+
async hasPendingJobs(jobPrefix) {
|
|
28
|
+
if (!(await this.db.knex.schema.hasTable(QueuedJob.table)))
|
|
29
|
+
return false;
|
|
30
|
+
let after = 0;
|
|
31
|
+
while (true) {
|
|
32
|
+
const rows = await this.db.knex(QueuedJob.table).select('id', 'payload').where('id', '>', after).orderBy('id').limit(100);
|
|
33
|
+
for (const row of rows)
|
|
34
|
+
if (jobEnvelopeHasPrefix(typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload, jobPrefix))
|
|
35
|
+
return true;
|
|
36
|
+
if (rows.length < 100)
|
|
37
|
+
return false;
|
|
38
|
+
after = rows[rows.length - 1].id;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
18
41
|
/**
|
|
19
42
|
* Persists a job in the `jobs` table.
|
|
20
43
|
*
|
|
@@ -22,7 +45,8 @@ export class DatabaseQueueDriver {
|
|
|
22
45
|
* @returns Database id of the queued job record.
|
|
23
46
|
*/
|
|
24
47
|
async push(job) {
|
|
25
|
-
|
|
48
|
+
const connection = ActiveRecord.getScopedDb() ?? this.db.knex;
|
|
49
|
+
return withQueueQueryCategory(() => ActiveRecord.withDb(connection, async () => {
|
|
26
50
|
const record = QueuedJob.create({
|
|
27
51
|
queue: job.queue,
|
|
28
52
|
payload: job.payload,
|
|
@@ -39,7 +63,7 @@ export class DatabaseQueueDriver {
|
|
|
39
63
|
isInsert: true,
|
|
40
64
|
onlyDirty: false,
|
|
41
65
|
});
|
|
42
|
-
const [jobId] = await
|
|
66
|
+
const [jobId] = await connection(QueuedJob.table).insert(row);
|
|
43
67
|
return Number(jobId);
|
|
44
68
|
}));
|
|
45
69
|
}
|
|
@@ -93,7 +117,7 @@ export class DatabaseQueueDriver {
|
|
|
93
117
|
* @returns True when the current attempt still owns the job.
|
|
94
118
|
*/
|
|
95
119
|
async touch(job, _retryAfterSeconds) {
|
|
96
|
-
return withQueueQueryCategory(() => ActiveRecord.withDb(this.db.knex, async () => {
|
|
120
|
+
return withQueueQueryCategory(() => ActiveRecord.withDb(ActiveRecord.getScopedDb() ?? this.db.knex, async () => {
|
|
97
121
|
const updated = await QueuedJob
|
|
98
122
|
.where('id', job.id)
|
|
99
123
|
.where('attempts', job.attempts)
|
|
@@ -29,6 +29,13 @@ export declare class RedisQueueDriver implements queue.QueueDriver {
|
|
|
29
29
|
* @returns Redis queue job id.
|
|
30
30
|
*/
|
|
31
31
|
push<TData extends Record<string, unknown>>(job: queue.QueueDriverPushInput<TData>): Promise<queue.QueueJobId>;
|
|
32
|
+
/**
|
|
33
|
+
* Discovers queue names from ready/delayed/reserved keys without reading payloads.
|
|
34
|
+
* SCAN includes existing queues written before this capability was introduced.
|
|
35
|
+
*/
|
|
36
|
+
queueNames(): Promise<string[]>;
|
|
37
|
+
/** Scans active Redis payloads during quiesced maintenance; includes delayed, reserved and chained jobs. */
|
|
38
|
+
hasPendingJobs(jobPrefix: string): Promise<boolean>;
|
|
32
39
|
/**
|
|
33
40
|
* Claims the next available job from a Redis queue.
|
|
34
41
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient } from '@redis/client';
|
|
2
2
|
import { unknownErrorMessage } from '@db3.ai/pure';
|
|
3
|
+
import { jobEnvelopeHasPrefix } from '../jobEnvelopeHasPrefix.js';
|
|
3
4
|
const PUSH_SCRIPT = `
|
|
4
5
|
local id = tostring(redis.call('INCR', KEYS[1]))
|
|
5
6
|
local record = cjson.decode(ARGV[1])
|
|
@@ -171,6 +172,42 @@ export class RedisQueueDriver {
|
|
|
171
172
|
]);
|
|
172
173
|
return String(id);
|
|
173
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Discovers queue names from ready/delayed/reserved keys without reading payloads.
|
|
177
|
+
* SCAN includes existing queues written before this capability was introduced.
|
|
178
|
+
*/
|
|
179
|
+
async queueNames() {
|
|
180
|
+
const prefix = `${this.#key('queues')}:`;
|
|
181
|
+
const pattern = prefix.replace(/[\\*?\[\]]/g, '\\$&') + '*';
|
|
182
|
+
const names = new Set();
|
|
183
|
+
let cursor = '0';
|
|
184
|
+
do {
|
|
185
|
+
const [next, keys] = await this.#command(['SCAN', cursor, 'MATCH', pattern, 'COUNT', 100]);
|
|
186
|
+
cursor = String(next);
|
|
187
|
+
for (const key of keys) {
|
|
188
|
+
if (!key.startsWith(prefix))
|
|
189
|
+
continue;
|
|
190
|
+
const suffix = key.slice(prefix.length).match(/^(.*):(ready|delayed|reserved)$/);
|
|
191
|
+
if (suffix?.[1])
|
|
192
|
+
names.add(suffix[1]);
|
|
193
|
+
}
|
|
194
|
+
} while (cursor !== '0');
|
|
195
|
+
return [...names].sort();
|
|
196
|
+
}
|
|
197
|
+
/** Scans active Redis payloads during quiesced maintenance; includes delayed, reserved and chained jobs. */
|
|
198
|
+
async hasPendingJobs(jobPrefix) {
|
|
199
|
+
let cursor = '0';
|
|
200
|
+
do {
|
|
201
|
+
const result = await this.#command(['HSCAN', this.#jobsKey(), cursor, 'COUNT', 100]);
|
|
202
|
+
cursor = String(result[0]);
|
|
203
|
+
for (let index = 1; index < result[1].length; index += 2) {
|
|
204
|
+
const record = JSON.parse(result[1][index]);
|
|
205
|
+
if (jobEnvelopeHasPrefix(record.payload, jobPrefix))
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
} while (cursor !== '0');
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
174
211
|
/**
|
|
175
212
|
* Claims the next available job from a Redis queue.
|
|
176
213
|
*
|
package/dist/queue/index.d.ts
CHANGED
|
@@ -11,3 +11,8 @@ export * from './drivers/DatabaseQueueDriver.js';
|
|
|
11
11
|
export * from './drivers/RedisQueueDriver.js';
|
|
12
12
|
export * from './contracts/index.js';
|
|
13
13
|
export * from './createJob.js';
|
|
14
|
+
export * from './QueueTerminalError.js';
|
|
15
|
+
export * from './RunnableJob.js';
|
|
16
|
+
export * from './JobRunAttempt.js';
|
|
17
|
+
export * from './JobRunEvent.js';
|
|
18
|
+
export * from './JobRunStoppedError.js';
|
package/dist/queue/index.js
CHANGED
|
@@ -11,3 +11,8 @@ export * from './drivers/DatabaseQueueDriver.js';
|
|
|
11
11
|
export * from './drivers/RedisQueueDriver.js';
|
|
12
12
|
export * from './contracts/index.js';
|
|
13
13
|
export * from './createJob.js';
|
|
14
|
+
export * from './QueueTerminalError.js';
|
|
15
|
+
export * from './RunnableJob.js';
|
|
16
|
+
export * from './JobRunAttempt.js';
|
|
17
|
+
export * from './JobRunEvent.js';
|
|
18
|
+
export * from './JobRunStoppedError.js';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SerializedQueuedJob } from './contracts/index.js';
|
|
2
|
+
/** Checks active work and nested chained continuations without inspecting application-owned payload data. */
|
|
3
|
+
export declare function jobEnvelopeHasPrefix(envelope: Pick<SerializedQueuedJob, 'job' | 'chained'>, prefix: string): boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Checks active work and nested chained continuations without inspecting application-owned payload data. */
|
|
2
|
+
export function jobEnvelopeHasPrefix(envelope, prefix) {
|
|
3
|
+
return envelope.job.startsWith(prefix) || Boolean(envelope.chained?.some(job => jobEnvelopeHasPrefix(job, prefix)));
|
|
4
|
+
}
|
|
@@ -5,7 +5,7 @@ import type * as scheduler from './contracts/index.js';
|
|
|
5
5
|
export declare abstract class ScheduledEvent {
|
|
6
6
|
abstract readonly kind: scheduler.ScheduledTaskKind;
|
|
7
7
|
protected configuredName: string | null;
|
|
8
|
-
protected configuredFrequency: scheduler.
|
|
8
|
+
protected configuredFrequency: scheduler.ScheduleFrequency | null;
|
|
9
9
|
protected configuredTimezone: string;
|
|
10
10
|
/**
|
|
11
11
|
* Sets the stable identity used for deduplication and history.
|
|
@@ -14,6 +14,10 @@ export declare abstract class ScheduledEvent {
|
|
|
14
14
|
* @returns This scheduled event.
|
|
15
15
|
*/
|
|
16
16
|
name(name: string): this;
|
|
17
|
+
/** Schedules one occurrence for each evaluated UTC minute, independent of timezone. */
|
|
18
|
+
everyMinute(): this;
|
|
19
|
+
/** Schedules one occurrence at minute zero of each UTC hour, independent of timezone. */
|
|
20
|
+
hourly(): this;
|
|
17
21
|
/**
|
|
18
22
|
* Schedules this task every day at local midnight.
|
|
19
23
|
*
|
|
@@ -38,7 +42,7 @@ export declare abstract class ScheduledEvent {
|
|
|
38
42
|
* Returns whether this event is due at one canonical UTC minute.
|
|
39
43
|
*
|
|
40
44
|
* @param instant - UTC minute being evaluated.
|
|
41
|
-
* @returns True when the configured
|
|
45
|
+
* @returns True when the configured frequency matches a valid minute.
|
|
42
46
|
*/
|
|
43
47
|
isDue(instant: Date): boolean;
|
|
44
48
|
/**
|
|
@@ -54,9 +58,9 @@ export declare abstract class ScheduledEvent {
|
|
|
54
58
|
*/
|
|
55
59
|
protected resolvedName(): string;
|
|
56
60
|
/**
|
|
57
|
-
* Returns the
|
|
61
|
+
* Returns the frequency or throws when none was configured.
|
|
58
62
|
*
|
|
59
|
-
* @returns Configured
|
|
63
|
+
* @returns Configured frequency.
|
|
60
64
|
*/
|
|
61
65
|
private frequency;
|
|
62
66
|
/**
|
|
@@ -23,6 +23,16 @@ export class ScheduledEvent {
|
|
|
23
23
|
this.configuredName = normalized;
|
|
24
24
|
return this;
|
|
25
25
|
}
|
|
26
|
+
/** Schedules one occurrence for each evaluated UTC minute, independent of timezone. */
|
|
27
|
+
everyMinute() {
|
|
28
|
+
this.configuredFrequency = { type: 'minute' };
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
/** Schedules one occurrence at minute zero of each UTC hour, independent of timezone. */
|
|
32
|
+
hourly() {
|
|
33
|
+
this.configuredFrequency = { type: 'hourly' };
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
26
36
|
/**
|
|
27
37
|
* Schedules this task every day at local midnight.
|
|
28
38
|
*
|
|
@@ -58,10 +68,14 @@ export class ScheduledEvent {
|
|
|
58
68
|
* Returns whether this event is due at one canonical UTC minute.
|
|
59
69
|
*
|
|
60
70
|
* @param instant - UTC minute being evaluated.
|
|
61
|
-
* @returns True when the configured
|
|
71
|
+
* @returns True when the configured frequency matches a valid minute.
|
|
62
72
|
*/
|
|
63
73
|
isDue(instant) {
|
|
64
74
|
const frequency = this.frequency();
|
|
75
|
+
if (frequency.type === 'minute')
|
|
76
|
+
return Number.isFinite(instant.getTime());
|
|
77
|
+
if (frequency.type === 'hourly')
|
|
78
|
+
return Number.isFinite(instant.getTime()) && instant.getUTCMinutes() === 0;
|
|
65
79
|
return dailyScheduleIsDue(instant, frequency.time, this.configuredTimezone);
|
|
66
80
|
}
|
|
67
81
|
/**
|
|
@@ -94,9 +108,9 @@ export class ScheduledEvent {
|
|
|
94
108
|
return name;
|
|
95
109
|
}
|
|
96
110
|
/**
|
|
97
|
-
* Returns the
|
|
111
|
+
* Returns the frequency or throws when none was configured.
|
|
98
112
|
*
|
|
99
|
-
* @returns Configured
|
|
113
|
+
* @returns Configured frequency.
|
|
100
114
|
*/
|
|
101
115
|
frequency() {
|
|
102
116
|
if (!this.configuredFrequency) {
|
|
@@ -31,6 +31,11 @@ export declare class Scheduler {
|
|
|
31
31
|
* @returns Fluent scheduled call definition.
|
|
32
32
|
*/
|
|
33
33
|
call(callback: scheduler.ScheduledCallHandler): ScheduledCall;
|
|
34
|
+
/**
|
|
35
|
+
* Atomically registers an owned schedule group. Declarations are synchronous and do not execute jobs.
|
|
36
|
+
* Names are prefixed with the owner unless already namespaced. Failed declarations register nothing.
|
|
37
|
+
*/
|
|
38
|
+
register(owner: string, configure: (schedule: scheduler.Schedule) => void, run?: scheduler.ScheduleRunner): scheduler.ScheduleRegistration;
|
|
34
39
|
/**
|
|
35
40
|
* Returns validated normalized definitions in registration order.
|
|
36
41
|
*
|
|
@@ -13,6 +13,8 @@ export class Scheduler {
|
|
|
13
13
|
database;
|
|
14
14
|
queue;
|
|
15
15
|
#events = [];
|
|
16
|
+
#owners = new Map();
|
|
17
|
+
#groups = new Set();
|
|
16
18
|
#unsubscribeQueueEvents;
|
|
17
19
|
/**
|
|
18
20
|
* Creates a scheduler and attaches durable queue lifecycle recording.
|
|
@@ -50,13 +52,57 @@ export class Scheduler {
|
|
|
50
52
|
this.#events.push(event);
|
|
51
53
|
return event;
|
|
52
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Atomically registers an owned schedule group. Declarations are synchronous and do not execute jobs.
|
|
57
|
+
* Names are prefixed with the owner unless already namespaced. Failed declarations register nothing.
|
|
58
|
+
*/
|
|
59
|
+
register(owner, configure, run = operation => operation()) {
|
|
60
|
+
if (!/^[a-z][a-z0-9_]{0,39}$/.test(owner) || this.#groups.has(owner))
|
|
61
|
+
throw new Error(`Invalid or duplicate schedule owner "${owner}".`);
|
|
62
|
+
const events = [];
|
|
63
|
+
const declaration = configure({
|
|
64
|
+
/** Collects a queued declaration without attaching it to the running scheduler yet. */
|
|
65
|
+
job: source => { const event = new ScheduledJob(source); events.push(event); return event; },
|
|
66
|
+
/** Collects a short callback under the same group ownership. */
|
|
67
|
+
call: handler => { const event = new ScheduledCall(handler); events.push(event); return event; },
|
|
68
|
+
});
|
|
69
|
+
if (declaration && typeof declaration.then === 'function')
|
|
70
|
+
throw new Error('Schedule declarations must be synchronous.');
|
|
71
|
+
const names = new Set(this.definitions().map(definition => definition.name));
|
|
72
|
+
const definitions = events.map(event => {
|
|
73
|
+
const name = event.definition().name;
|
|
74
|
+
if (!name.startsWith(`${owner}.`))
|
|
75
|
+
event.name(`${owner}.${name}`);
|
|
76
|
+
const definition = { ...event.definition(), owner };
|
|
77
|
+
if (names.has(definition.name))
|
|
78
|
+
throw new Error(`Duplicate scheduled event name "${definition.name}".`);
|
|
79
|
+
names.add(definition.name);
|
|
80
|
+
return definition;
|
|
81
|
+
});
|
|
82
|
+
this.#groups.add(owner);
|
|
83
|
+
for (const event of events) {
|
|
84
|
+
this.#events.push(event);
|
|
85
|
+
this.#owners.set(event, { owner, run });
|
|
86
|
+
}
|
|
87
|
+
let closed = false;
|
|
88
|
+
return { definitions, close: () => {
|
|
89
|
+
if (closed)
|
|
90
|
+
return;
|
|
91
|
+
closed = true;
|
|
92
|
+
for (const event of events) {
|
|
93
|
+
this.#events.splice(this.#events.indexOf(event), 1);
|
|
94
|
+
this.#owners.delete(event);
|
|
95
|
+
}
|
|
96
|
+
this.#groups.delete(owner);
|
|
97
|
+
} };
|
|
98
|
+
}
|
|
53
99
|
/**
|
|
54
100
|
* Returns validated normalized definitions in registration order.
|
|
55
101
|
*
|
|
56
102
|
* @returns Registered scheduler definitions.
|
|
57
103
|
*/
|
|
58
104
|
definitions() {
|
|
59
|
-
const definitions = this.#events.map(event => event.definition());
|
|
105
|
+
const definitions = this.#events.map(event => ({ ...event.definition(), ...(this.#owners.has(event) ? { owner: this.#owners.get(event).owner } : {}) }));
|
|
60
106
|
const names = new Set();
|
|
61
107
|
for (const definition of definitions) {
|
|
62
108
|
if (names.has(definition.name)) {
|
|
@@ -93,36 +139,54 @@ export class Scheduler {
|
|
|
93
139
|
failures: [],
|
|
94
140
|
};
|
|
95
141
|
for (const event of dueEvents) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
142
|
+
const owner = this.#owners.get(event);
|
|
143
|
+
/** Runs a complete occurrence inside its registration owner's lifecycle gate. */
|
|
144
|
+
const execute = async () => {
|
|
145
|
+
if (!this.#events.includes(event)) {
|
|
146
|
+
result.skipped++;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const definition = event.definition();
|
|
150
|
+
const occurrence = await ActiveRecord.withDb(this.database.knex, () => {
|
|
151
|
+
return ScheduledOccurrence.claim({
|
|
152
|
+
name: definition.name,
|
|
153
|
+
kind: definition.kind,
|
|
154
|
+
scheduledFor: evaluatedFor,
|
|
155
|
+
jobName: definition.jobName,
|
|
156
|
+
});
|
|
103
157
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
result.claimed += 1;
|
|
110
|
-
try {
|
|
111
|
-
if (event instanceof ScheduledJob) {
|
|
112
|
-
await this.dispatchJob(event, occurrence);
|
|
113
|
-
result.dispatched += 1;
|
|
158
|
+
if (!occurrence) {
|
|
159
|
+
result.skipped += 1;
|
|
160
|
+
return;
|
|
114
161
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
162
|
+
result.claimed += 1;
|
|
163
|
+
try {
|
|
164
|
+
if (event instanceof ScheduledJob) {
|
|
165
|
+
await this.dispatchJob(event, occurrence);
|
|
166
|
+
result.dispatched += 1;
|
|
167
|
+
}
|
|
168
|
+
else if (event instanceof ScheduledCall) {
|
|
169
|
+
await this.runCall(event, occurrence);
|
|
170
|
+
result.completed += 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
await this.failOccurrence(occurrence, error);
|
|
175
|
+
result.failures.push({
|
|
176
|
+
name: definition.name,
|
|
177
|
+
error: errorDetails(error),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
if (!owner)
|
|
182
|
+
await execute();
|
|
183
|
+
else {
|
|
184
|
+
try {
|
|
185
|
+
await owner.run(execute);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
result.failures.push({ name: event.definition().name, error: errorDetails(error) });
|
|
118
189
|
}
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
await this.failOccurrence(occurrence, error);
|
|
122
|
-
result.failures.push({
|
|
123
|
-
name: definition.name,
|
|
124
|
-
error: errorDetails(error),
|
|
125
|
-
});
|
|
126
190
|
}
|
|
127
191
|
}
|
|
128
192
|
result.finishedAt = new Date();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActiveRecord } from '../db/index.js';
|
|
2
|
+
import type { SchedulerCheckpoint } from './contracts/index.js';
|
|
3
|
+
declare const SchedulerCheckpointRecord_base: import("../db/index.js").DefinedActiveRecord<typeof ActiveRecord, {
|
|
4
|
+
id: import("../db/index.js").UlidField;
|
|
5
|
+
name: import("../db/index.js").StringField;
|
|
6
|
+
value: import("../db/index.js").JsonField<{
|
|
7
|
+
evaluatedFor: string;
|
|
8
|
+
} | null>;
|
|
9
|
+
updatedAt: import("../db/index.js").TimestampField;
|
|
10
|
+
}>;
|
|
11
|
+
/** Durable scheduler restart progress; register this model in the application's migration registry. */
|
|
12
|
+
export declare class SchedulerCheckpointRecord extends SchedulerCheckpointRecord_base {
|
|
13
|
+
/** Returns restart persistence for one stable scheduler name; concurrent saves never regress progress. */
|
|
14
|
+
static checkpoint(name?: string): SchedulerCheckpoint;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ActiveRecord } from '../db/index.js';
|
|
2
|
+
/** Durable scheduler restart progress; register this model in the application's migration registry. */
|
|
3
|
+
export class SchedulerCheckpointRecord extends ActiveRecord.define({
|
|
4
|
+
table: 'scheduler_checkpoints',
|
|
5
|
+
fields: field => ({
|
|
6
|
+
id: field.ulid(),
|
|
7
|
+
name: field.string({ required: true, length: 120, unique: true }),
|
|
8
|
+
value: field.json({ required: true }),
|
|
9
|
+
updatedAt: field.timestamp({ column: 'updated_at', auto: 'update' }),
|
|
10
|
+
}),
|
|
11
|
+
}) {
|
|
12
|
+
/** Returns restart persistence for one stable scheduler name; concurrent saves never regress progress. */
|
|
13
|
+
static checkpoint(name = 'default') {
|
|
14
|
+
return {
|
|
15
|
+
/** Establishes coverage before the first dispatch without resetting an existing scheduler. */
|
|
16
|
+
async load(firstMinute) {
|
|
17
|
+
let record = await SchedulerCheckpointRecord.where('name', name).first();
|
|
18
|
+
if (!record) {
|
|
19
|
+
try {
|
|
20
|
+
record = await SchedulerCheckpointRecord.create({ name, value: { evaluatedFor: new Date(firstMinute.getTime() - 60_000).toISOString() } }).save();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
record = await SchedulerCheckpointRecord.where('name', name).first();
|
|
24
|
+
if (!record)
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return checkpointMinute(record.value.evaluatedFor);
|
|
29
|
+
},
|
|
30
|
+
/** Advances only a valid, initialized UTC-minute cursor under a database row lock. */
|
|
31
|
+
async save(evaluatedFor) {
|
|
32
|
+
const minute = checkpointMinute(evaluatedFor.toISOString());
|
|
33
|
+
await SchedulerCheckpointRecord.getDb().transaction(transaction => ActiveRecord.withDb(transaction, async () => {
|
|
34
|
+
const row = await SchedulerCheckpointRecord.where('name', name).toKnex().forUpdate().first();
|
|
35
|
+
if (!row)
|
|
36
|
+
throw new Error('Scheduler checkpoint must be initialized before advancing it.');
|
|
37
|
+
const record = SchedulerCheckpointRecord.fromDb(row);
|
|
38
|
+
if (checkpointMinute(record.value.evaluatedFor).getTime() >= minute.getTime())
|
|
39
|
+
return;
|
|
40
|
+
record.value = { evaluatedFor: minute.toISOString() };
|
|
41
|
+
await record.save();
|
|
42
|
+
}));
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Rejects corrupt progress rather than silently skipping an unknown scheduling window. */
|
|
48
|
+
function checkpointMinute(value) {
|
|
49
|
+
const minute = new Date(typeof value === 'string' ? value : NaN);
|
|
50
|
+
if (!Number.isFinite(minute.getTime()) || minute.getTime() % 60_000 !== 0)
|
|
51
|
+
throw new Error('Scheduler checkpoint requires a valid UTC minute.');
|
|
52
|
+
return minute;
|
|
53
|
+
}
|
|
@@ -14,7 +14,9 @@ export declare class SchedulerWorker {
|
|
|
14
14
|
*/
|
|
15
15
|
constructor(schedulerService: Scheduler, options?: scheduler.SchedulerWorkerOptions);
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Evaluates every uncovered minute in order, including time spent dispatching
|
|
18
|
+
* earlier batches. Catch-up yields after sixty minutes without discarding work.
|
|
19
|
+
* A durable checkpoint resumes coverage across replacement processes.
|
|
18
20
|
*
|
|
19
21
|
* @throws Error when the same worker instance is started twice.
|
|
20
22
|
*/
|
|
@@ -23,10 +25,6 @@ export declare class SchedulerWorker {
|
|
|
23
25
|
* Requests graceful shutdown and interrupts the current minute wait.
|
|
24
26
|
*/
|
|
25
27
|
stop(): void;
|
|
26
|
-
/**
|
|
27
|
-
* Evaluates one scheduler minute and reports failures without ending work.
|
|
28
|
-
*/
|
|
29
|
-
private runTick;
|
|
30
28
|
}
|
|
31
29
|
/**
|
|
32
30
|
* Calculates the delay from one instant to the next UTC minute boundary.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { utcMinute } from './scheduleTime.js';
|
|
1
2
|
const DEFAULT_SLOW_TICK_WARNING_MS = 50_000;
|
|
3
|
+
const MINUTE_MS = 60_000;
|
|
4
|
+
const MAX_MINUTES_PER_BATCH = 60;
|
|
5
|
+
const FAILED_TICK_RETRY_MS = 1_000;
|
|
2
6
|
/**
|
|
3
7
|
* Long-running minute-aligned process adapter around Scheduler.runDue().
|
|
4
8
|
*/
|
|
@@ -7,6 +11,9 @@ export class SchedulerWorker {
|
|
|
7
11
|
#running = false;
|
|
8
12
|
#abortController = null;
|
|
9
13
|
#now;
|
|
14
|
+
#nextMinute = null;
|
|
15
|
+
#onTick;
|
|
16
|
+
#checkpoint;
|
|
10
17
|
#sleep;
|
|
11
18
|
#logger;
|
|
12
19
|
#slowTickWarningMs;
|
|
@@ -18,6 +25,8 @@ export class SchedulerWorker {
|
|
|
18
25
|
*/
|
|
19
26
|
constructor(schedulerService, options = {}) {
|
|
20
27
|
this.schedulerService = schedulerService;
|
|
28
|
+
this.#onTick = options.onTick;
|
|
29
|
+
this.#checkpoint = options.checkpoint;
|
|
21
30
|
this.#now = options.now ?? (() => new Date());
|
|
22
31
|
this.#sleep = options.sleep ?? schedulerSleep;
|
|
23
32
|
this.#logger = options.logger ?? consoleSchedulerLogger();
|
|
@@ -25,7 +34,9 @@ export class SchedulerWorker {
|
|
|
25
34
|
?? DEFAULT_SLOW_TICK_WARNING_MS;
|
|
26
35
|
}
|
|
27
36
|
/**
|
|
28
|
-
*
|
|
37
|
+
* Evaluates every uncovered minute in order, including time spent dispatching
|
|
38
|
+
* earlier batches. Catch-up yields after sixty minutes without discarding work.
|
|
39
|
+
* A durable checkpoint resumes coverage across replacement processes.
|
|
29
40
|
*
|
|
30
41
|
* @throws Error when the same worker instance is started twice.
|
|
31
42
|
*/
|
|
@@ -36,11 +47,30 @@ export class SchedulerWorker {
|
|
|
36
47
|
this.#running = true;
|
|
37
48
|
this.#abortController = new AbortController();
|
|
38
49
|
try {
|
|
50
|
+
if (this.#nextMinute === null) {
|
|
51
|
+
const firstMinute = utcMinute(this.#now());
|
|
52
|
+
const previous = await this.#checkpoint?.load(firstMinute);
|
|
53
|
+
this.#nextMinute = previous ? utcMinute(previous).getTime() + MINUTE_MS : firstMinute.getTime();
|
|
54
|
+
}
|
|
55
|
+
let batchSize = 0;
|
|
39
56
|
while (this.#running) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
const now = this.#now();
|
|
58
|
+
const currentMinute = utcMinute(now).getTime();
|
|
59
|
+
if (this.#nextMinute <= currentMinute) {
|
|
60
|
+
const completed = await this.#runTick(new Date(this.#nextMinute));
|
|
61
|
+
if (completed)
|
|
62
|
+
this.#nextMinute += MINUTE_MS;
|
|
63
|
+
if (!this.#running)
|
|
64
|
+
break;
|
|
65
|
+
if (!completed || ++batchSize >= MAX_MINUTES_PER_BATCH) {
|
|
66
|
+
await this.#sleep(completed ? 1 : FAILED_TICK_RETRY_MS, this.#abortController.signal);
|
|
67
|
+
batchSize = 0;
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
batchSize = 0;
|
|
72
|
+
// An early timer or backward clock step must not skip or repeat a minute.
|
|
73
|
+
await this.#sleep(Math.min(MINUTE_MS, Math.max(1, this.#nextMinute - now.getTime())), this.#abortController.signal);
|
|
44
74
|
}
|
|
45
75
|
}
|
|
46
76
|
finally {
|
|
@@ -56,12 +86,22 @@ export class SchedulerWorker {
|
|
|
56
86
|
this.#abortController?.abort();
|
|
57
87
|
}
|
|
58
88
|
/**
|
|
59
|
-
* Evaluates one
|
|
89
|
+
* Evaluates one fixed minute, preserving progress only after the whole batch.
|
|
90
|
+
* Recorded event failures remain visible and do not prevent later minutes;
|
|
91
|
+
* incomplete evaluations and checkpoint failures retry this same minute.
|
|
92
|
+
*
|
|
93
|
+
* @param evaluatedFor - Original scheduled minute, independent of dispatch duration.
|
|
94
|
+
* @returns Whether the complete minute was evaluated and its checkpoint saved.
|
|
60
95
|
*/
|
|
61
|
-
async runTick() {
|
|
96
|
+
async #runTick(evaluatedFor) {
|
|
62
97
|
const startedAt = performance.now();
|
|
98
|
+
let completed = false;
|
|
63
99
|
try {
|
|
64
|
-
const result = await this.schedulerService.runDue(
|
|
100
|
+
const result = await this.schedulerService.runDue(evaluatedFor);
|
|
101
|
+
if (result.failures.length === 0)
|
|
102
|
+
await this.#onTick?.(result);
|
|
103
|
+
await this.#checkpoint?.save(evaluatedFor);
|
|
104
|
+
completed = true;
|
|
65
105
|
if (result.due > 0 || result.failures.length > 0) {
|
|
66
106
|
this.#logger.info(`[scheduler] ${result.evaluatedFor.toISOString()} due=${result.due} claimed=${result.claimed} dispatched=${result.dispatched} completed=${result.completed} skipped=${result.skipped} failed=${result.failures.length}.`);
|
|
67
107
|
}
|
|
@@ -70,12 +110,13 @@ export class SchedulerWorker {
|
|
|
70
110
|
}
|
|
71
111
|
}
|
|
72
112
|
catch (error) {
|
|
73
|
-
this.#logger.error(
|
|
113
|
+
this.#logger.error(`[scheduler] Evaluation for ${evaluatedFor.toISOString()} did not finish; retrying the same minute.`, error);
|
|
74
114
|
}
|
|
75
115
|
const durationMs = performance.now() - startedAt;
|
|
76
116
|
if (durationMs >= this.#slowTickWarningMs) {
|
|
77
117
|
this.#logger.warn(`[scheduler] Tick took ${Math.round(durationMs)}ms; inline calls should remain short.`);
|
|
78
118
|
}
|
|
119
|
+
return completed;
|
|
79
120
|
}
|
|
80
121
|
}
|
|
81
122
|
/**
|
|
@@ -96,13 +137,16 @@ export function millisecondsUntilNextMinute(now) {
|
|
|
96
137
|
*/
|
|
97
138
|
async function schedulerSleep(delayMs, signal) {
|
|
98
139
|
await new Promise(resolve => {
|
|
99
|
-
|
|
100
|
-
|
|
140
|
+
/** Clears the timer and its abort subscription on either completion path. */
|
|
141
|
+
const finish = () => {
|
|
101
142
|
clearTimeout(timeout);
|
|
143
|
+
signal.removeEventListener('abort', finish);
|
|
102
144
|
resolve();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
});
|
|
145
|
+
};
|
|
146
|
+
const timeout = setTimeout(finish, delayMs);
|
|
147
|
+
signal.addEventListener('abort', finish, { once: true });
|
|
148
|
+
if (signal.aborted)
|
|
149
|
+
finish();
|
|
106
150
|
});
|
|
107
151
|
}
|
|
108
152
|
/**
|