@db3.ai/app 0.1.0-beta.1 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -9
- package/agent-instructions.md +76 -0
- package/dist/ai/AIErrors.d.ts +7 -1
- package/dist/ai/AIErrors.js +8 -1
- package/dist/ai/AIFailover.d.ts +10 -0
- package/dist/ai/AIFailover.js +3 -2
- package/dist/ai/AIProviderAdmission.d.ts +101 -0
- package/dist/ai/AIProviderAdmission.js +336 -0
- package/dist/ai/AIRateLimiter.d.ts +11 -2
- package/dist/ai/AIRateLimiter.js +125 -10
- package/dist/ai/AIRequestTrackingError.d.ts +22 -0
- package/dist/ai/AIRequestTrackingError.js +33 -0
- package/dist/ai/Agent.d.ts +7 -7
- package/dist/ai/Agent.js +84 -56
- package/dist/ai/Ai.d.ts +11 -3
- package/dist/ai/Ai.js +94 -44
- package/dist/ai/OpenAIProviderError.js +1 -1
- package/dist/ai/ProviderSseObserver.d.ts +19 -0
- package/dist/ai/ProviderSseObserver.js +98 -0
- package/dist/ai/aiServiceTier.d.ts +5 -0
- package/dist/ai/aiServiceTier.js +14 -0
- package/dist/ai/chunkEmbeddingText.d.ts +14 -0
- package/dist/ai/chunkEmbeddingText.js +147 -0
- package/dist/ai/contracts/AI.d.ts +16 -0
- package/dist/ai/contracts/AIRequestErrorDiagnostics.d.ts +12 -0
- package/dist/ai/contracts/AIRequestErrorDiagnostics.js +1 -0
- package/dist/ai/contracts/AIServiceTier.d.ts +2 -0
- package/dist/ai/contracts/AIServiceTier.js +1 -0
- package/dist/ai/contracts/Agent.d.ts +3 -0
- package/dist/ai/contracts/EmbeddingTextChunk.d.ts +25 -0
- package/dist/ai/contracts/EmbeddingTextChunk.js +1 -0
- package/dist/ai/contracts/Pricing.d.ts +7 -1
- package/dist/ai/contracts/Providers.d.ts +2 -0
- package/dist/ai/contracts/index.d.ts +2 -0
- package/dist/ai/contracts/index.js +1 -0
- package/dist/ai/index.d.ts +8 -2
- package/dist/ai/index.js +5 -2
- package/dist/ai/{Models.d.ts → modelPricing.d.ts} +2 -2
- package/dist/ai/{Models.js → modelPricing.js} +38 -4
- package/dist/apps/AppInstallation.d.ts +21 -0
- package/dist/apps/AppInstallation.js +16 -0
- package/dist/apps/AppRouteError.d.ts +6 -0
- package/dist/apps/AppRouteError.js +10 -0
- package/dist/apps/AppService.d.ts +24 -0
- package/dist/apps/AppService.js +21 -0
- package/dist/apps/Apps.d.ts +59 -0
- package/dist/apps/Apps.js +566 -0
- package/dist/apps/appsPlugin.d.ts +3 -0
- package/dist/apps/appsPlugin.js +36 -0
- package/dist/apps/commands/disableApp.d.ts +2 -0
- package/dist/apps/commands/disableApp.js +5 -0
- package/dist/apps/commands/enableApp.d.ts +2 -0
- package/dist/apps/commands/enableApp.js +5 -0
- package/dist/apps/commands/index.d.ts +10 -0
- package/dist/apps/commands/index.js +27 -0
- package/dist/apps/commands/installApp.d.ts +2 -0
- package/dist/apps/commands/installApp.js +5 -0
- package/dist/apps/commands/listApps.d.ts +2 -0
- package/dist/apps/commands/listApps.js +5 -0
- package/dist/apps/commands/makeAppMigration.d.ts +2 -0
- package/dist/apps/commands/makeAppMigration.js +9 -0
- package/dist/apps/commands/rollbackApp.d.ts +2 -0
- package/dist/apps/commands/rollbackApp.js +5 -0
- package/dist/apps/commands/uninstallApp.d.ts +2 -0
- package/dist/apps/commands/uninstallApp.js +5 -0
- package/dist/apps/contracts/AppAction.d.ts +2 -0
- package/dist/apps/contracts/AppAction.js +1 -0
- package/dist/apps/contracts/AppDefinition.d.ts +45 -0
- package/dist/apps/contracts/AppDefinition.js +1 -0
- package/dist/apps/contracts/AppDescription.d.ts +32 -0
- package/dist/apps/contracts/AppDescription.js +1 -0
- package/dist/apps/contracts/AppManifest.d.ts +30 -0
- package/dist/apps/contracts/AppManifest.js +1 -0
- package/dist/apps/contracts/AppNavigation.d.ts +9 -0
- package/dist/apps/contracts/AppNavigation.js +1 -0
- package/dist/apps/contracts/AppNavigationBadge.d.ts +6 -0
- package/dist/apps/contracts/AppNavigationBadge.js +1 -0
- package/dist/apps/contracts/AppNavigationContext.d.ts +8 -0
- package/dist/apps/contracts/AppNavigationContext.js +1 -0
- package/dist/apps/contracts/AppNavigationResult.d.ts +12 -0
- package/dist/apps/contracts/AppNavigationResult.js +1 -0
- package/dist/apps/contracts/AppRoute.d.ts +19 -0
- package/dist/apps/contracts/AppRoute.js +1 -0
- package/dist/apps/contracts/DiscoveredApp.d.ts +10 -0
- package/dist/apps/contracts/DiscoveredApp.js +1 -0
- package/dist/apps/contracts/InstalledApps.d.ts +3 -0
- package/dist/apps/contracts/InstalledApps.js +1 -0
- package/dist/apps/contracts/index.d.ts +11 -0
- package/dist/apps/contracts/index.js +1 -0
- package/dist/apps/defineApp.d.ts +7 -0
- package/dist/apps/defineApp.js +8 -0
- package/dist/apps/discoverApps.d.ts +3 -0
- package/dist/apps/discoverApps.js +62 -0
- package/dist/apps/fastify.d.ts +1 -0
- package/dist/apps/fastify.js +1 -0
- package/dist/apps/index.d.ts +7 -0
- package/dist/apps/index.js +7 -0
- package/dist/apps/normalizeAppNavigation.d.ts +3 -0
- package/dist/apps/normalizeAppNavigation.js +30 -0
- package/dist/apps/registerAppRoutes.d.ts +6 -0
- package/dist/apps/registerAppRoutes.js +52 -0
- package/dist/apps/validateAppDefinitions.d.ts +3 -0
- package/dist/apps/validateAppDefinitions.js +65 -0
- package/dist/apps/vite.d.ts +1 -0
- package/dist/apps/vite.js +1 -0
- package/dist/apps/writeAppTypes.d.ts +2 -0
- package/dist/apps/writeAppTypes.js +13 -0
- package/dist/auth/Auth.d.ts +18 -0
- package/dist/auth/Auth.js +89 -13
- package/dist/auth/PasswordLoginAttempt.d.ts +50 -0
- package/dist/auth/PasswordLoginAttempt.js +60 -0
- package/dist/auth/PasswordSuspendedError.d.ts +10 -0
- package/dist/auth/PasswordSuspendedError.js +13 -0
- package/dist/auth/contracts/PasswordAuthProviderOptions.d.ts +26 -0
- package/dist/auth/contracts/PasswordAuthProviderOptions.js +1 -0
- package/dist/auth/contracts/PasswordSuspension.d.ts +7 -0
- package/dist/auth/contracts/PasswordSuspension.js +1 -0
- package/dist/auth/contracts/PasswordSuspensionOptions.d.ts +10 -0
- package/dist/auth/contracts/PasswordSuspensionOptions.js +1 -0
- package/dist/auth/contracts/index.d.ts +3 -0
- package/dist/auth/contracts/index.js +3 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/providers/PasswordAuthProvider.d.ts +19 -23
- package/dist/auth/providers/PasswordAuthProvider.js +96 -8
- package/dist/auth/providers/resolveAuthProviders.js +1 -0
- package/dist/cli/Cli.d.ts +2 -0
- package/dist/cli/Cli.js +8 -0
- package/dist/cli/commands/repl.js +2 -1
- package/dist/cli/contracts/CliCommand.d.ts +2 -0
- package/dist/cli/contracts/CliCommandInfo.d.ts +12 -0
- package/dist/cli/contracts/CliCommandInfo.js +1 -0
- package/dist/cli/contracts/index.d.ts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/listDb3Commands.d.ts +8 -0
- package/dist/cli/listDb3Commands.js +10 -0
- package/dist/cli/runDb3.d.ts +3 -6
- package/dist/cli/runDb3.js +5 -59
- package/dist/cli/withDb3Cli.d.ts +4 -0
- package/dist/cli/withDb3Cli.js +60 -0
- package/dist/db/ActiveQueryBuilder.d.ts +8 -0
- package/dist/db/ActiveQueryBuilder.js +11 -0
- package/dist/db/ActiveRecord.d.ts +6 -0
- package/dist/db/ActiveRecord.js +8 -0
- package/dist/db/connection.js +14 -2
- package/dist/db/fields/TimestampField.d.ts +1 -1
- package/dist/db/fields/TimestampField.js +7 -12
- package/dist/db/migrations/DatabaseMigrationManager.d.ts +3 -1
- package/dist/db/migrations/DatabaseMigrationManager.js +9 -3
- package/dist/db/migrations/contracts/DatabaseMigration.d.ts +7 -0
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +1 -1
- package/dist/db/migrations/{KnexMigrationRenderer.d.ts → renderKnexMigration.d.ts} +4 -3
- package/dist/db/migrations/{KnexMigrationRenderer.js → renderKnexMigration.js} +7 -6
- package/dist/flows/FlowCompiler.js +1 -1
- package/dist/flows/Flows.js +1 -1
- package/dist/flows/index.d.ts +1 -1
- package/dist/flows/index.js +1 -1
- package/dist/health/Health.d.ts +41 -0
- package/dist/health/Health.js +153 -0
- package/dist/health/contracts/Health.d.ts +36 -0
- package/dist/health/contracts/Health.js +1 -0
- package/dist/health/contracts/HealthHttp.d.ts +15 -0
- package/dist/health/contracts/HealthHttp.js +1 -0
- package/dist/health/contracts/index.d.ts +2 -0
- package/dist/health/contracts/index.js +1 -0
- package/dist/health/databaseHealthCheck.d.ts +15 -0
- package/dist/health/databaseHealthCheck.js +18 -0
- package/dist/health/fastify.d.ts +13 -0
- package/dist/health/fastify.js +44 -0
- package/dist/health/index.d.ts +3 -0
- package/dist/health/index.js +2 -0
- package/dist/in-app/InApp.d.ts +33 -0
- package/dist/in-app/InApp.js +191 -0
- package/dist/in-app/InAppError.d.ts +6 -0
- package/dist/in-app/InAppError.js +10 -0
- package/dist/in-app/InAppRecord.d.ts +36 -0
- package/dist/in-app/InAppRecord.js +24 -0
- package/dist/in-app/contracts/InAppInbox.d.ts +45 -0
- package/dist/in-app/contracts/InAppInbox.js +1 -0
- package/dist/in-app/contracts/InAppMessage.d.ts +22 -0
- package/dist/in-app/contracts/InAppMessage.js +1 -0
- package/dist/in-app/contracts/InAppScope.d.ts +17 -0
- package/dist/in-app/contracts/InAppScope.js +1 -0
- package/dist/in-app/contracts/index.d.ts +3 -0
- package/dist/in-app/contracts/index.js +3 -0
- package/dist/in-app/index.d.ts +4 -0
- package/dist/in-app/index.js +4 -0
- package/dist/in-app/validation.d.ts +21 -0
- package/dist/in-app/validation.js +50 -0
- package/dist/index.d.ts +2 -14
- package/dist/index.js +2 -5
- package/dist/logging/DevelopmentConsole.d.ts +42 -0
- package/dist/logging/DevelopmentConsole.js +162 -0
- package/dist/logging/Log.d.ts +3 -1
- package/dist/logging/Log.js +5 -2
- package/dist/logging/PrettyLogFormatter.d.ts +16 -0
- package/dist/logging/PrettyLogFormatter.js +151 -0
- package/dist/logging/acquireDevelopmentConsole.d.ts +6 -0
- package/dist/logging/acquireDevelopmentConsole.js +21 -0
- package/dist/logging/contracts/DevelopmentConsoleOptions.d.ts +16 -0
- package/dist/logging/contracts/DevelopmentConsoleOptions.js +1 -0
- package/dist/logging/contracts/EmailLogOptions.d.ts +8 -0
- package/dist/logging/contracts/EmailLogOptions.js +1 -0
- package/dist/logging/contracts/HttpExchangeMonitorOptions.d.ts +12 -0
- package/dist/logging/contracts/HttpExchangeMonitorOptions.js +1 -0
- package/dist/logging/contracts/LogTransportOptions.d.ts +19 -0
- package/dist/logging/contracts/LogTransportOptions.js +1 -0
- package/dist/logging/contracts/LoggingOptions.d.ts +12 -1
- package/dist/logging/contracts/index.d.ts +4 -0
- package/dist/logging/contracts/index.js +4 -0
- package/dist/logging/drivers/PinoLoggerDriver.d.ts +3 -1
- package/dist/logging/drivers/PinoLoggerDriver.js +75 -16
- package/dist/logging/formatLogEmail.d.ts +4 -0
- package/dist/logging/formatLogEmail.js +21 -0
- package/dist/logging/highlightQueueMessage.d.ts +11 -0
- package/dist/logging/highlightQueueMessage.js +36 -0
- package/dist/logging/httpBodyCapture.d.ts +24 -0
- package/dist/logging/httpBodyCapture.js +321 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.js +2 -0
- package/dist/logging/redactLogEmail.d.ts +2 -0
- package/dist/logging/redactLogEmail.js +12 -0
- package/dist/logging/registerHttpExchangeMonitor.d.ts +10 -0
- package/dist/logging/registerHttpExchangeMonitor.js +43 -0
- package/dist/logging/transports/EmailLogTransport.d.ts +18 -0
- package/dist/logging/transports/EmailLogTransport.js +47 -0
- package/dist/mail/Mail.d.ts +2 -0
- package/dist/mail/Mail.js +3 -0
- package/dist/mail/MailDeliveryError.d.ts +9 -0
- package/dist/mail/MailDeliveryError.js +16 -0
- package/dist/mail/index.d.ts +1 -0
- package/dist/mail/index.js +1 -0
- package/dist/mail/transports/MailgunTransport.js +2 -0
- package/dist/mail/transports/ResendTransport.d.ts +3 -2
- package/dist/mail/transports/ResendTransport.js +22 -14
- package/dist/media/MediaManager.d.ts +4 -2
- package/dist/media/MediaManager.js +40 -8
- package/dist/network/BlockedUrlError.d.ts +16 -0
- package/dist/network/BlockedUrlError.js +19 -0
- package/dist/network/assertPublicUrl.d.ts +22 -0
- package/dist/network/assertPublicUrl.js +67 -0
- package/dist/network/contracts/GuardedFetch.d.ts +35 -0
- package/dist/network/contracts/GuardedFetch.js +1 -0
- package/dist/network/contracts/PublicUrlOptions.d.ts +17 -0
- package/dist/network/contracts/PublicUrlOptions.js +1 -0
- package/dist/network/contracts/index.d.ts +2 -0
- package/dist/network/contracts/index.js +2 -0
- package/dist/network/guardedFetch.d.ts +29 -0
- package/dist/network/guardedFetch.js +133 -0
- package/dist/network/index.d.ts +6 -0
- package/dist/network/index.js +6 -0
- package/dist/network/playwright/GuardedBrowserProxy.d.ts +32 -0
- package/dist/network/playwright/GuardedBrowserProxy.js +172 -0
- package/dist/network/playwright/contracts/GuardedBrowserContextOptions.d.ts +37 -0
- package/dist/network/playwright/contracts/GuardedBrowserContextOptions.js +1 -0
- package/dist/network/playwright/contracts/index.d.ts +1 -0
- package/dist/network/playwright/contracts/index.js +1 -0
- package/dist/network/playwright/index.d.ts +2 -0
- package/dist/network/playwright/index.js +2 -0
- package/dist/network/playwright/newGuardedBrowserContext.d.ts +21 -0
- package/dist/network/playwright/newGuardedBrowserContext.js +86 -0
- package/dist/network/privateNetworkAddresses.d.ts +44 -0
- package/dist/network/privateNetworkAddresses.js +157 -0
- package/dist/network/publicAddressLookup.d.ts +26 -0
- package/dist/network/publicAddressLookup.js +42 -0
- package/dist/notifications/Notifications.d.ts +27 -0
- package/dist/notifications/Notifications.js +99 -0
- package/dist/notifications/contracts/Notification.d.ts +56 -0
- package/dist/notifications/contracts/Notification.js +1 -0
- package/dist/notifications/contracts/NotificationDelivery.d.ts +11 -0
- package/dist/notifications/contracts/NotificationDelivery.js +1 -0
- package/dist/notifications/contracts/index.d.ts +2 -0
- package/dist/notifications/contracts/index.js +2 -0
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.js +2 -0
- package/dist/queue/JobRunAttempt.d.ts +16 -0
- package/dist/queue/JobRunAttempt.js +17 -0
- package/dist/queue/JobRunEvent.d.ts +15 -0
- package/dist/queue/JobRunEvent.js +16 -0
- package/dist/queue/JobRunStoppedError.d.ts +6 -0
- package/dist/queue/JobRunStoppedError.js +10 -0
- package/dist/queue/Queue.d.ts +19 -4
- package/dist/queue/Queue.js +122 -9
- package/dist/queue/QueueTerminalError.d.ts +9 -0
- package/dist/queue/QueueTerminalError.js +12 -0
- package/dist/queue/QueueWorker.d.ts +12 -5
- package/dist/queue/QueueWorker.js +94 -9
- package/dist/queue/QueueWorkerReporter.d.ts +17 -0
- package/dist/queue/QueueWorkerReporter.js +71 -0
- package/dist/queue/QueueableJob.d.ts +2 -2
- package/dist/queue/RunnableJob.d.ts +78 -0
- package/dist/queue/RunnableJob.js +284 -0
- package/dist/queue/console.d.ts +8 -0
- package/dist/queue/console.js +53 -19
- package/dist/queue/contracts/QueueDriver.d.ts +8 -0
- package/dist/queue/contracts/QueueJobRunner.d.ts +4 -0
- package/dist/queue/contracts/QueueJobRunner.js +1 -0
- package/dist/queue/contracts/QueuePayload.d.ts +1 -1
- package/dist/queue/contracts/QueueSelection.d.ts +11 -0
- package/dist/queue/contracts/QueueSelection.js +1 -0
- package/dist/queue/contracts/QueueService.d.ts +18 -4
- package/dist/queue/contracts/QueueWorkerLifecycle.d.ts +2 -2
- package/dist/queue/contracts/QueueWorkerSnapshot.d.ts +14 -0
- package/dist/queue/contracts/QueueWorkerSnapshot.js +1 -0
- package/dist/queue/contracts/QueueableJob.d.ts +7 -3
- package/dist/queue/contracts/QueueableJobContract.d.ts +12 -0
- package/dist/queue/contracts/QueueableJobContract.js +1 -0
- package/dist/queue/contracts/index.d.ts +4 -0
- package/dist/queue/contracts/index.js +2 -0
- package/dist/queue/drivers/DatabaseQueueDriver.d.ts +4 -0
- package/dist/queue/drivers/DatabaseQueueDriver.js +27 -3
- package/dist/queue/drivers/RedisQueueDriver.d.ts +7 -0
- package/dist/queue/drivers/RedisQueueDriver.js +37 -0
- package/dist/queue/index.d.ts +5 -0
- package/dist/queue/index.js +5 -0
- package/dist/queue/jobEnvelopeHasPrefix.d.ts +3 -0
- package/dist/queue/jobEnvelopeHasPrefix.js +4 -0
- package/dist/scheduler/ScheduledEvent.d.ts +8 -4
- package/dist/scheduler/ScheduledEvent.js +17 -3
- package/dist/scheduler/ScheduledJob.d.ts +1 -1
- package/dist/scheduler/Scheduler.d.ts +5 -0
- package/dist/scheduler/Scheduler.js +92 -28
- package/dist/scheduler/SchedulerCheckpointRecord.d.ts +16 -0
- package/dist/scheduler/SchedulerCheckpointRecord.js +53 -0
- package/dist/scheduler/SchedulerWorker.d.ts +3 -5
- package/dist/scheduler/SchedulerWorker.js +58 -14
- package/dist/scheduler/console.js +3 -1
- package/dist/scheduler/contracts/Schedule.d.ts +19 -0
- package/dist/scheduler/contracts/Schedule.js +1 -0
- package/dist/scheduler/contracts/Scheduler.d.ts +18 -6
- package/dist/scheduler/contracts/SchedulerCheckpoint.d.ts +23 -0
- package/dist/scheduler/contracts/SchedulerCheckpoint.js +1 -0
- package/dist/scheduler/contracts/SchedulerConsole.d.ts +5 -0
- package/dist/scheduler/contracts/SchedulerWorker.d.ts +6 -1
- package/dist/scheduler/contracts/index.d.ts +2 -0
- package/dist/scheduler/contracts/index.js +2 -0
- package/dist/scheduler/index.d.ts +1 -0
- package/dist/scheduler/index.js +1 -0
- package/dist/server/App.d.ts +36 -4
- package/dist/server/App.js +57 -7
- package/dist/server/contracts/HttpErrorHandlerOptions.d.ts +29 -0
- package/dist/server/contracts/HttpErrorHandlerOptions.js +1 -0
- package/dist/server/contracts/index.d.ts +1 -0
- package/dist/server/contracts/index.js +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +4 -0
- package/dist/server/publicServerErrorMessage.d.ts +11 -0
- package/dist/server/publicServerErrorMessage.js +16 -0
- package/dist/server/registerBrowserJsonFormatting.d.ts +16 -0
- package/dist/server/registerBrowserJsonFormatting.js +100 -0
- package/dist/server/registerHttpErrorHandler.d.ts +20 -0
- package/dist/server/registerHttpErrorHandler.js +93 -0
- package/dist/ssr/vite/contracts/ViteSsrOptions.d.ts +2 -0
- package/dist/ssr/vite/fastifyViteSsr.js +4 -3
- package/dist/storage/drivers/FlystorageDisk.js +2 -2
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +2 -2
- package/dist/websocket/WebSocketChannels.d.ts +18 -0
- package/dist/websocket/WebSocketChannels.js +129 -0
- package/dist/websocket/WebSocketConnection.d.ts +25 -0
- package/dist/websocket/WebSocketConnection.js +244 -0
- package/dist/websocket/WebSockets.d.ts +41 -0
- package/dist/websocket/WebSockets.js +194 -0
- package/dist/websocket/client/WebSocketClient.d.ts +21 -0
- package/dist/websocket/client/WebSocketClient.js +230 -0
- package/dist/websocket/client/index.d.ts +3 -0
- package/dist/websocket/client/index.js +1 -0
- package/dist/websocket/contracts/WebSocketChannel.d.ts +20 -0
- package/dist/websocket/contracts/WebSocketChannel.js +1 -0
- package/dist/websocket/contracts/WebSocketClient.d.ts +45 -0
- package/dist/websocket/contracts/WebSocketClient.js +1 -0
- package/dist/websocket/contracts/WebSocketEndpoint.d.ts +43 -0
- package/dist/websocket/contracts/WebSocketEndpoint.js +1 -0
- package/dist/websocket/contracts/WebSocketOptions.d.ts +48 -0
- package/dist/websocket/contracts/WebSocketOptions.js +1 -0
- package/dist/websocket/contracts/WebSocketPresence.d.ts +6 -0
- package/dist/websocket/contracts/WebSocketPresence.js +1 -0
- package/dist/websocket/contracts/WebSocketServiceOptions.d.ts +12 -0
- package/dist/websocket/contracts/WebSocketServiceOptions.js +1 -0
- package/dist/websocket/contracts/index.d.ts +6 -0
- package/dist/websocket/contracts/index.js +1 -0
- package/dist/websocket/defineChannel.d.ts +8 -0
- package/dist/websocket/defineChannel.js +13 -0
- package/dist/websocket/defineWebSocket.d.ts +14 -0
- package/dist/websocket/defineWebSocket.js +4 -0
- package/dist/websocket/fastify.d.ts +11 -0
- package/dist/websocket/fastify.js +38 -0
- package/dist/websocket/index.d.ts +5 -0
- package/dist/websocket/index.js +4 -0
- package/package.json +108 -7
- package/src/ai/AIErrors.ts +10 -0
- package/src/ai/AIFailover.ts +3 -2
- package/src/ai/AIProviderAdmission.ts +329 -0
- package/src/ai/AIRateLimiter.ts +134 -10
- package/src/ai/AIRequestTrackingError.ts +37 -0
- package/src/ai/Agent.ts +85 -71
- package/src/ai/Ai.ts +104 -48
- package/src/ai/OpenAIProviderError.ts +1 -1
- package/src/ai/ProviderSseObserver.ts +89 -0
- package/src/ai/README.md +216 -11
- package/src/ai/aiServiceTier.ts +14 -0
- package/src/ai/chunkEmbeddingText.ts +137 -0
- package/src/ai/contracts/AI.ts +17 -0
- package/src/ai/contracts/AIRequestErrorDiagnostics.ts +12 -0
- package/src/ai/contracts/AIServiceTier.ts +2 -0
- package/src/ai/contracts/Agent.ts +3 -0
- package/src/ai/contracts/EmbeddingTextChunk.ts +26 -0
- package/src/ai/contracts/Pricing.ts +7 -1
- package/src/ai/contracts/Providers.ts +2 -0
- package/src/ai/contracts/index.ts +2 -0
- package/src/ai/examples/prepareDocumentEmbeddings.ts +20 -0
- package/src/ai/index.ts +9 -2
- package/src/ai/{Models.ts → modelPricing.ts} +37 -4
- package/src/apps/AppInstallation.ts +25 -0
- package/src/apps/AppRouteError.ts +8 -0
- package/src/apps/AppService.ts +30 -0
- package/src/apps/Apps.ts +508 -0
- package/src/apps/README.md +354 -0
- package/src/apps/appsPlugin.ts +34 -0
- package/src/apps/commands/disableApp.ts +6 -0
- package/src/apps/commands/enableApp.ts +6 -0
- package/src/apps/commands/index.ts +29 -0
- package/src/apps/commands/installApp.ts +6 -0
- package/src/apps/commands/listApps.ts +6 -0
- package/src/apps/commands/makeAppMigration.ts +9 -0
- package/src/apps/commands/rollbackApp.ts +6 -0
- package/src/apps/commands/uninstallApp.ts +6 -0
- package/src/apps/contracts/AppAction.ts +2 -0
- package/src/apps/contracts/AppDefinition.ts +49 -0
- package/src/apps/contracts/AppDescription.ts +27 -0
- package/src/apps/contracts/AppManifest.ts +32 -0
- package/src/apps/contracts/AppNavigation.ts +10 -0
- package/src/apps/contracts/AppNavigationBadge.ts +6 -0
- package/src/apps/contracts/AppNavigationContext.ts +6 -0
- package/src/apps/contracts/AppNavigationResult.ts +9 -0
- package/src/apps/contracts/AppRoute.ts +18 -0
- package/src/apps/contracts/DiscoveredApp.ts +11 -0
- package/src/apps/contracts/InstalledApps.ts +2 -0
- package/src/apps/contracts/index.ts +11 -0
- package/src/apps/defineApp.ts +10 -0
- package/src/apps/discoverApps.ts +56 -0
- package/src/apps/fastify.ts +1 -0
- package/src/apps/index.ts +7 -0
- package/src/apps/normalizeAppNavigation.ts +29 -0
- package/src/apps/registerAppRoutes.ts +48 -0
- package/src/apps/validateAppDefinitions.ts +52 -0
- package/src/apps/vite.ts +1 -0
- package/src/apps/writeAppTypes.ts +14 -0
- package/src/auth/Auth.ts +89 -23
- package/src/auth/PasswordLoginAttempt.ts +72 -0
- package/src/auth/PasswordSuspendedError.ts +13 -0
- package/src/auth/README.md +94 -4
- package/src/auth/contracts/PasswordAuthProviderOptions.ts +30 -0
- package/src/auth/contracts/PasswordSuspension.ts +7 -0
- package/src/auth/contracts/PasswordSuspensionOptions.ts +10 -0
- package/src/auth/contracts/index.ts +3 -0
- package/src/auth/examples/runPasswordAuth.ts +6 -6
- package/src/auth/index.ts +2 -0
- package/src/auth/providers/PasswordAuthProvider.ts +96 -30
- package/src/auth/providers/resolveAuthProviders.ts +1 -0
- package/src/cli/Cli.ts +9 -0
- package/src/cli/README.md +19 -0
- package/src/cli/commands/repl.ts +2 -1
- package/src/cli/contracts/CliCommand.ts +2 -0
- package/src/cli/contracts/CliCommandInfo.ts +13 -0
- package/src/cli/contracts/index.ts +1 -0
- package/src/cli/index.ts +1 -0
- package/src/cli/listDb3Commands.ts +12 -0
- package/src/cli/runDb3.ts +5 -51
- package/src/cli/withDb3Cli.ts +55 -0
- package/src/db/ActiveQueryBuilder.ts +12 -0
- package/src/db/ActiveRecord.ts +9 -0
- package/src/db/README.md +45 -1
- package/src/db/connection.ts +12 -1
- package/src/db/examples/FieldNote.ts +7 -13
- package/src/db/examples/KnowledgeNote.ts +7 -13
- package/src/db/examples/runNoteMigrations.ts +11 -11
- package/src/db/fields/TimestampField.ts +8 -17
- package/src/db/migrations/DatabaseMigrationManager.ts +12 -4
- package/src/db/migrations/contracts/DatabaseMigration.ts +9 -0
- package/src/db/migrations/index.ts +1 -1
- package/src/db/migrations/{KnexMigrationRenderer.ts → renderKnexMigration.ts} +8 -14
- package/src/flows/FlowCompiler.ts +1 -1
- package/src/flows/Flows.ts +1 -1
- package/src/flows/index.ts +1 -1
- package/src/health/Health.ts +163 -0
- package/src/health/README.md +46 -0
- package/src/health/contracts/Health.ts +47 -0
- package/src/health/contracts/HealthHttp.ts +19 -0
- package/src/health/contracts/index.ts +2 -0
- package/src/health/databaseHealthCheck.ts +21 -0
- package/src/health/fastify.ts +49 -0
- package/src/health/index.ts +3 -0
- package/src/in-app/InApp.ts +173 -0
- package/src/in-app/InAppError.ts +8 -0
- package/src/in-app/InAppRecord.ts +41 -0
- package/src/in-app/README.md +193 -0
- package/src/in-app/contracts/InAppInbox.ts +51 -0
- package/src/in-app/contracts/InAppMessage.ts +19 -0
- package/src/in-app/contracts/InAppScope.ts +12 -0
- package/src/in-app/contracts/index.ts +3 -0
- package/src/in-app/examples/sendReportReady.ts +14 -0
- package/src/in-app/index.ts +4 -0
- package/src/in-app/validation.ts +49 -0
- package/src/index.ts +2 -22
- package/src/logging/DevelopmentConsole.ts +154 -0
- package/src/logging/Log.ts +5 -2
- package/src/logging/PrettyLogFormatter.ts +165 -0
- package/src/logging/README.md +52 -109
- package/src/logging/acquireDevelopmentConsole.ts +19 -0
- package/src/logging/contracts/DevelopmentConsoleOptions.ts +13 -0
- package/src/logging/contracts/EmailLogOptions.ts +9 -0
- package/src/logging/contracts/HttpExchangeMonitorOptions.ts +13 -0
- package/src/logging/contracts/LogTransportOptions.ts +10 -0
- package/src/logging/contracts/LoggingOptions.ts +12 -1
- package/src/logging/contracts/index.ts +4 -0
- package/src/logging/development/README.md +88 -0
- package/src/logging/drivers/PinoLoggerDriver.ts +72 -20
- package/src/logging/examples/runDevelopmentLogs.ts +36 -0
- package/src/logging/formatLogEmail.ts +24 -0
- package/src/logging/highlightQueueMessage.ts +38 -0
- package/src/logging/httpBodyCapture.ts +393 -0
- package/src/logging/index.ts +2 -0
- package/src/logging/redactLogEmail.ts +12 -0
- package/src/logging/registerHttpExchangeMonitor.ts +45 -0
- package/src/logging/transports/EmailLogTransport.ts +46 -0
- package/src/mail/Mail.ts +5 -0
- package/src/mail/MailDeliveryError.ts +8 -0
- package/src/mail/README.md +11 -1
- package/src/mail/index.ts +1 -0
- package/src/mail/transports/MailgunTransport.ts +2 -0
- package/src/mail/transports/ResendTransport.ts +24 -22
- package/src/media/MediaManager.ts +31 -10
- package/src/media/README.md +4 -0
- package/src/media/examples/runPrivateFiles.ts +2 -2
- package/src/network/BlockedUrlError.ts +19 -0
- package/src/network/README.md +133 -0
- package/src/network/assertPublicUrl.ts +77 -0
- package/src/network/contracts/GuardedFetch.ts +40 -0
- package/src/network/contracts/PublicUrlOptions.ts +17 -0
- package/src/network/contracts/index.ts +2 -0
- package/src/network/examples/runGuardedFetch.ts +35 -0
- package/src/network/guardedFetch.ts +150 -0
- package/src/network/index.ts +6 -0
- package/src/network/playwright/GuardedBrowserProxy.ts +172 -0
- package/src/network/playwright/contracts/GuardedBrowserContextOptions.ts +43 -0
- package/src/network/playwright/contracts/index.ts +1 -0
- package/src/network/playwright/index.ts +2 -0
- package/src/network/playwright/newGuardedBrowserContext.ts +74 -0
- package/src/network/privateNetworkAddresses.ts +166 -0
- package/src/network/publicAddressLookup.ts +49 -0
- package/src/notifications/Notifications.ts +105 -0
- package/src/notifications/README.md +59 -0
- package/src/notifications/contracts/Notification.ts +65 -0
- package/src/notifications/contracts/NotificationDelivery.ts +11 -0
- package/src/notifications/contracts/index.ts +2 -0
- package/src/notifications/examples/ReportReadyNotification.ts +31 -0
- package/src/notifications/index.ts +2 -0
- package/src/queue/JobRunAttempt.ts +17 -0
- package/src/queue/JobRunEvent.ts +16 -0
- package/src/queue/JobRunStoppedError.ts +8 -0
- package/src/queue/Queue.ts +112 -11
- package/src/queue/QueueTerminalError.ts +12 -0
- package/src/queue/QueueWorker.ts +86 -7
- package/src/queue/QueueWorkerReporter.ts +59 -0
- package/src/queue/QueueableJob.ts +2 -2
- package/src/queue/README.md +142 -1
- package/src/queue/RunnableJob.ts +272 -0
- package/src/queue/console.ts +54 -21
- package/src/queue/contracts/QueueDriver.ts +9 -0
- package/src/queue/contracts/QueueJobRunner.ts +4 -0
- package/src/queue/contracts/QueuePayload.ts +1 -1
- package/src/queue/contracts/QueueSelection.ts +11 -0
- package/src/queue/contracts/QueueService.ts +19 -4
- package/src/queue/contracts/QueueWorkerLifecycle.ts +2 -2
- package/src/queue/contracts/QueueWorkerSnapshot.ts +15 -0
- package/src/queue/contracts/QueueableJob.ts +7 -3
- package/src/queue/contracts/QueueableJobContract.ts +13 -0
- package/src/queue/contracts/index.ts +6 -0
- package/src/queue/drivers/DatabaseQueueDriver.ts +25 -3
- package/src/queue/drivers/RedisQueueDriver.ts +36 -0
- package/src/queue/examples/runQueueSelection.ts +34 -0
- package/src/queue/index.ts +7 -0
- package/src/queue/jobEnvelopeHasPrefix.ts +6 -0
- package/src/scheduler/README.md +50 -9
- package/src/scheduler/ScheduledEvent.ts +20 -5
- package/src/scheduler/ScheduledJob.ts +1 -1
- package/src/scheduler/Scheduler.ts +73 -27
- package/src/scheduler/SchedulerCheckpointRecord.ts +51 -0
- package/src/scheduler/SchedulerWorker.ts +54 -19
- package/src/scheduler/console.ts +3 -1
- package/src/scheduler/contracts/Schedule.ts +22 -0
- package/src/scheduler/contracts/Scheduler.ts +21 -6
- package/src/scheduler/contracts/SchedulerCheckpoint.ts +23 -0
- package/src/scheduler/contracts/SchedulerConsole.ts +5 -0
- package/src/scheduler/contracts/SchedulerWorker.ts +6 -1
- package/src/scheduler/contracts/index.ts +2 -0
- package/src/scheduler/examples/runSchedulerWindow.ts +54 -0
- package/src/scheduler/index.ts +1 -0
- package/src/server/App.ts +73 -10
- package/src/server/README.md +95 -2
- package/src/server/contracts/HttpErrorHandlerOptions.ts +29 -0
- package/src/server/contracts/index.ts +1 -0
- package/src/server/examples/createFirstServer.ts +5 -3
- package/src/server/index.ts +4 -0
- package/src/server/publicServerErrorMessage.ts +17 -0
- package/src/server/registerBrowserJsonFormatting.ts +94 -0
- package/src/server/registerHttpErrorHandler.ts +96 -0
- package/src/ssr/README.md +6 -0
- package/src/ssr/vite/contracts/ViteSsrOptions.ts +2 -0
- package/src/ssr/vite/fastifyViteSsr.ts +4 -3
- package/src/storage/drivers/FlystorageDisk.ts +2 -2
- package/src/storage/index.ts +2 -2
- package/src/websocket/README.md +458 -0
- package/src/websocket/WebSocketChannels.ts +99 -0
- package/src/websocket/WebSocketConnection.ts +162 -0
- package/src/websocket/WebSockets.ts +157 -0
- package/src/websocket/client/WebSocketClient.ts +188 -0
- package/src/websocket/client/index.ts +3 -0
- package/src/websocket/contracts/WebSocketChannel.ts +23 -0
- package/src/websocket/contracts/WebSocketClient.ts +48 -0
- package/src/websocket/contracts/WebSocketEndpoint.ts +46 -0
- package/src/websocket/contracts/WebSocketOptions.ts +50 -0
- package/src/websocket/contracts/WebSocketPresence.ts +6 -0
- package/src/websocket/contracts/WebSocketServiceOptions.ts +12 -0
- package/src/websocket/contracts/index.ts +6 -0
- package/src/websocket/defineChannel.ts +14 -0
- package/src/websocket/defineWebSocket.ts +15 -0
- package/src/websocket/examples/board/LiveBoard.ts +97 -0
- package/src/websocket/examples/board/SummarizeBoardJob.ts +52 -0
- package/src/websocket/examples/board/boardSnapshot.ts +16 -0
- package/src/websocket/examples/board/createBoardClient.ts +43 -0
- package/src/websocket/examples/board/createBoardStore.ts +58 -0
- package/src/websocket/examples/board/registerBoardRoutes.ts +31 -0
- package/src/websocket/examples/createChannelSync.ts +66 -0
- package/src/websocket/examples/deploymentEndpoint.ts +13 -0
- package/src/websocket/examples/deploymentSnapshot.ts +36 -0
- package/src/websocket/examples/resourceChannels.ts +18 -0
- package/src/websocket/examples/userEndpoint.ts +27 -0
- package/src/websocket/fastify.ts +39 -0
- package/src/websocket/index.ts +5 -0
- package/templates/AGENTS.md +33 -2
- /package/dist/flows/{FlowValueValidator.d.ts → flowValueValidator.d.ts} +0 -0
- /package/dist/flows/{FlowValueValidator.js → flowValueValidator.js} +0 -0
- /package/dist/storage/{mime.d.ts → mimeTypeFromPath.d.ts} +0 -0
- /package/dist/storage/{mime.js → mimeTypeFromPath.js} +0 -0
- /package/dist/storage/{contents.d.ts → storageContentsToBuffer.d.ts} +0 -0
- /package/dist/storage/{contents.js → storageContentsToBuffer.js} +0 -0
- /package/src/flows/{FlowValueValidator.ts → flowValueValidator.ts} +0 -0
- /package/src/storage/{mime.ts → mimeTypeFromPath.ts} +0 -0
- /package/src/storage/{contents.ts → storageContentsToBuffer.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
2
|
|
|
3
|
-
import { mimeTypeFromPath } from '../storage/
|
|
3
|
+
import { mimeTypeFromPath } from '../storage/mimeTypeFromPath';
|
|
4
4
|
import { normalizeStoragePath } from '../storage/path';
|
|
5
5
|
import type { Storage, StorageContents, StorageDisk, StorageVisibility } from '../storage';
|
|
6
6
|
import { MEDIA_FILE_VISIBILITY, MEDIA_ITEM_TYPE, MEDIA_LIBRARY_DEFAULT_KEY } from './constants';
|
|
@@ -56,6 +56,7 @@ export class MediaManager {
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Finds or creates the media library for an application-owned scope.
|
|
59
|
+
* Concurrent creators reuse the winning row without overwriting its defaults.
|
|
59
60
|
*
|
|
60
61
|
* @param input - Scope identity and defaults for a new library.
|
|
61
62
|
* @returns Existing or newly-created media library.
|
|
@@ -92,9 +93,15 @@ export class MediaManager {
|
|
|
92
93
|
meta: input.meta ?? null,
|
|
93
94
|
});
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
try {
|
|
97
|
+
await library.save();
|
|
98
|
+
return library;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (!isUniqueConstraintError(error) && !isRetriableTransactionConflict(error)) throw error;
|
|
101
|
+
const raced = await MediaLibrary.where({ scopeType, scopeId, key }).first();
|
|
102
|
+
if (!raced) throw error;
|
|
103
|
+
return raced;
|
|
104
|
+
}
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
/**
|
|
@@ -223,7 +230,7 @@ export class MediaManager {
|
|
|
223
230
|
}
|
|
224
231
|
|
|
225
232
|
/**
|
|
226
|
-
* Finds or creates the root browser item for a library.
|
|
233
|
+
* Finds or creates the root browser item for a library, including concurrent writers.
|
|
227
234
|
*
|
|
228
235
|
* @param libraryOrId - Media library row or id.
|
|
229
236
|
* @returns Root browser item for the library.
|
|
@@ -248,13 +255,20 @@ export class MediaManager {
|
|
|
248
255
|
meta: null,
|
|
249
256
|
});
|
|
250
257
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
258
|
+
try {
|
|
259
|
+
await root.save();
|
|
260
|
+
return root;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (!isUniqueConstraintError(error) && !isRetriableTransactionConflict(error)) throw error;
|
|
263
|
+
const raced = await MediaItem.where({ library, path: '/', type: MEDIA_ITEM_TYPE.root }).first();
|
|
264
|
+
if (!raced) throw error;
|
|
265
|
+
return raced;
|
|
266
|
+
}
|
|
254
267
|
}
|
|
255
268
|
|
|
256
269
|
/**
|
|
257
|
-
* Finds or creates a slash-prefixed browser folder path.
|
|
270
|
+
* Finds or creates a slash-prefixed browser folder path. Concurrent creators
|
|
271
|
+
* reuse the winning directory without replacing its metadata.
|
|
258
272
|
*
|
|
259
273
|
* @param input - Library and folder path to ensure.
|
|
260
274
|
* @returns Folder item for the requested path, or the root for `/`.
|
|
@@ -300,7 +314,14 @@ export class MediaManager {
|
|
|
300
314
|
path: currentPath,
|
|
301
315
|
meta: currentPath === path ? input.meta ?? null : null,
|
|
302
316
|
});
|
|
303
|
-
|
|
317
|
+
try {
|
|
318
|
+
await folder.save();
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (!isUniqueConstraintError(error) && !isRetriableTransactionConflict(error)) throw error;
|
|
321
|
+
const raced = await MediaItem.where({ library, path: currentPath, type: MEDIA_ITEM_TYPE.directory }).first();
|
|
322
|
+
if (!raced) throw error;
|
|
323
|
+
folder = raced;
|
|
324
|
+
}
|
|
304
325
|
}
|
|
305
326
|
|
|
306
327
|
parent = folder;
|
package/src/media/README.md
CHANGED
|
@@ -51,6 +51,10 @@ This lets one app start with one default library per scope, while still allowing
|
|
|
51
51
|
future libraries such as `generated-images`, `reference-material`, or
|
|
52
52
|
`brand-assets` when they need separate configuration or workflows.
|
|
53
53
|
|
|
54
|
+
Concurrent first uploads reuse the same scoped library, root and directory
|
|
55
|
+
rows when another writer wins creation. Existing defaults and folder metadata
|
|
56
|
+
are preserved. Other database errors still propagate to the caller.
|
|
57
|
+
|
|
54
58
|
## Managed Files Without Browser Items
|
|
55
59
|
|
|
56
60
|
Use `storeFile()` when a file should be managed, loaded by ULID, and scoped to a
|
|
@@ -2,7 +2,7 @@ import { mkdtemp, rm } from 'node:fs/promises';
|
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { AuthProvider, AuthToken, PasswordResetToken, UserIdentity } from '@db3.ai/app/auth';
|
|
5
|
+
import { AuthProvider, PasswordLoginAttempt, AuthToken, PasswordResetToken, UserIdentity } from '@db3.ai/app/auth';
|
|
6
6
|
import { createGeneratedTestDatabase } from '@db3.ai/app/db/test/db';
|
|
7
7
|
import { MediaFile, MediaItem, MediaLibrary } from '@db3.ai/app/media';
|
|
8
8
|
import { App } from '@db3.ai/app/server';
|
|
@@ -16,7 +16,7 @@ export async function runPrivateFiles() {
|
|
|
16
16
|
const application = new App({ db: database.db, config: { auth: { providers: { password: true } } }, storage: { disks: { local: { driver: 'local', root } } } });
|
|
17
17
|
const server = createPrivateFileServer(application);
|
|
18
18
|
try {
|
|
19
|
-
await application.db.install(UserIdentity, AuthProvider, AuthToken, PasswordResetToken, MediaLibrary, MediaFile, MediaItem);
|
|
19
|
+
await application.db.install(UserIdentity, AuthProvider, PasswordLoginAttempt, AuthToken, PasswordResetToken, MediaLibrary, MediaFile, MediaItem);
|
|
20
20
|
const ada = await application.auth.registerWithPassword({ name: 'Ada', email: 'ada@example.test', password: 'example-password-123' });
|
|
21
21
|
const grace = await application.auth.registerWithPassword({ name: 'Grace', email: 'grace@example.test', password: 'example-password-456' });
|
|
22
22
|
const headers = { authorization: `Bearer ${ada.token}`, 'content-type': 'text/plain' };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised when an outbound request targets a URL the network guard refuses.
|
|
3
|
+
*
|
|
4
|
+
* The message is safe to show to the user who supplied the URL: it names the
|
|
5
|
+
* rule that failed without revealing resolved internal addresses. Callers
|
|
6
|
+
* typically catch it to turn a blocked crawl, webhook or preview into a
|
|
7
|
+
* validation error instead of a server failure.
|
|
8
|
+
*/
|
|
9
|
+
export class BlockedUrlError extends Error {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a blocked-URL error with a public explanation.
|
|
12
|
+
*
|
|
13
|
+
* @param message - Caller-safe reason the URL was refused.
|
|
14
|
+
*/
|
|
15
|
+
constructor(message = 'This URL is not allowed.') {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'BlockedUrlError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Network
|
|
2
|
+
|
|
3
|
+
The network service protects outbound requests whose destination comes from a
|
|
4
|
+
user, a crawled page or a redirect. It stops those requests from reaching the
|
|
5
|
+
server's own network: loopback, private ranges, link-local cloud metadata
|
|
6
|
+
endpoints, and other reserved addresses.
|
|
7
|
+
|
|
8
|
+
Use it whenever an application fetches a URL it did not choose itself, such as
|
|
9
|
+
webhook deliveries, link previews, crawlers, feed imports or screenshot tools.
|
|
10
|
+
Requests to fixed, trusted provider APIs do not need it.
|
|
11
|
+
|
|
12
|
+
## Fetch a user-supplied URL
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { BlockedUrlError, guardedFetch } from '@db3.ai/app/network';
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const { response, finalUrl } = await guardedFetch(input.url, {
|
|
19
|
+
init: { headers: { accept: 'text/html' }, signal: AbortSignal.timeout(10_000) },
|
|
20
|
+
});
|
|
21
|
+
// Use response and finalUrl as with fetch.
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (error instanceof BlockedUrlError) {
|
|
24
|
+
// error.message is safe to show to the user who supplied the URL.
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`guardedFetch` returns a normal `Response`. It:
|
|
31
|
+
|
|
32
|
+
- validates the URL and every redirect target before requesting it;
|
|
33
|
+
- checks the address DNS returns **when the socket connects**, so a hostname
|
|
34
|
+
cannot pass validation and then resolve to a private address (DNS rebinding);
|
|
35
|
+
- follows redirects like `fetch`: `303`, and `301`/`302` after `POST`, switch
|
|
36
|
+
to `GET` without a body, and `Authorization`/`Cookie` headers are removed
|
|
37
|
+
when a redirect leaves the original origin;
|
|
38
|
+
- stops after `maxRedirects` (default 4), or returns the first redirect
|
|
39
|
+
unfollowed with `redirect: 'manual'`.
|
|
40
|
+
|
|
41
|
+
It sends requests with the runtime's global `fetch`, so tests that replace
|
|
42
|
+
`globalThis.fetch` keep working.
|
|
43
|
+
|
|
44
|
+
## Local development
|
|
45
|
+
|
|
46
|
+
Pass `allowPrivate: true` to reach private hosts, for example a webhook
|
|
47
|
+
receiver on `http://localhost:8100`. TLS certificate verification remains enabled
|
|
48
|
+
for every HTTPS destination. Configure a trusted local CA for development HTTPS;
|
|
49
|
+
allowing a private address does not authorize an unverified server certificate.
|
|
50
|
+
Decide address policy in application configuration (for example only when
|
|
51
|
+
`NODE_ENV=development`) and never from request input.
|
|
52
|
+
|
|
53
|
+
## Other HTTP clients
|
|
54
|
+
|
|
55
|
+
- `assertPublicUrl(url, options)` performs the pre-flight check and throws
|
|
56
|
+
`BlockedUrlError`. Call it for the first URL and for every redirect target.
|
|
57
|
+
- `publicAddressLookup` has the signature of `dns.lookup`. Pass it as the
|
|
58
|
+
`lookup` socket option (Node `http`/`https`, undici `connect.lookup`, got
|
|
59
|
+
`dnsLookup`) so the connected address is checked. Node does not call `lookup`
|
|
60
|
+
for IP-literal hosts, which is why redirect targets also need
|
|
61
|
+
`assertPublicUrl`.
|
|
62
|
+
- `isPrivateIpAddress`, `isPrivateHostname` and `normalizedUrlHostname` expose
|
|
63
|
+
the address policy for custom checks.
|
|
64
|
+
|
|
65
|
+
## Headless browsers
|
|
66
|
+
|
|
67
|
+
Browsers make their own requests for redirects, frames, scripts and images.
|
|
68
|
+
The optional `@db3.ai/app/network/playwright` entry point requires Playwright
|
|
69
|
+
and Chromium. It gives each context an authenticated loopback proxy that checks
|
|
70
|
+
every HTTP destination and HTTPS CONNECT tunnel, including connect-time DNS.
|
|
71
|
+
Chromium's implicit loopback bypass is disabled. Callers cannot override the proxy.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { chromium } from 'playwright';
|
|
75
|
+
import { newGuardedBrowserContext } from '@db3.ai/app/network/playwright';
|
|
76
|
+
|
|
77
|
+
const browser = await chromium.launch({
|
|
78
|
+
args: ['--force-webrtc-ip-handling-policy', '--webrtc-ip-handling-policy=disable_non_proxied_udp'],
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
const context = await newGuardedBrowserContext(browser, {}, {
|
|
82
|
+
blockedResourceTypes: ['font', 'media'],
|
|
83
|
+
onBlocked: ({ url }) => console.warn('Blocked', url),
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
const page = await context.newPage();
|
|
87
|
+
await page.goto(input.url);
|
|
88
|
+
const screenshot = await page.screenshot();
|
|
89
|
+
} finally {
|
|
90
|
+
await context.close();
|
|
91
|
+
}
|
|
92
|
+
} finally {
|
|
93
|
+
await browser.close();
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Redirects, cookie storage, origins and TLS certificate verification stay native to
|
|
98
|
+
Chromium. The proxy streams HTTP bodies without collecting them in memory and
|
|
99
|
+
handles interrupted streams as ordinary network failures. `requestTimeoutMs`
|
|
100
|
+
sets socket inactivity timeout (30 seconds by default). Closing a context closes
|
|
101
|
+
its listener and all in-flight proxy sockets. Setup failures also clean up.
|
|
102
|
+
|
|
103
|
+
Service workers and WebSockets are disabled. WebRTC is not HTTP traffic, which is
|
|
104
|
+
why the Chromium launch flags above remain necessary for untrusted pages.
|
|
105
|
+
`allowPrivate` permits local destinations but does not disable Chromium TLS
|
|
106
|
+
verification; explicit `ignoreHTTPSErrors` is a separate browser option. Run these
|
|
107
|
+
workers behind a network egress policy as an additional infrastructure boundary.
|
|
108
|
+
|
|
109
|
+
## Address policy
|
|
110
|
+
|
|
111
|
+
Blocked destinations include `localhost` names, `0.0.0.0/8`, `10.0.0.0/8`,
|
|
112
|
+
`100.64.0.0/10`, `127.0.0.0/8`, `169.254.0.0/16`, `172.16.0.0/12`,
|
|
113
|
+
`192.0.0.0/24`, `192.0.2.0/24`, `192.88.99.0/24`, `192.168.0.0/16`,
|
|
114
|
+
`198.18.0.0/15`, `198.51.100.0/24`, `203.0.113.0/24`, `224.0.0.0` and above,
|
|
115
|
+
IPv6 loopback, unique-local, link-local, site-local, multicast, documentation,
|
|
116
|
+
Teredo and discard ranges, and IPv4-compatible addresses. IPv4-mapped, NAT64
|
|
117
|
+
(`64:ff9b::/96`) and 6to4 (`2002::/16`) addresses are judged by the IPv4 address
|
|
118
|
+
they embed.
|
|
119
|
+
|
|
120
|
+
## Run the example
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npx tsx node_modules/@db3.ai/app/src/network/examples/runGuardedFetch.ts
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Expect `privateBlockedByDefault` and `privateAllowedWhenRequested` to be true.
|
|
127
|
+
|
|
128
|
+
## Dependencies
|
|
129
|
+
|
|
130
|
+
Uses `undici` 7 for the connection dispatcher, which is compatible with the
|
|
131
|
+
global `fetch` bundled with supported Node versions. The Playwright entry point
|
|
132
|
+
depends on the optional `playwright` peer dependency. The service has no
|
|
133
|
+
framework service dependencies.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { lookup } from 'node:dns/promises';
|
|
2
|
+
import { isIP } from 'node:net';
|
|
3
|
+
import { BlockedUrlError } from './BlockedUrlError';
|
|
4
|
+
import { isPrivateHostname, isPrivateIpAddress, normalizedUrlHostname } from './privateNetworkAddresses';
|
|
5
|
+
import type * as network from './contracts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validates that a URL is an HTTP(S) address on the public internet.
|
|
9
|
+
*
|
|
10
|
+
* This is a pre-flight check that gives users a clear error before any
|
|
11
|
+
* connection is attempted. It is not sufficient on its own: DNS can return a
|
|
12
|
+
* different address when the request connects (DNS rebinding). Send requests
|
|
13
|
+
* through {@link guardedFetch}, or pass {@link publicAddressLookup} to the HTTP
|
|
14
|
+
* client, so the connected address is checked as well.
|
|
15
|
+
*
|
|
16
|
+
* @param rawUrl - Absolute URL supplied by a user, crawler or redirect.
|
|
17
|
+
* @param options - Destination policy; private hosts are refused by default.
|
|
18
|
+
* @throws {BlockedUrlError} When the URL is malformed, not HTTP(S), or reaches
|
|
19
|
+
* a private, local or reserved address.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* await assertPublicUrl('https://example.com/feed.xml');
|
|
23
|
+
* await assertPublicUrl('http://169.254.169.254/'); // throws BlockedUrlError
|
|
24
|
+
*/
|
|
25
|
+
export async function assertPublicUrl(rawUrl: string, options: network.PublicUrlOptions = {}): Promise<void> {
|
|
26
|
+
const hostname = publicUrlHostname(rawUrl, options);
|
|
27
|
+
|
|
28
|
+
if (hostname === null || isIP(hostname)) return;
|
|
29
|
+
|
|
30
|
+
let addresses: Array<{ address: string }>;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
addresses = await lookup(hostname, { all: true, verbatim: true });
|
|
34
|
+
} catch {
|
|
35
|
+
throw new BlockedUrlError('Unable to resolve the URL hostname.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (addresses.length === 0 || addresses.some(entry => isPrivateIpAddress(entry.address))) {
|
|
39
|
+
throw new BlockedUrlError(PRIVATE_DESTINATION_MESSAGE);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Public explanation used for every private, local or reserved destination. */
|
|
44
|
+
export const PRIVATE_DESTINATION_MESSAGE = 'Private, local, or reserved URLs are not allowed.';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Performs the synchronous part of URL validation.
|
|
48
|
+
*
|
|
49
|
+
* @param rawUrl - Absolute URL to validate.
|
|
50
|
+
* @param options - Destination policy.
|
|
51
|
+
* @returns Normalized hostname still requiring DNS validation, or null when no
|
|
52
|
+
* further checks are needed because private destinations are allowed.
|
|
53
|
+
* @throws {BlockedUrlError} When the URL fails a synchronous rule.
|
|
54
|
+
*/
|
|
55
|
+
function publicUrlHostname(rawUrl: string, options: network.PublicUrlOptions): string | null {
|
|
56
|
+
let parsed: URL;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
parsed = new URL(rawUrl);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new BlockedUrlError('The URL is not valid.');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
65
|
+
throw new BlockedUrlError('Only http and https URLs are allowed.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options.allowPrivate) return null;
|
|
69
|
+
|
|
70
|
+
const hostname = normalizedUrlHostname(parsed.hostname);
|
|
71
|
+
|
|
72
|
+
if (isPrivateHostname(hostname) || (isIP(hostname) && isPrivateIpAddress(hostname))) {
|
|
73
|
+
throw new BlockedUrlError(PRIVATE_DESTINATION_MESSAGE);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return hostname;
|
|
77
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { PublicUrlOptions } from './PublicUrlOptions';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for one guarded outbound HTTP request.
|
|
5
|
+
*
|
|
6
|
+
* `init` is passed to `fetch` unchanged except that redirects are always
|
|
7
|
+
* handled by the guard and the connection dispatcher is always replaced with
|
|
8
|
+
* one that re-validates the addresses DNS returns at connect time.
|
|
9
|
+
*/
|
|
10
|
+
export interface GuardedFetchOptions extends PublicUrlOptions {
|
|
11
|
+
/** Standard fetch options such as method, headers, body and abort signal. */
|
|
12
|
+
init?: RequestInit;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Maximum redirects followed before the request fails with `BlockedUrlError`.
|
|
16
|
+
*
|
|
17
|
+
* Every redirect target is validated as a new destination. Defaults to 4.
|
|
18
|
+
*/
|
|
19
|
+
maxRedirects?: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* How redirects are handled.
|
|
23
|
+
*
|
|
24
|
+
* `follow` (default) validates and follows each hop. `manual` returns the
|
|
25
|
+
* first 3xx response unchanged so a caller such as a browser can follow it
|
|
26
|
+
* through its own guarded request path.
|
|
27
|
+
*/
|
|
28
|
+
redirect?: 'follow' | 'manual';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Response returned by a guarded outbound request.
|
|
33
|
+
*/
|
|
34
|
+
export interface GuardedFetchResult {
|
|
35
|
+
/** Final response after any followed redirects. */
|
|
36
|
+
response: Response;
|
|
37
|
+
|
|
38
|
+
/** Absolute URL that produced `response`, after followed redirects. */
|
|
39
|
+
finalUrl: string;
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destination policy shared by every outbound network guard.
|
|
3
|
+
*
|
|
4
|
+
* Applications decide the policy (for example from their environment) and pass
|
|
5
|
+
* it to each guarded operation. The guard itself never reads environment
|
|
6
|
+
* variables, so a request handler cannot widen the policy through input.
|
|
7
|
+
*/
|
|
8
|
+
export interface PublicUrlOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Allows private, loopback and reserved destinations.
|
|
11
|
+
*
|
|
12
|
+
* Intended for interactive local development, where integrations are often
|
|
13
|
+
* tested against `localhost` receivers. Production code should leave this
|
|
14
|
+
* unset so user-supplied URLs can only reach public internet hosts.
|
|
15
|
+
*/
|
|
16
|
+
allowPrivate?: boolean;
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { BlockedUrlError, guardedFetch } from '@db3.ai/app/network';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Requests a local receiver with and without private-network access.
|
|
7
|
+
*
|
|
8
|
+
* A user-supplied URL pointing at the machine itself is refused by default,
|
|
9
|
+
* and reachable only when the caller explicitly allows private destinations,
|
|
10
|
+
* as a local development workflow would.
|
|
11
|
+
*
|
|
12
|
+
* @returns Whether the default policy blocked the request and the opted-in request succeeded.
|
|
13
|
+
*/
|
|
14
|
+
export async function runGuardedFetch() {
|
|
15
|
+
const server = createServer((_request, response) => response.end('local receiver'));
|
|
16
|
+
|
|
17
|
+
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
18
|
+
|
|
19
|
+
const address = server.address();
|
|
20
|
+
const url = `http://127.0.0.1:${typeof address === 'object' && address ? address.port : 0}/`;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const blocked = await guardedFetch(url).then(() => false, error => error instanceof BlockedUrlError);
|
|
24
|
+
const { response } = await guardedFetch(url, { allowPrivate: true });
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
privateBlockedByDefault: blocked,
|
|
28
|
+
privateAllowedWhenRequested: await response.text() === 'local receiver',
|
|
29
|
+
};
|
|
30
|
+
} finally {
|
|
31
|
+
await new Promise<void>(resolve => server.close(() => resolve()));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) console.log(JSON.stringify(await runGuardedFetch(), null, 2));
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Agent, type Dispatcher } from 'undici';
|
|
2
|
+
import { BlockedUrlError } from './BlockedUrlError';
|
|
3
|
+
import { assertPublicUrl } from './assertPublicUrl';
|
|
4
|
+
import { publicAddressLookup } from './publicAddressLookup';
|
|
5
|
+
import type * as network from './contracts';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_MAX_REDIRECTS = 4;
|
|
8
|
+
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
9
|
+
const CROSS_ORIGIN_SENSITIVE_HEADERS = ['authorization', 'cookie', 'proxy-authorization'];
|
|
10
|
+
|
|
11
|
+
let publicDispatcher: Dispatcher | undefined;
|
|
12
|
+
let privateDispatcher: Dispatcher | undefined;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetches a user-supplied URL without allowing it to reach private networks.
|
|
16
|
+
*
|
|
17
|
+
* Each hop is validated before it is requested, and the connection itself uses
|
|
18
|
+
* {@link publicAddressLookup}, so the address DNS returns at connect time is
|
|
19
|
+
* the address that was checked. Redirects are followed manually: every target
|
|
20
|
+
* is validated, `303` (and `301`/`302` after `POST`) switch to `GET` without a
|
|
21
|
+
* body as `fetch` does, and credentials (`Authorization`, `Cookie`) are removed
|
|
22
|
+
* when a redirect leaves the original origin.
|
|
23
|
+
*
|
|
24
|
+
* `allowPrivate` permits private destinations without weakening TLS certificate
|
|
25
|
+
* verification. Local HTTPS receivers must present a certificate trusted by Node.
|
|
26
|
+
*
|
|
27
|
+
* The request is sent with the runtime's global `fetch`, so test doubles that
|
|
28
|
+
* replace `globalThis.fetch` continue to work.
|
|
29
|
+
*
|
|
30
|
+
* @param rawUrl - Absolute HTTP(S) URL to request.
|
|
31
|
+
* @param options - Fetch init, redirect handling and destination policy.
|
|
32
|
+
* @returns Final response and the URL that produced it.
|
|
33
|
+
* @throws {BlockedUrlError} When any hop targets a refused destination or the
|
|
34
|
+
* redirect limit is exceeded.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const { response, finalUrl } = await guardedFetch('https://example.com/', {
|
|
38
|
+
* init: { headers: { accept: 'text/html' }, signal: AbortSignal.timeout(10_000) },
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
export async function guardedFetch(rawUrl: string, options: network.GuardedFetchOptions = {}): Promise<network.GuardedFetchResult> {
|
|
42
|
+
const maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
|
|
43
|
+
let current = rawUrl;
|
|
44
|
+
let init: RequestInit = { ...options.init };
|
|
45
|
+
|
|
46
|
+
for (let hop = 0; ; hop++) {
|
|
47
|
+
await assertPublicUrl(current, options);
|
|
48
|
+
|
|
49
|
+
const response = await fetchHop(current, init, options.allowPrivate === true);
|
|
50
|
+
const location = response.headers.get('location');
|
|
51
|
+
|
|
52
|
+
if (!REDIRECT_STATUSES.has(response.status) || !location || options.redirect === 'manual') {
|
|
53
|
+
return { response, finalUrl: current };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await response.body?.cancel();
|
|
57
|
+
|
|
58
|
+
if (hop >= maxRedirects) throw new BlockedUrlError('Too many redirects.');
|
|
59
|
+
|
|
60
|
+
const next = new URL(location, current).toString();
|
|
61
|
+
|
|
62
|
+
init = redirectInit(init, response.status, new URL(current).origin !== new URL(next).origin);
|
|
63
|
+
current = next;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Sends one request hop through the dispatcher matching the destination policy.
|
|
69
|
+
*
|
|
70
|
+
* @param url - Already validated hop URL.
|
|
71
|
+
* @param init - Fetch options for this hop.
|
|
72
|
+
* @param allowPrivate - Whether private destinations are allowed.
|
|
73
|
+
* @returns Unfollowed hop response.
|
|
74
|
+
* @throws {BlockedUrlError} When the connect-time lookup refuses the address.
|
|
75
|
+
*/
|
|
76
|
+
async function fetchHop(url: string, init: RequestInit, allowPrivate: boolean): Promise<Response> {
|
|
77
|
+
try {
|
|
78
|
+
// The caller checks each URL/redirect; this dispatcher validates the actual
|
|
79
|
+
// socket address, including DNS changes between validation and connection.
|
|
80
|
+
return await fetch(url, {
|
|
81
|
+
...init,
|
|
82
|
+
redirect: 'manual',
|
|
83
|
+
dispatcher: hopDispatcher(allowPrivate),
|
|
84
|
+
} as RequestInit);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw blockedCause(error) ?? error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Chooses the connection dispatcher for one hop.
|
|
92
|
+
*
|
|
93
|
+
* @param allowPrivate - Whether private destinations are allowed.
|
|
94
|
+
* @returns Shared dispatcher for the hop's policy.
|
|
95
|
+
*/
|
|
96
|
+
function hopDispatcher(allowPrivate: boolean): Dispatcher {
|
|
97
|
+
if (!allowPrivate) {
|
|
98
|
+
return publicDispatcher ??= new Agent({ connect: { lookup: publicAddressLookup, rejectUnauthorized: true } });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return privateDispatcher ??= new Agent({ connect: { rejectUnauthorized: true } });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Builds the fetch options for the next redirect hop.
|
|
106
|
+
*
|
|
107
|
+
* @param init - Options used for the previous hop.
|
|
108
|
+
* @param status - Redirect status code.
|
|
109
|
+
* @param crossOrigin - Whether the redirect leaves the previous origin.
|
|
110
|
+
* @returns Options for the next hop.
|
|
111
|
+
*/
|
|
112
|
+
function redirectInit(init: RequestInit, status: number, crossOrigin: boolean): RequestInit {
|
|
113
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
114
|
+
const switchesToGet = status === 303
|
|
115
|
+
? method !== 'GET' && method !== 'HEAD'
|
|
116
|
+
: (status === 301 || status === 302) && method === 'POST';
|
|
117
|
+
const headers = new Headers(init.headers);
|
|
118
|
+
|
|
119
|
+
if (switchesToGet) {
|
|
120
|
+
headers.delete('content-type');
|
|
121
|
+
headers.delete('content-length');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (crossOrigin) {
|
|
125
|
+
for (const name of CROSS_ORIGIN_SENSITIVE_HEADERS) headers.delete(name);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
...init,
|
|
130
|
+
headers,
|
|
131
|
+
...(switchesToGet ? { method: 'GET', body: undefined } : {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Finds a guard rejection wrapped inside a fetch network error.
|
|
137
|
+
*
|
|
138
|
+
* @param error - Error thrown by `fetch`.
|
|
139
|
+
* @returns The wrapped `BlockedUrlError`, or null.
|
|
140
|
+
*/
|
|
141
|
+
function blockedCause(error: unknown): BlockedUrlError | null {
|
|
142
|
+
let current: unknown = error;
|
|
143
|
+
|
|
144
|
+
for (let depth = 0; depth < 5 && current; depth++) {
|
|
145
|
+
if (current instanceof BlockedUrlError) return current;
|
|
146
|
+
current = (current as { cause?: unknown }).cause;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return null;
|
|
150
|
+
}
|