@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
|
@@ -3,6 +3,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import { hostname } from 'node:os';
|
|
4
4
|
import pino from 'pino';
|
|
5
5
|
import { isInteractiveDevelopmentEnvironment } from '../../devtools/index.js';
|
|
6
|
+
import { EmailLogTransport } from '../transports/EmailLogTransport.js';
|
|
7
|
+
import { acquireDevelopmentConsole } from '../acquireDevelopmentConsole.js';
|
|
6
8
|
const defaultRedactions = [
|
|
7
9
|
'password',
|
|
8
10
|
'*.password',
|
|
@@ -24,6 +26,8 @@ const defaultRedactions = [
|
|
|
24
26
|
export class PinoLoggerDriver {
|
|
25
27
|
logger;
|
|
26
28
|
#transport;
|
|
29
|
+
#emails = [];
|
|
30
|
+
#console;
|
|
27
31
|
#closed = false;
|
|
28
32
|
#closing = false;
|
|
29
33
|
#closePromise = null;
|
|
@@ -32,10 +36,24 @@ export class PinoLoggerDriver {
|
|
|
32
36
|
*
|
|
33
37
|
* @param options - Framework logging configuration.
|
|
34
38
|
* @param destination - Optional direct destination used by focused tests.
|
|
39
|
+
* @param mail - Application-owned mail service used by the email destination.
|
|
35
40
|
*/
|
|
36
|
-
constructor(options = {}, destination) {
|
|
41
|
+
constructor(options = {}, destination, mail) {
|
|
37
42
|
const environment = options.environment ?? process.env.NODE_ENV ?? 'development';
|
|
38
43
|
const level = resolveLogLevel(options, process.env);
|
|
44
|
+
if ((options.transports?.filter(transport => transport.type === 'console').length ?? 0) > 1)
|
|
45
|
+
throw new Error('Configure at most one console log transport.');
|
|
46
|
+
for (const transport of options.transports ?? []) {
|
|
47
|
+
if (!['console', 'file', 'email', 'devtools'].includes(transport.type))
|
|
48
|
+
throw new Error('Unknown logging transport type.');
|
|
49
|
+
if (transport.level !== undefined && !isLogLevel(transport.level))
|
|
50
|
+
throw new Error('Invalid logging transport level.');
|
|
51
|
+
if (transport.type === 'email' && transport.level !== 'silent') {
|
|
52
|
+
if (!mail)
|
|
53
|
+
throw new Error('Email logging requires an application Mail resolver.');
|
|
54
|
+
this.#emails.push({ level: transport.level ?? 'error', stream: new EmailLogTransport(transport, mail) });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
39
57
|
const source = options.source
|
|
40
58
|
?? process.env.PLATFORM_LOG_SOURCE
|
|
41
59
|
?? process.env.npm_package_name
|
|
@@ -58,28 +76,51 @@ export class PinoLoggerDriver {
|
|
|
58
76
|
};
|
|
59
77
|
if (destination) {
|
|
60
78
|
this.#transport = null;
|
|
61
|
-
this.logger = pino(pinoOptions, destination);
|
|
79
|
+
this.logger = pino(pinoOptions, this.#destinations(destination));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const format = options.consoleFormat ?? process.env.PLATFORM_LOG_FORMAT ?? 'auto';
|
|
83
|
+
if (!['auto', 'pretty', 'json'].includes(format))
|
|
84
|
+
throw new Error(`Invalid PLATFORM_LOG_FORMAT value "${format}". Use auto, pretty, or json.`);
|
|
85
|
+
const consoleTransport = options.transports?.find(transport => transport.type === 'console');
|
|
86
|
+
const consoleEnabled = options.transports ? Boolean(consoleTransport) : options.console !== false;
|
|
87
|
+
const pretty = consoleEnabled && consoleTransport?.level !== 'silent' && level !== 'silent' && (format === 'pretty' || (format === 'auto' && isInteractiveDevelopmentEnvironment(environment) && Boolean(process.stdout.isTTY)));
|
|
88
|
+
const targets = transportTargets(pretty ? { ...options, console: false, transports: options.transports?.filter(transport => transport.type !== 'console') } : options, level, environment, process.env);
|
|
89
|
+
if (pretty) {
|
|
90
|
+
this.#transport = targets.length ? pino.transport({ targets }) : null;
|
|
91
|
+
this.#transport?.on('error', reportTransportError);
|
|
92
|
+
this.#console = acquireDevelopmentConsole();
|
|
93
|
+
const streams = [{ level: (consoleTransport?.level === 'silent' ? 'trace' : consoleTransport?.level) ?? level, stream: this.#console.console }];
|
|
94
|
+
if (this.#transport)
|
|
95
|
+
streams.push({ level: 'trace', stream: this.#transport });
|
|
96
|
+
this.logger = pino(pinoOptions, this.#destinations(pino.multistream(streams)));
|
|
62
97
|
return;
|
|
63
98
|
}
|
|
64
|
-
const targets = transportTargets(options, level, environment, process.env);
|
|
65
99
|
if (targets.length === 0) {
|
|
66
100
|
this.#transport = null;
|
|
67
101
|
this.logger = pino({
|
|
68
102
|
...pinoOptions,
|
|
69
|
-
level: 'silent',
|
|
70
|
-
});
|
|
103
|
+
level: this.#emails.length ? level : 'silent',
|
|
104
|
+
}, this.#emails.length ? this.#destinations({ /** Discards the disabled console destination. */ write() { } }) : undefined);
|
|
71
105
|
return;
|
|
72
106
|
}
|
|
73
|
-
if (targets.length === 1 && targets[0]?.target === 'pino/file') {
|
|
107
|
+
if (targets.length === 1 && targets[0]?.target === 'pino/file' && targets[0]?.options?.destination === 1 && targets[0]?.level === level) {
|
|
74
108
|
this.#transport = null;
|
|
75
|
-
this.logger = pino(pinoOptions);
|
|
109
|
+
this.logger = pino(pinoOptions, this.#destinations(process.stdout));
|
|
76
110
|
return;
|
|
77
111
|
}
|
|
78
112
|
this.#transport = pino.transport({
|
|
79
113
|
targets,
|
|
80
114
|
});
|
|
81
115
|
this.#transport.on('error', reportTransportError);
|
|
82
|
-
this.logger = pino(pinoOptions, this.#transport);
|
|
116
|
+
this.logger = pino(pinoOptions, this.#destinations(this.#transport));
|
|
117
|
+
}
|
|
118
|
+
/** Fans already serialized and redacted records out to ordinary destinations and operator email. */
|
|
119
|
+
#destinations(destination) {
|
|
120
|
+
return this.#emails.length ? pino.multistream([
|
|
121
|
+
{ level: 'trace', stream: destination },
|
|
122
|
+
...this.#emails,
|
|
123
|
+
]) : destination;
|
|
83
124
|
}
|
|
84
125
|
/**
|
|
85
126
|
* Waits until Pino has handed accepted records to its destination.
|
|
@@ -94,7 +135,8 @@ export class PinoLoggerDriver {
|
|
|
94
135
|
this.#transport.ref();
|
|
95
136
|
}
|
|
96
137
|
await new Promise((resolve, reject) => {
|
|
97
|
-
this.logger
|
|
138
|
+
const destination = this.#transport ?? this.logger;
|
|
139
|
+
destination.flush((error) => {
|
|
98
140
|
if (error) {
|
|
99
141
|
reject(error);
|
|
100
142
|
return;
|
|
@@ -102,6 +144,8 @@ export class PinoLoggerDriver {
|
|
|
102
144
|
resolve();
|
|
103
145
|
});
|
|
104
146
|
});
|
|
147
|
+
await this.#console?.console.flush();
|
|
148
|
+
await Promise.all(this.#emails.map(email => email.stream.flush()));
|
|
105
149
|
}
|
|
106
150
|
finally {
|
|
107
151
|
if (!this.#closing) {
|
|
@@ -123,6 +167,7 @@ export class PinoLoggerDriver {
|
|
|
123
167
|
if (this.#closed)
|
|
124
168
|
return;
|
|
125
169
|
this.#closing = true;
|
|
170
|
+
this.#console?.release();
|
|
126
171
|
await this.flush();
|
|
127
172
|
this.#closed = true;
|
|
128
173
|
if (!this.#transport)
|
|
@@ -162,15 +207,29 @@ function transportTargets(options, level, environment, env) {
|
|
|
162
207
|
if (level === 'silent')
|
|
163
208
|
return [];
|
|
164
209
|
const targets = [];
|
|
210
|
+
if (options.transports) {
|
|
211
|
+
for (const transport of options.transports) {
|
|
212
|
+
const minimum = transport.level ?? level;
|
|
213
|
+
if (minimum === 'silent')
|
|
214
|
+
continue;
|
|
215
|
+
if (transport.type === 'console')
|
|
216
|
+
targets.push({ target: 'pino/file', level: minimum, options: { destination: 1 } });
|
|
217
|
+
if (transport.type === 'file') {
|
|
218
|
+
if (!transport.destination)
|
|
219
|
+
throw new Error('File logging requires a destination.');
|
|
220
|
+
targets.push({ target: 'pino/file', level: minimum, options: { destination: transport.destination, mkdir: true } });
|
|
221
|
+
}
|
|
222
|
+
if (transport.type === 'devtools')
|
|
223
|
+
targets.push({ target: devtoolsTransportTarget(), level: minimum, options: transport.options ?? {} });
|
|
224
|
+
}
|
|
225
|
+
return targets;
|
|
226
|
+
}
|
|
165
227
|
if (options.console !== false) {
|
|
166
|
-
targets.push({
|
|
167
|
-
target: 'pino/file',
|
|
168
|
-
level,
|
|
169
|
-
options: {
|
|
170
|
-
destination: 1,
|
|
171
|
-
},
|
|
172
|
-
});
|
|
228
|
+
targets.push({ target: 'pino/file', level, options: { destination: 1 } });
|
|
173
229
|
}
|
|
230
|
+
const file = options.file ?? env.PLATFORM_LOG_FILE;
|
|
231
|
+
if (file)
|
|
232
|
+
targets.push({ target: 'pino/file', level, options: { destination: file, mkdir: true } });
|
|
174
233
|
if (devtoolsEnabled(options.devtools, environment, env)) {
|
|
175
234
|
targets.push({
|
|
176
235
|
target: devtoolsTransportTarget(),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MailMessage } from '../mail/index.js';
|
|
2
|
+
import type { EmailLogOptions } from './contracts/index.js';
|
|
3
|
+
/** Formats a redacted log record without copying arbitrary nested payloads or exception properties into email. */
|
|
4
|
+
export declare function formatLogEmail(record: Record<string, unknown>, options: EmailLogOptions): MailMessage;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { redactLogEmail } from './redactLogEmail.js';
|
|
2
|
+
/** Formats a redacted log record without copying arbitrary nested payloads or exception properties into email. */
|
|
3
|
+
export function formatLogEmail(record, options) {
|
|
4
|
+
const context = Object.fromEntries(Object.entries(record).filter(([key, value]) => key !== 'err' && (value === null || ['string', 'number', 'boolean'].includes(typeof value))));
|
|
5
|
+
const sections = [`Log context (JSON):\n${JSON.stringify(context, null, 2)}`];
|
|
6
|
+
let error = record.err;
|
|
7
|
+
if (typeof error === 'string')
|
|
8
|
+
sections.push(`Exception: ${error}`);
|
|
9
|
+
for (let depth = 0; error && typeof error === 'object' && depth < 4; depth++) {
|
|
10
|
+
const value = error;
|
|
11
|
+
sections.push(`${depth ? 'Caused by' : 'Exception'}: ${String(value.type ?? value.name ?? 'Error')}\nMessage: ${String(value.message ?? 'Unavailable')}\nStack trace:\n${String(value.stack ?? 'Unavailable')}`);
|
|
12
|
+
error = value.cause;
|
|
13
|
+
}
|
|
14
|
+
const text = redactLogEmail(sections.join('\n\n'));
|
|
15
|
+
const subject = redactLogEmail(`${options.subjectPrefix ?? 'Application error'}: ${String(record.msg ?? 'Exception logged')}`).replace(/[\r\n]/g, ' ').slice(0, 200);
|
|
16
|
+
return { to: options.to, subject, text, html: `<pre style="white-space:pre-wrap">${escapeHtml(text)}</pre>` };
|
|
17
|
+
}
|
|
18
|
+
/** Escapes log text as inert HTML content in the email's preformatted body. */
|
|
19
|
+
function escapeHtml(text) {
|
|
20
|
+
return text.replace(/[&<>"']/g, character => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[character]);
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colours the parts of a queue log message that are scanned most: the state
|
|
3
|
+
* verb after `[queue] ` (blue in progress, green done, yellow retrying, red
|
|
4
|
+
* failed) and job labels (bold magenta). Other messages are returned unchanged
|
|
5
|
+
* apart from job labels.
|
|
6
|
+
*
|
|
7
|
+
* @param message - Plain log message.
|
|
8
|
+
* @param paint - Wraps text in an SGR code, such as `'32'` or `'1;35'`.
|
|
9
|
+
* @returns The message with colour codes.
|
|
10
|
+
*/
|
|
11
|
+
export declare function highlightQueueMessage(message: string, paint: (text: string, code: string) => string): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Terminal colour for each queue state verb at the start of a `[queue]` message. */
|
|
2
|
+
const STATES = [
|
|
3
|
+
[/^(Claimed|Running|Dispatched)\b/, '34'],
|
|
4
|
+
[/^(Processed|Retried)\b/, '32'],
|
|
5
|
+
[/^(Released|Deferred)\b/, '33'],
|
|
6
|
+
[/^(Failed|Lost lease|Stopped fenced)\b/, '31'],
|
|
7
|
+
];
|
|
8
|
+
/** A job label such as `CollectKeywordRanksJob#5249`. */
|
|
9
|
+
const JOB_LABEL = /\b[A-Z][A-Za-z0-9_]*#\d+\b/g;
|
|
10
|
+
/**
|
|
11
|
+
* Colours the parts of a queue log message that are scanned most: the state
|
|
12
|
+
* verb after `[queue] ` (blue in progress, green done, yellow retrying, red
|
|
13
|
+
* failed) and job labels (bold magenta). Other messages are returned unchanged
|
|
14
|
+
* apart from job labels.
|
|
15
|
+
*
|
|
16
|
+
* @param message - Plain log message.
|
|
17
|
+
* @param paint - Wraps text in an SGR code, such as `'32'` or `'1;35'`.
|
|
18
|
+
* @returns The message with colour codes.
|
|
19
|
+
*/
|
|
20
|
+
export function highlightQueueMessage(message, paint) {
|
|
21
|
+
let prefix = '';
|
|
22
|
+
let rest = message;
|
|
23
|
+
if (rest.startsWith('[queue] ')) {
|
|
24
|
+
prefix = '[queue] ';
|
|
25
|
+
rest = rest.slice(prefix.length);
|
|
26
|
+
for (const [pattern, code] of STATES) {
|
|
27
|
+
const match = pattern.exec(rest);
|
|
28
|
+
if (match) {
|
|
29
|
+
prefix += paint(match[0], code);
|
|
30
|
+
rest = rest.slice(match[0].length);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return prefix + rest.replace(JOB_LABEL, label => paint(label, '1;35'));
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HttpBodyLog } from './contracts/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Captures one request or response body without retaining unbounded payloads.
|
|
4
|
+
*
|
|
5
|
+
* @param payload - Parsed request body or outgoing Fastify payload.
|
|
6
|
+
* @param contentType - Media type reported by the HTTP message.
|
|
7
|
+
* @param maxBodyBytes - Maximum UTF-8 bytes retained in the log record.
|
|
8
|
+
* @returns Bounded body metadata and an inspectable value when safe.
|
|
9
|
+
*/
|
|
10
|
+
export declare function captureBody(payload: unknown, contentType: string | undefined, maxBodyBytes: number): HttpBodyLog;
|
|
11
|
+
/**
|
|
12
|
+
* Redacts credential-bearing headers before a record reaches Pino.
|
|
13
|
+
*
|
|
14
|
+
* @param headers - Fastify request or response header collection.
|
|
15
|
+
* @returns Header object safe for local request inspection.
|
|
16
|
+
*/
|
|
17
|
+
export declare function redactHeaders(headers: Readonly<Record<string, unknown>>): Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Converts a Fastify header into one displayable content-type string.
|
|
20
|
+
*
|
|
21
|
+
* @param value - Request or response content-type header.
|
|
22
|
+
* @returns First string value when present.
|
|
23
|
+
*/
|
|
24
|
+
export declare function headerValue(value: unknown): string | undefined;
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
const maximumCapturedProperties = 2_000;
|
|
2
|
+
const maximumCaptureDepth = 12;
|
|
3
|
+
const maximumHeaderValueLength = 4_096;
|
|
4
|
+
const redactedValue = '[Redacted]';
|
|
5
|
+
/**
|
|
6
|
+
* Captures one request or response body without retaining unbounded payloads.
|
|
7
|
+
*
|
|
8
|
+
* @param payload - Parsed request body or outgoing Fastify payload.
|
|
9
|
+
* @param contentType - Media type reported by the HTTP message.
|
|
10
|
+
* @param maxBodyBytes - Maximum UTF-8 bytes retained in the log record.
|
|
11
|
+
* @returns Bounded body metadata and an inspectable value when safe.
|
|
12
|
+
*/
|
|
13
|
+
export function captureBody(payload, contentType, maxBodyBytes) {
|
|
14
|
+
if (payload === undefined || payload === null || payload === '') {
|
|
15
|
+
return {
|
|
16
|
+
kind: 'empty',
|
|
17
|
+
contentType,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (isStreamLike(payload)) {
|
|
21
|
+
return {
|
|
22
|
+
kind: 'stream',
|
|
23
|
+
contentType,
|
|
24
|
+
note: 'Streaming response body omitted.',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (Buffer.isBuffer(payload) || payload instanceof Uint8Array) {
|
|
28
|
+
const buffer = Buffer.from(payload);
|
|
29
|
+
if (!isTextContentType(contentType)) {
|
|
30
|
+
return {
|
|
31
|
+
kind: 'binary',
|
|
32
|
+
contentType,
|
|
33
|
+
sizeBytes: buffer.byteLength,
|
|
34
|
+
note: 'Binary body omitted.',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return captureText(buffer.toString('utf8'), contentType, maxBodyBytes);
|
|
38
|
+
}
|
|
39
|
+
if (typeof payload === 'string') {
|
|
40
|
+
return captureText(payload, contentType, maxBodyBytes);
|
|
41
|
+
}
|
|
42
|
+
return captureJson(payload, contentType, maxBodyBytes);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Captures textual content, parsing JSON so nested secrets can be redacted.
|
|
46
|
+
*
|
|
47
|
+
* @param value - Original textual payload.
|
|
48
|
+
* @param contentType - Media type reported by the HTTP message.
|
|
49
|
+
* @param maxBodyBytes - Maximum UTF-8 bytes retained in the log record.
|
|
50
|
+
* @returns Text or JSON body capture.
|
|
51
|
+
*/
|
|
52
|
+
function captureText(value, contentType, maxBodyBytes) {
|
|
53
|
+
if (isFormContentType(contentType)) {
|
|
54
|
+
return captureJson(formBodyFromText(value), contentType, maxBodyBytes);
|
|
55
|
+
}
|
|
56
|
+
if (isJsonContentType(contentType) || looksLikeJson(value)) {
|
|
57
|
+
try {
|
|
58
|
+
return captureJson(JSON.parse(value), contentType, maxBodyBytes);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Malformed JSON remains useful as bounded response text.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const sizeBytes = Buffer.byteLength(value);
|
|
65
|
+
return {
|
|
66
|
+
kind: 'text',
|
|
67
|
+
contentType,
|
|
68
|
+
sizeBytes,
|
|
69
|
+
truncated: sizeBytes > maxBodyBytes,
|
|
70
|
+
value: truncateUtf8(value, maxBodyBytes),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Converts URL-encoded form text into a shape that supports field redaction.
|
|
75
|
+
*
|
|
76
|
+
* Repeated form keys remain arrays so the inspector does not silently discard
|
|
77
|
+
* submitted values.
|
|
78
|
+
*
|
|
79
|
+
* @param value - URL-encoded request body.
|
|
80
|
+
* @returns Parsed form object suitable for normal JSON sanitization.
|
|
81
|
+
*/
|
|
82
|
+
function formBodyFromText(value) {
|
|
83
|
+
const captured = {};
|
|
84
|
+
for (const [key, item] of new URLSearchParams(value)) {
|
|
85
|
+
const existing = captured[key];
|
|
86
|
+
if (existing === undefined) {
|
|
87
|
+
captured[key] = item;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
captured[key] = Array.isArray(existing)
|
|
91
|
+
? [...existing, item]
|
|
92
|
+
: [existing, item];
|
|
93
|
+
}
|
|
94
|
+
return captured;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Produces a JSON-safe, recursively redacted representation of a payload.
|
|
98
|
+
*
|
|
99
|
+
* @param value - Parsed body value.
|
|
100
|
+
* @param contentType - Media type reported by the HTTP message.
|
|
101
|
+
* @param maxBodyBytes - Maximum UTF-8 bytes retained in the log record.
|
|
102
|
+
* @returns JSON body capture.
|
|
103
|
+
*/
|
|
104
|
+
function captureJson(value, contentType, maxBodyBytes) {
|
|
105
|
+
const sanitized = sanitizeBodyValue(value, '', {
|
|
106
|
+
seen: new WeakSet(),
|
|
107
|
+
remainingProperties: maximumCapturedProperties,
|
|
108
|
+
});
|
|
109
|
+
const serialized = JSON.stringify(sanitized);
|
|
110
|
+
if (serialized === undefined) {
|
|
111
|
+
return captureText(String(value), contentType, maxBodyBytes);
|
|
112
|
+
}
|
|
113
|
+
const sizeBytes = Buffer.byteLength(serialized);
|
|
114
|
+
return {
|
|
115
|
+
kind: 'json',
|
|
116
|
+
contentType,
|
|
117
|
+
sizeBytes,
|
|
118
|
+
truncated: sizeBytes > maxBodyBytes,
|
|
119
|
+
value: sizeBytes > maxBodyBytes
|
|
120
|
+
? truncateUtf8(serialized, maxBodyBytes)
|
|
121
|
+
: sanitized,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Recursively removes sensitive fields and bounds complex object traversal.
|
|
126
|
+
*
|
|
127
|
+
* @param value - Current body value.
|
|
128
|
+
* @param key - Owning object key used for sensitive-name detection.
|
|
129
|
+
* @param state - Shared circular-reference and property-count state.
|
|
130
|
+
* @param depth - Current traversal depth.
|
|
131
|
+
* @returns JSON-safe value suitable for structured logging.
|
|
132
|
+
*/
|
|
133
|
+
function sanitizeBodyValue(value, key, state, depth = 0) {
|
|
134
|
+
if (isSensitiveName(key))
|
|
135
|
+
return redactedValue;
|
|
136
|
+
if (value === null || value === undefined)
|
|
137
|
+
return value;
|
|
138
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
if (typeof value === 'bigint')
|
|
142
|
+
return value.toString();
|
|
143
|
+
if (typeof value !== 'object')
|
|
144
|
+
return String(value);
|
|
145
|
+
if (state.seen.has(value))
|
|
146
|
+
return '[Circular]';
|
|
147
|
+
if (depth >= maximumCaptureDepth)
|
|
148
|
+
return '[Maximum depth reached]';
|
|
149
|
+
state.seen.add(value);
|
|
150
|
+
if (value instanceof Date)
|
|
151
|
+
return value.toISOString();
|
|
152
|
+
if (value instanceof Error) {
|
|
153
|
+
return {
|
|
154
|
+
name: value.name,
|
|
155
|
+
message: value.message,
|
|
156
|
+
stack: value.stack,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
160
|
+
return `[Binary ${value.byteLength} bytes]`;
|
|
161
|
+
}
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
const captured = [];
|
|
164
|
+
for (const item of value) {
|
|
165
|
+
if (state.remainingProperties <= 0) {
|
|
166
|
+
captured.push('[Capture limit reached]');
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
state.remainingProperties -= 1;
|
|
170
|
+
captured.push(sanitizeBodyValue(item, '', state, depth + 1));
|
|
171
|
+
}
|
|
172
|
+
return captured;
|
|
173
|
+
}
|
|
174
|
+
const captured = {};
|
|
175
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
176
|
+
if (state.remainingProperties <= 0) {
|
|
177
|
+
captured['[capture]'] = 'Property limit reached';
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
state.remainingProperties -= 1;
|
|
181
|
+
captured[childKey] = sanitizeBodyValue(childValue, childKey, state, depth + 1);
|
|
182
|
+
}
|
|
183
|
+
return captured;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Redacts credential-bearing headers before a record reaches Pino.
|
|
187
|
+
*
|
|
188
|
+
* @param headers - Fastify request or response header collection.
|
|
189
|
+
* @returns Header object safe for local request inspection.
|
|
190
|
+
*/
|
|
191
|
+
export function redactHeaders(headers) {
|
|
192
|
+
const captured = {};
|
|
193
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
194
|
+
captured[name] = isSensitiveName(name)
|
|
195
|
+
? redactedValue
|
|
196
|
+
: boundedHeaderValue(value);
|
|
197
|
+
}
|
|
198
|
+
return captured;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Bounds one response or request header value.
|
|
202
|
+
*
|
|
203
|
+
* @param value - Original Fastify header value.
|
|
204
|
+
* @returns Original scalar shape with long strings shortened.
|
|
205
|
+
*/
|
|
206
|
+
function boundedHeaderValue(value) {
|
|
207
|
+
if (typeof value === 'string') {
|
|
208
|
+
return value.length > maximumHeaderValueLength
|
|
209
|
+
? `${value.slice(0, maximumHeaderValueLength)}…`
|
|
210
|
+
: value;
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(value)) {
|
|
213
|
+
return value.map(item => boundedHeaderValue(item));
|
|
214
|
+
}
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Detects common credential and session field names.
|
|
219
|
+
*
|
|
220
|
+
* @param name - Header or JSON property name.
|
|
221
|
+
* @returns True when the value must be removed from the capture.
|
|
222
|
+
*/
|
|
223
|
+
function isSensitiveName(name) {
|
|
224
|
+
const normalized = name.toLowerCase().replace(/[-_]/g, '');
|
|
225
|
+
return normalized === 'authorization'
|
|
226
|
+
|| normalized === 'proxyauthorization'
|
|
227
|
+
|| normalized === 'cookie'
|
|
228
|
+
|| normalized === 'setcookie'
|
|
229
|
+
|| normalized === 'password'
|
|
230
|
+
|| normalized === 'passphrase'
|
|
231
|
+
|| normalized === 'secret'
|
|
232
|
+
|| normalized === 'token'
|
|
233
|
+
|| normalized === 'accesstoken'
|
|
234
|
+
|| normalized === 'refreshtoken'
|
|
235
|
+
|| normalized === 'apikey'
|
|
236
|
+
|| normalized === 'xapikey';
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Converts a Fastify header into one displayable content-type string.
|
|
240
|
+
*
|
|
241
|
+
* @param value - Request or response content-type header.
|
|
242
|
+
* @returns First string value when present.
|
|
243
|
+
*/
|
|
244
|
+
export function headerValue(value) {
|
|
245
|
+
if (typeof value === 'string')
|
|
246
|
+
return value;
|
|
247
|
+
if (Array.isArray(value) && typeof value[0] === 'string')
|
|
248
|
+
return value[0];
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Identifies payloads that Fastify will stream instead of serialize.
|
|
253
|
+
*
|
|
254
|
+
* @param value - Candidate outgoing response payload.
|
|
255
|
+
* @returns True when the value exposes the Node stream pipe contract.
|
|
256
|
+
*/
|
|
257
|
+
function isStreamLike(value) {
|
|
258
|
+
return typeof value === 'object'
|
|
259
|
+
&& value !== null
|
|
260
|
+
&& 'pipe' in value
|
|
261
|
+
&& typeof value.pipe === 'function';
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Identifies media types that are safe to decode as UTF-8 text.
|
|
265
|
+
*
|
|
266
|
+
* @param contentType - HTTP content type.
|
|
267
|
+
* @returns True for JSON and common textual media types.
|
|
268
|
+
*/
|
|
269
|
+
function isTextContentType(contentType) {
|
|
270
|
+
const normalized = (contentType || '').toLowerCase();
|
|
271
|
+
return normalized.startsWith('text/')
|
|
272
|
+
|| normalized.includes('json')
|
|
273
|
+
|| normalized.includes('xml')
|
|
274
|
+
|| normalized.includes('javascript')
|
|
275
|
+
|| normalized.includes('x-www-form-urlencoded')
|
|
276
|
+
|| normalized.includes('svg');
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Identifies JSON media types.
|
|
280
|
+
*
|
|
281
|
+
* @param contentType - HTTP content type.
|
|
282
|
+
* @returns True when the media type uses JSON.
|
|
283
|
+
*/
|
|
284
|
+
function isJsonContentType(contentType) {
|
|
285
|
+
return (contentType || '').toLowerCase().includes('json');
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Identifies URL-encoded form media types requiring field-level redaction.
|
|
289
|
+
*
|
|
290
|
+
* @param contentType - HTTP content type.
|
|
291
|
+
* @returns True when the body uses URL-encoded form syntax.
|
|
292
|
+
*/
|
|
293
|
+
function isFormContentType(contentType) {
|
|
294
|
+
return (contentType || '').toLowerCase().includes('x-www-form-urlencoded');
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Detects likely JSON text when a content type was omitted or incorrect.
|
|
298
|
+
*
|
|
299
|
+
* @param value - Textual payload.
|
|
300
|
+
* @returns True when the trimmed body begins with a JSON container.
|
|
301
|
+
*/
|
|
302
|
+
function looksLikeJson(value) {
|
|
303
|
+
const trimmed = value.trim();
|
|
304
|
+
return (trimmed.startsWith('{') && trimmed.endsWith('}'))
|
|
305
|
+
|| (trimmed.startsWith('[') && trimmed.endsWith(']'));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Truncates text by UTF-8 byte length rather than JavaScript character count.
|
|
309
|
+
*
|
|
310
|
+
* @param value - Original text.
|
|
311
|
+
* @param maxBytes - Maximum bytes to retain.
|
|
312
|
+
* @returns Original or shortened UTF-8 text.
|
|
313
|
+
*/
|
|
314
|
+
function truncateUtf8(value, maxBytes) {
|
|
315
|
+
const buffer = Buffer.from(value);
|
|
316
|
+
if (buffer.byteLength <= maxBytes)
|
|
317
|
+
return value;
|
|
318
|
+
const marker = '…';
|
|
319
|
+
const contentBytes = Math.max(0, maxBytes - Buffer.byteLength(marker));
|
|
320
|
+
return `${buffer.subarray(0, contentBytes).toString('utf8')}…`;
|
|
321
|
+
}
|
package/dist/logging/index.d.ts
CHANGED
package/dist/logging/index.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Redacts common credential forms while preserving exception wording and stack locations. */
|
|
2
|
+
export function redactLogEmail(text) {
|
|
3
|
+
return text
|
|
4
|
+
.replace(/\b(?:Bearer|Basic)\s+[^\s"',;]+/gi, '[REDACTED authorization]')
|
|
5
|
+
.replace(/\b(?:sk-|sk_|rk_|ghp_|github_pat_)[A-Za-z0-9_-]+/g, '[REDACTED key]')
|
|
6
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED token]')
|
|
7
|
+
.replace(/(["']?(?:password|passwd|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|authorization|token|secret)["']?\s*[:=]\s*)(?:"[^"\n]*"|'[^'\n]*'|[^\s,;]+)/gi, '$1[REDACTED]')
|
|
8
|
+
.replace(/((?:set-cookie|cookie)\s*:\s*)[^\r\n]+/gi, '$1[REDACTED]')
|
|
9
|
+
.replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s<>"']+/gi, url => url
|
|
10
|
+
.replace(/(\/\/)[^/@\s]+@/, '$1[REDACTED]@')
|
|
11
|
+
.replace(/[?#].*$/, '?[REDACTED parameters]'));
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type * as logging from './contracts/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Captures bounded, redacted payloads for grouped console logs and devtools.
|
|
5
|
+
* Register before routes on a Fastify host using app.log.logger. Fastify's own
|
|
6
|
+
* arrival/completion records provide lifecycle timing; streams are never consumed.
|
|
7
|
+
* @param server - Caller-owned HTTP server.
|
|
8
|
+
* @param options - Development capture and private-route exclusion policy.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerHttpExchangeMonitor(server: FastifyInstance, options?: logging.HttpExchangeMonitorOptions): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isInteractiveDevelopmentEnvironment } from '../devtools/index.js';
|
|
2
|
+
import { captureBody, headerValue, redactHeaders } from './httpBodyCapture.js';
|
|
3
|
+
/**
|
|
4
|
+
* Captures bounded, redacted payloads for grouped console logs and devtools.
|
|
5
|
+
* Register before routes on a Fastify host using app.log.logger. Fastify's own
|
|
6
|
+
* arrival/completion records provide lifecycle timing; streams are never consumed.
|
|
7
|
+
* @param server - Caller-owned HTTP server.
|
|
8
|
+
* @param options - Development capture and private-route exclusion policy.
|
|
9
|
+
*/
|
|
10
|
+
export function registerHttpExchangeMonitor(server, options = {}) {
|
|
11
|
+
if (options.enabled === false || !isInteractiveDevelopmentEnvironment(options.environment ?? process.env.NODE_ENV))
|
|
12
|
+
return;
|
|
13
|
+
const configured = options.maxBodyBytes;
|
|
14
|
+
const maxBodyBytes = configured && Number.isFinite(configured) && configured > 0 ? Math.min(65536, Math.floor(configured)) : 16384;
|
|
15
|
+
const ended = new WeakSet();
|
|
16
|
+
/** Reports one terminal interruption even if timeout and socket close both fire. */
|
|
17
|
+
function interrupted(request, message) {
|
|
18
|
+
if (ended.has(request))
|
|
19
|
+
return;
|
|
20
|
+
ended.add(request);
|
|
21
|
+
request.log.warn(message);
|
|
22
|
+
}
|
|
23
|
+
server.addHook('onRequest', (request, reply, done) => {
|
|
24
|
+
reply.raw.once('close', () => {
|
|
25
|
+
if (!reply.raw.writableFinished)
|
|
26
|
+
interrupted(request, 'request aborted');
|
|
27
|
+
});
|
|
28
|
+
done();
|
|
29
|
+
});
|
|
30
|
+
server.addHook('onTimeout', (request, _reply, done) => { interrupted(request, 'request timed out'); done(); });
|
|
31
|
+
server.addHook('onRequestAbort', (request, done) => { interrupted(request, 'request aborted'); done(); });
|
|
32
|
+
server.addHook('onResponse', (request, _reply, done) => { ended.add(request); done(); });
|
|
33
|
+
server.addHook('onSend', (request, reply, payload, done) => {
|
|
34
|
+
if (!options.exclude?.(request)) {
|
|
35
|
+
const httpExchange = {
|
|
36
|
+
request: { method: request.method, url: request.url, headers: redactHeaders(request.headers), body: captureBody(request.body, headerValue(request.headers['content-type']), maxBodyBytes) },
|
|
37
|
+
response: { statusCode: reply.statusCode, headers: redactHeaders(reply.getHeaders()), body: captureBody(payload, headerValue(reply.getHeader('content-type')), maxBodyBytes) },
|
|
38
|
+
};
|
|
39
|
+
request.log.info({ httpExchange }, 'request exchange');
|
|
40
|
+
}
|
|
41
|
+
done(null, payload);
|
|
42
|
+
});
|
|
43
|
+
}
|