@codemation/host 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -0
- package/dist/CodemationConfig-XCkSV2dj.d.ts +168 -0
- package/dist/CodemationConsumerConfigLoader-Dmm2TzAA.d.ts +61 -0
- package/dist/CodemationConsumerConfigLoader-scS_RQMy.js +334 -0
- package/dist/CodemationConsumerConfigLoader-scS_RQMy.js.map +1 -0
- package/dist/CodemationFrontendBootstrapRequest-CE6DjOWJ.js +5768 -0
- package/dist/CodemationFrontendBootstrapRequest-CE6DjOWJ.js.map +1 -0
- package/dist/CodemationPluginListMerger-BNmaoXQL.js +49 -0
- package/dist/CodemationPluginListMerger-BNmaoXQL.js.map +1 -0
- package/dist/CodemationPluginListMerger-BRYqEk0y.d.ts +793 -0
- package/dist/CodemationWhitelabelConfig-DgbjgtrR.d.ts +48 -0
- package/dist/ConsoleLogger-ClPU7jtc.js +35 -0
- package/dist/ConsoleLogger-ClPU7jtc.js.map +1 -0
- package/dist/CredentialServices-BKBGe7l3.js +1030 -0
- package/dist/CredentialServices-BKBGe7l3.js.map +1 -0
- package/dist/CredentialServices-DpDpm8mL.d.ts +291 -0
- package/dist/LogLevelPolicy-4cq9z0TI.d.ts +37 -0
- package/dist/PrismaMigrationDeployer-B1E_gYz7.js +8212 -0
- package/dist/PrismaMigrationDeployer-B1E_gYz7.js.map +1 -0
- package/dist/ServerLoggerFactory-BRHxIDS7.js +340 -0
- package/dist/ServerLoggerFactory-BRHxIDS7.js.map +1 -0
- package/dist/WorkflowViewContracts-DCLpTn25.d.ts +47 -0
- package/dist/chunk-7V6ThxGB.js +39 -0
- package/dist/client-Yh7-CQud.d.ts +21995 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +15 -0
- package/dist/client.js.map +1 -0
- package/dist/consumer.d.ts +5 -0
- package/dist/consumer.js +7 -0
- package/dist/credentials.d.ts +50 -0
- package/dist/credentials.js +11 -0
- package/dist/credentials.js.map +1 -0
- package/dist/decorate-B-N_5S4p.js +10 -0
- package/dist/decorateParam-BTcc3KNk.js +15 -0
- package/dist/devServerSidecar.d.ts +52 -0
- package/dist/devServerSidecar.js +131 -0
- package/dist/devServerSidecar.js.map +1 -0
- package/dist/index-Bs4F1IsC.d.ts +1044 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +14 -0
- package/dist/nextServer.d.ts +89 -0
- package/dist/nextServer.js +127 -0
- package/dist/nextServer.js.map +1 -0
- package/dist/persistenceServer-K5eqlZm3.d.ts +36 -0
- package/dist/persistenceServer-W9uRw0dJ.js +19 -0
- package/dist/persistenceServer-W9uRw0dJ.js.map +1 -0
- package/dist/persistenceServer.d.ts +6 -0
- package/dist/persistenceServer.js +6 -0
- package/dist/server-BBdsATju.d.ts +132 -0
- package/dist/server-BiHSuA13.js +175 -0
- package/dist/server-BiHSuA13.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +13 -0
- package/package.json +152 -0
- package/playwright.config.ts +74 -0
- package/prisma/migrations/20260315063514_init/migration.sql +16 -0
- package/prisma/migrations/20260316090000_workflow_debugger_overlay/migration.sql +9 -0
- package/prisma/migrations/20260317120000_trigger_state_store/migration.sql +3 -0
- package/prisma/migrations/20260317153000_trigger_setup_state/migration.sql +8 -0
- package/prisma/migrations/20260318110000_credentials_v2/migration.sql +49 -0
- package/prisma/migrations/20260319110000_credential_oauth2_material/migration.sql +28 -0
- package/prisma/migrations/20260319200000_codemation_auth_tables/migration.sql +56 -0
- package/prisma/migrations/20260320140000_user_invites_account_status/migration.sql +20 -0
- package/prisma/migrations/20260325120000_workflow_activation/migration.sql +8 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +179 -0
- package/prisma.config.ts +12 -0
- package/scripts/ensure-prisma-runtime-sourcemaps.mjs +42 -0
- package/scripts/integration-database-global-setup.mjs +30 -0
- package/src/application/ApplicationRequestError.ts +12 -0
- package/src/application/auth/AuthenticatedPrincipal.ts +5 -0
- package/src/application/auth/SessionVerifier.ts +5 -0
- package/src/application/binary/OverlayPinnedBinaryUploadService.ts +119 -0
- package/src/application/binary/RunBinaryAttachmentLookupService.ts +139 -0
- package/src/application/binary/RunStateBinaryStorageKeysCollector.ts +57 -0
- package/src/application/bus/Command.ts +3 -0
- package/src/application/bus/CommandBus.ts +5 -0
- package/src/application/bus/CommandHandler.ts +5 -0
- package/src/application/bus/DomainEvent.ts +1 -0
- package/src/application/bus/DomainEventBus.ts +5 -0
- package/src/application/bus/DomainEventHandler.ts +5 -0
- package/src/application/bus/Query.ts +3 -0
- package/src/application/bus/QueryBus.ts +5 -0
- package/src/application/bus/QueryHandler.ts +5 -0
- package/src/application/commands/AcceptUserInviteCommand.ts +10 -0
- package/src/application/commands/AcceptUserInviteCommandHandler.ts +19 -0
- package/src/application/commands/CopyRunToWorkflowDebuggerCommand.ts +14 -0
- package/src/application/commands/CopyRunToWorkflowDebuggerCommandHandler.ts +56 -0
- package/src/application/commands/CreateCredentialInstanceCommand.ts +9 -0
- package/src/application/commands/CreateCredentialInstanceCommandHandler.ts +28 -0
- package/src/application/commands/CredentialCommandHandlers.ts +10 -0
- package/src/application/commands/DeleteCredentialInstanceCommand.ts +7 -0
- package/src/application/commands/DeleteCredentialInstanceCommandHandler.ts +27 -0
- package/src/application/commands/HandleWebhookInvocationCommand.ts +12 -0
- package/src/application/commands/HandleWebhookInvocationCommandHandler.ts +42 -0
- package/src/application/commands/InviteUserCommand.ts +12 -0
- package/src/application/commands/InviteUserCommandHandler.ts +22 -0
- package/src/application/commands/RegenerateUserInviteCommand.ts +12 -0
- package/src/application/commands/RegenerateUserInviteCommandHandler.ts +24 -0
- package/src/application/commands/ReplaceMutableRunWorkflowSnapshotCommand.ts +12 -0
- package/src/application/commands/ReplaceMutableRunWorkflowSnapshotCommandHandler.ts +47 -0
- package/src/application/commands/ReplaceWorkflowDebuggerOverlayCommand.ts +14 -0
- package/src/application/commands/ReplaceWorkflowDebuggerOverlayCommandHandler.ts +35 -0
- package/src/application/commands/ReplayWorkflowNodeCommand.ts +12 -0
- package/src/application/commands/ReplayWorkflowNodeCommandHandler.ts +164 -0
- package/src/application/commands/SetPinnedNodeInputCommand.ts +13 -0
- package/src/application/commands/SetPinnedNodeInputCommandHandler.ts +50 -0
- package/src/application/commands/SetWorkflowActivationCommand.ts +10 -0
- package/src/application/commands/SetWorkflowActivationCommandHandler.ts +39 -0
- package/src/application/commands/StartWorkflowRunCommand.ts +8 -0
- package/src/application/commands/StartWorkflowRunCommandHandler.ts +286 -0
- package/src/application/commands/TestCredentialInstanceCommand.ts +9 -0
- package/src/application/commands/TestCredentialInstanceCommandHandler.ts +28 -0
- package/src/application/commands/UpdateCredentialInstanceCommand.ts +12 -0
- package/src/application/commands/UpdateCredentialInstanceCommandHandler.ts +28 -0
- package/src/application/commands/UpdateUserAccountStatusCommand.ts +12 -0
- package/src/application/commands/UpdateUserAccountStatusCommandHandler.ts +24 -0
- package/src/application/commands/UploadOverlayPinnedBinaryCommand.ts +16 -0
- package/src/application/commands/UploadOverlayPinnedBinaryCommandHandler.ts +31 -0
- package/src/application/commands/UpsertCredentialBindingCommand.ts +11 -0
- package/src/application/commands/UpsertCredentialBindingCommandHandler.ts +28 -0
- package/src/application/commands/UpsertLocalBootstrapUserCommand.ts +12 -0
- package/src/application/commands/UpsertLocalBootstrapUserCommandHandler.ts +24 -0
- package/src/application/commands/UserAccountCommandHandlers.ts +10 -0
- package/src/application/contracts/CredentialContractsRegistry.ts +88 -0
- package/src/application/contracts/RunContracts.ts +41 -0
- package/src/application/contracts/WorkflowDebuggerContracts.ts +12 -0
- package/src/application/contracts/WorkflowViewContracts.ts +40 -0
- package/src/application/contracts/WorkflowWebsocketMessage.ts +8 -0
- package/src/application/contracts/userDirectoryContracts.types.ts +60 -0
- package/src/application/dev/BootRuntimeSnapshotHolder.ts +19 -0
- package/src/application/dev/BootRuntimeSummary.types.ts +11 -0
- package/src/application/dev/DevBootstrapSummaryAssembler.ts +84 -0
- package/src/application/dev/DevBootstrapSummaryJson.types.ts +9 -0
- package/src/application/logging/LogFilter.ts +7 -0
- package/src/application/logging/Logger.ts +10 -0
- package/src/application/mapping/DataMapper.ts +3 -0
- package/src/application/mapping/WorkflowDefinitionMapper.ts +171 -0
- package/src/application/mapping/WorkflowPolicyUiPresentationFactory.ts +39 -0
- package/src/application/queries/CredentialQueryHandlers.ts +12 -0
- package/src/application/queries/GetCredentialFieldEnvStatusQuery.ts +5 -0
- package/src/application/queries/GetCredentialFieldEnvStatusQueryHandler.ts +52 -0
- package/src/application/queries/GetCredentialInstanceQuery.ts +9 -0
- package/src/application/queries/GetCredentialInstanceQueryHandler.ts +27 -0
- package/src/application/queries/GetCredentialInstanceWithSecretsQuery.ts +9 -0
- package/src/application/queries/GetCredentialInstanceWithSecretsQueryHandler.ts +27 -0
- package/src/application/queries/GetRunBinaryAttachmentQuery.ts +11 -0
- package/src/application/queries/GetRunBinaryAttachmentQueryHandler.ts +23 -0
- package/src/application/queries/GetRunStateQuery.ts +8 -0
- package/src/application/queries/GetRunStateQueryHandler.ts +21 -0
- package/src/application/queries/GetWorkflowCredentialHealthQuery.ts +9 -0
- package/src/application/queries/GetWorkflowCredentialHealthQueryHandler.ts +27 -0
- package/src/application/queries/GetWorkflowDebuggerOverlayQuery.ts +8 -0
- package/src/application/queries/GetWorkflowDebuggerOverlayQueryHandler.ts +28 -0
- package/src/application/queries/GetWorkflowDetailQuery.ts +8 -0
- package/src/application/queries/GetWorkflowDetailQueryHandler.ts +24 -0
- package/src/application/queries/GetWorkflowOverlayBinaryAttachmentQuery.ts +11 -0
- package/src/application/queries/GetWorkflowOverlayBinaryAttachmentQueryHandler.ts +23 -0
- package/src/application/queries/GetWorkflowSummariesQuery.ts +4 -0
- package/src/application/queries/GetWorkflowSummariesQueryHandler.ts +23 -0
- package/src/application/queries/ListCredentialInstancesQuery.ts +5 -0
- package/src/application/queries/ListCredentialInstancesQueryHandler.ts +27 -0
- package/src/application/queries/ListCredentialTypesQuery.ts +5 -0
- package/src/application/queries/ListCredentialTypesQueryHandler.ts +28 -0
- package/src/application/queries/ListUserAccountsQuery.ts +5 -0
- package/src/application/queries/ListUserAccountsQueryHandler.ts +22 -0
- package/src/application/queries/ListWorkflowRunsQuery.ts +8 -0
- package/src/application/queries/ListWorkflowRunsQueryHandler.ts +21 -0
- package/src/application/queries/UserAccountQueryHandlers.ts +4 -0
- package/src/application/queries/VerifyUserInviteQuery.ts +9 -0
- package/src/application/queries/VerifyUserInviteQueryHandler.ts +21 -0
- package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +98 -0
- package/src/application/websocket/WorkflowRunEventWebsocketRelay.ts +36 -0
- package/src/application/websocket/WorkflowWebsocketPublisher.ts +5 -0
- package/src/application/workflows/WebhookEndpointPathValidator.ts +35 -0
- package/src/application/workflows/WorkflowDebuggerOverlayStateFactory.ts +122 -0
- package/src/applicationTokens.ts +72 -0
- package/src/bootstrap/CodemationBootstrapRequest.ts +27 -0
- package/src/bootstrap/CodemationContainerFactory.ts +310 -0
- package/src/bootstrap/CodemationContainerRegistration.ts +23 -0
- package/src/bootstrap/CodemationContainerRegistrationRegistrar.ts +42 -0
- package/src/bootstrap/CodemationFrontendBootstrapRequest.ts +16 -0
- package/src/bootstrap/CodemationWorkerBootstrapRequest.ts +19 -0
- package/src/bootstrap/PreparedCodemationRuntime.ts +37 -0
- package/src/bootstrap/PreparedCodemationRuntimeFactory.ts +308 -0
- package/src/bootstrap/boot/CliRuntimeBootService.ts +27 -0
- package/src/bootstrap/boot/FrontendRuntimeBootService.ts +86 -0
- package/src/bootstrap/boot/WorkerRuntimeBootService.ts +64 -0
- package/src/bootstrap/runtime/AppConfigFactory.ts +57 -0
- package/src/bootstrap/runtime/ResolvedImplementationSelectionFactory.ts +118 -0
- package/src/client.ts +3 -0
- package/src/codemationApplication.ts +311 -0
- package/src/consumer.ts +4 -0
- package/src/credentials.ts +24 -0
- package/src/devServerSidecar.ts +10 -0
- package/src/domain/credentials/CredentialBindingService.ts +139 -0
- package/src/domain/credentials/CredentialFieldEnvOverlayService.ts +60 -0
- package/src/domain/credentials/CredentialInstanceService.ts +391 -0
- package/src/domain/credentials/CredentialMaterialResolver.ts +55 -0
- package/src/domain/credentials/CredentialRuntimeMaterialService.ts +39 -0
- package/src/domain/credentials/CredentialSecretCipher.ts +70 -0
- package/src/domain/credentials/CredentialServices.ts +145 -0
- package/src/domain/credentials/CredentialSessionServiceImpl.ts +119 -0
- package/src/domain/credentials/CredentialTestService.ts +73 -0
- package/src/domain/credentials/CredentialTypeRegistryImpl.ts +29 -0
- package/src/domain/credentials/OAuth2ConnectServiceFactory.ts +396 -0
- package/src/domain/credentials/OAuth2ProviderRegistry.ts +75 -0
- package/src/domain/credentials/WorkflowCredentialNodeResolver.ts +246 -0
- package/src/domain/runs/WorkflowRunRepository.ts +11 -0
- package/src/domain/users/UserAccountServiceRegistry.ts +315 -0
- package/src/domain/users/userLoginMethodLabels.types.ts +29 -0
- package/src/domain/workflows/WorkflowActivationPreflight.ts +32 -0
- package/src/domain/workflows/WorkflowActivationPreflightRules.ts +77 -0
- package/src/domain/workflows/WorkflowActivationRepository.ts +9 -0
- package/src/domain/workflows/WorkflowDebuggerOverlayRepository.ts +7 -0
- package/src/domain/workflows/WorkflowDebuggerOverlayState.ts +8 -0
- package/src/domain/workflows/WorkflowDefinitionRepository.ts +11 -0
- package/src/index.ts +58 -0
- package/src/infrastructure/auth/AuthJsSessionVerifier.ts +26 -0
- package/src/infrastructure/auth/DevelopmentSessionBypassVerifier.ts +12 -0
- package/src/infrastructure/binary/BinaryBodyNodeReadableFactory.ts +22 -0
- package/src/infrastructure/binary/CountingSha256Transform.ts +26 -0
- package/src/infrastructure/binary/LocalFilesystemBinaryStorageRegistry.ts +86 -0
- package/src/infrastructure/config/CodemationPluginRegistrar.ts +44 -0
- package/src/infrastructure/credentials/FrameworkBuiltinCredentialTypesRegistrar.ts +21 -0
- package/src/infrastructure/credentials/OpenAiApiKeyCredentialHealthTester.ts +89 -0
- package/src/infrastructure/credentials/OpenAiApiKeyCredentialShapes.types.ts +15 -0
- package/src/infrastructure/credentials/OpenAiApiKeyCredentialTypeFactory.ts +47 -0
- package/src/infrastructure/di/HandlesCommandRegistry.ts +24 -0
- package/src/infrastructure/di/HandlesDomainEventRegistry.ts +24 -0
- package/src/infrastructure/di/HandlesQueryRegistry.ts +24 -0
- package/src/infrastructure/di/InMemoryCommandBus.ts +47 -0
- package/src/infrastructure/di/InMemoryDomainEventBus.ts +47 -0
- package/src/infrastructure/di/InMemoryQueryBus.ts +45 -0
- package/src/infrastructure/ids/CodemationIdFactory.ts +12 -0
- package/src/infrastructure/logging/BrowserLogger.ts +1 -0
- package/src/infrastructure/logging/BrowserLoggerFactory.ts +14 -0
- package/src/infrastructure/logging/ConsoleLogger.ts +41 -0
- package/src/infrastructure/logging/FilteringLogger.ts +38 -0
- package/src/infrastructure/logging/LogLevelPolicy.ts +148 -0
- package/src/infrastructure/logging/LogLevelPolicyFactory.ts +16 -0
- package/src/infrastructure/logging/PerformanceLogPolicy.ts +10 -0
- package/src/infrastructure/logging/PerformanceLogPolicyFactory.ts +14 -0
- package/src/infrastructure/logging/ServerLogger.ts +1 -0
- package/src/infrastructure/logging/ServerLoggerFactory.ts +28 -0
- package/src/infrastructure/persistence/CodemationPostgresPrismaClientFactory.ts +9 -0
- package/src/infrastructure/persistence/CredentialPersistenceStore.ts +139 -0
- package/src/infrastructure/persistence/DatabasePersistenceResolver.ts +91 -0
- package/src/infrastructure/persistence/InMemoryTriggerSetupStateRepository.ts +23 -0
- package/src/infrastructure/persistence/InMemoryWorkflowActivationRepository.ts +18 -0
- package/src/infrastructure/persistence/InMemoryWorkflowDebuggerOverlayRepository.ts +16 -0
- package/src/infrastructure/persistence/InMemoryWorkflowRunRepository.ts +94 -0
- package/src/infrastructure/persistence/PrismaClientFactory.ts +26 -0
- package/src/infrastructure/persistence/PrismaCredentialStore.ts +368 -0
- package/src/infrastructure/persistence/PrismaMigrationDeployer.ts +184 -0
- package/src/infrastructure/persistence/PrismaTriggerSetupStateRepository.ts +68 -0
- package/src/infrastructure/persistence/PrismaWorkflowActivationRepository.ts +36 -0
- package/src/infrastructure/persistence/PrismaWorkflowDebuggerOverlayRepository.ts +65 -0
- package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +243 -0
- package/src/infrastructure/persistence/RuntimeWorkflowActivationPolicy.ts +27 -0
- package/src/infrastructure/persistence/SchedulerPersistenceCompatibilityValidator.ts +20 -0
- package/src/infrastructure/persistence/WorkflowDefinitionRepositoryAdapter.ts +31 -0
- package/src/infrastructure/persistence/WorkflowRunRepository.ts +46 -0
- package/src/infrastructure/persistence/generated/prisma/client.d.ts +1 -0
- package/src/infrastructure/persistence/generated/prisma/default.d.ts +1 -0
- package/src/infrastructure/persistence/generated/prisma/edge.d.ts +1 -0
- package/src/infrastructure/persistence/generated/prisma/index.d.ts +4766 -0
- package/src/infrastructure/persistence/generated/prisma/package.json +144 -0
- package/src/infrastructure/persistence/generated/prisma/query_compiler_fast_bg.wasm +0 -0
- package/src/infrastructure/persistence/generated/prisma/runtime/client.d.ts +3358 -0
- package/src/infrastructure/persistence/generated/prisma/runtime/index-browser.d.ts +90 -0
- package/src/infrastructure/persistence/generated/prisma/schema.prisma +35 -0
- package/src/infrastructure/persistence/generated/prisma/wasm-edge-light-loader.mjs +5 -0
- package/src/infrastructure/persistence/generated/prisma/wasm-worker-loader.mjs +5 -0
- package/src/infrastructure/persistence/generated/prisma-client/client.d.ts +1 -0
- package/src/infrastructure/persistence/generated/prisma-client/client.js +5 -0
- package/src/infrastructure/persistence/generated/prisma-client/default.d.ts +1 -0
- package/src/infrastructure/persistence/generated/prisma-client/default.js +5 -0
- package/src/infrastructure/persistence/generated/prisma-client/edge.d.ts +1 -0
- package/src/infrastructure/persistence/generated/prisma-client/edge.js +299 -0
- package/src/infrastructure/persistence/generated/prisma-client/index-browser.js +325 -0
- package/src/infrastructure/persistence/generated/prisma-client/index.d.ts +21623 -0
- package/src/infrastructure/persistence/generated/prisma-client/index.js +299 -0
- package/src/infrastructure/persistence/generated/prisma-client/package.json +144 -0
- package/src/infrastructure/persistence/generated/prisma-client/query_compiler_fast_bg.js +2 -0
- package/src/infrastructure/persistence/generated/prisma-client/query_compiler_fast_bg.wasm +0 -0
- package/src/infrastructure/persistence/generated/prisma-client/query_compiler_fast_bg.wasm-base64.js +2 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/client.d.ts +3358 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/client.js +86 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/client.js.map +1 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/index-browser.d.ts +90 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/index-browser.js +6 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/index-browser.js.map +1 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/wasm-compiler-edge.js +76 -0
- package/src/infrastructure/persistence/generated/prisma-client/runtime/wasm-compiler-edge.js.map +1 -0
- package/src/infrastructure/persistence/generated/prisma-client/schema.prisma +179 -0
- package/src/infrastructure/persistence/generated/prisma-client/wasm-edge-light-loader.mjs +5 -0
- package/src/infrastructure/persistence/generated/prisma-client/wasm-worker-loader.mjs +5 -0
- package/src/infrastructure/runtime/LiveWorkflowRepository.ts +14 -0
- package/src/infrastructure/runtime/WorkerRuntimeScheduler.ts +35 -0
- package/src/infrastructure/server/http/ServerHttpRouteParams.ts +1 -0
- package/src/infrastructure/webhooks/RequestToWebhookItemMapper.ts +128 -0
- package/src/nextServer.ts +31 -0
- package/src/persistenceServer.ts +5 -0
- package/src/presentation/config/AppConfig.ts +25 -0
- package/src/presentation/config/CodemationAppContext.ts +19 -0
- package/src/presentation/config/CodemationApplicationFacade.ts +5 -0
- package/src/presentation/config/CodemationAuthConfig.ts +31 -0
- package/src/presentation/config/CodemationClassToken.ts +3 -0
- package/src/presentation/config/CodemationConfig.ts +86 -0
- package/src/presentation/config/CodemationConfigNormalizer.ts +179 -0
- package/src/presentation/config/CodemationLogConfig.ts +22 -0
- package/src/presentation/config/CodemationPackageManifest.ts +9 -0
- package/src/presentation/config/CodemationPlugin.ts +20 -0
- package/src/presentation/config/CodemationPluginListMerger.ts +46 -0
- package/src/presentation/config/CodemationWhitelabelConfig.ts +9 -0
- package/src/presentation/config/CodemationWorkflowDiscovery.ts +3 -0
- package/src/presentation/http/ApiPaths.ts +165 -0
- package/src/presentation/http/CodemationServerGatewayFactory.ts +120 -0
- package/src/presentation/http/HttpRequestJsonBodyReader.ts +12 -0
- package/src/presentation/http/ServerHttpErrorResponseFactory.ts +33 -0
- package/src/presentation/http/ServerHttpRouteParams.ts +1 -0
- package/src/presentation/http/hono/CodemationHonoApiAppFactory.ts +64 -0
- package/src/presentation/http/hono/HonoApiRouteRegistrar.ts +5 -0
- package/src/presentation/http/hono/HonoHttpAnonymousRoutePolicyRegistry.ts +27 -0
- package/src/presentation/http/hono/registrars/BinaryHonoApiRouteRegistrar.ts +21 -0
- package/src/presentation/http/hono/registrars/CredentialHonoApiRouteRegistrar.ts +34 -0
- package/src/presentation/http/hono/registrars/DevHonoApiRouteRegistrar.ts +17 -0
- package/src/presentation/http/hono/registrars/OAuth2HonoApiRouteRegistrar.ts +18 -0
- package/src/presentation/http/hono/registrars/RunHonoApiRouteRegistrar.ts +31 -0
- package/src/presentation/http/hono/registrars/UserHonoApiRouteRegistrar.ts +24 -0
- package/src/presentation/http/hono/registrars/WebhookHonoApiRouteRegistrar.ts +23 -0
- package/src/presentation/http/hono/registrars/WhitelabelHonoApiRouteRegistrar.ts +18 -0
- package/src/presentation/http/hono/registrars/WorkflowHonoApiRouteRegistrar.ts +33 -0
- package/src/presentation/http/routeHandlers/BinaryHttpRouteHandlerFactory.ts +101 -0
- package/src/presentation/http/routeHandlers/CredentialHttpRouteHandler.ts +129 -0
- package/src/presentation/http/routeHandlers/DevBootstrapSummaryHttpRouteHandler.ts +21 -0
- package/src/presentation/http/routeHandlers/OAuth2HttpRouteHandlerFactory.ts +129 -0
- package/src/presentation/http/routeHandlers/RunHttpRouteHandler.ts +82 -0
- package/src/presentation/http/routeHandlers/UserHttpRouteHandlerFactory.ts +109 -0
- package/src/presentation/http/routeHandlers/WebhookHttpRouteHandler.ts +42 -0
- package/src/presentation/http/routeHandlers/WhitelabelLogoHttpRouteHandler.ts +96 -0
- package/src/presentation/http/routeHandlers/WorkflowHttpRouteHandler.ts +104 -0
- package/src/presentation/server/CodemationConsumerAppResolver.ts +82 -0
- package/src/presentation/server/CodemationConsumerConfigExportsResolver.ts +33 -0
- package/src/presentation/server/CodemationConsumerConfigLoader.ts +270 -0
- package/src/presentation/server/CodemationPluginDiscovery.ts +151 -0
- package/src/presentation/server/CodemationTsyringeParamInfoReader.ts +26 -0
- package/src/presentation/server/CodemationTsyringeTypeInfoRegistrar.ts +121 -0
- package/src/presentation/server/DevelopmentRuntimeRouteGuard.ts +59 -0
- package/src/presentation/server/DiscoveredWorkflowsEmptyMessageFactory.ts +11 -0
- package/src/presentation/server/WorkflowDefinitionExportsResolver.ts +24 -0
- package/src/presentation/server/WorkflowDiscoveryPathSegmentsComputer.ts +53 -0
- package/src/presentation/server/WorkflowModulePathFinder.ts +47 -0
- package/src/presentation/websocket/WorkflowWebsocketServer.ts +169 -0
- package/src/server.ts +14 -0
- package/tsconfig.json +10 -0
- package/vitest.shared.ts +45 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import type { CredentialOAuth2AuthDefinition } from "@codemation/core";
|
|
2
|
+
import { inject, injectable } from "@codemation/core";
|
|
3
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
import { ApplicationRequestError } from "../../application/ApplicationRequestError";
|
|
5
|
+
import { ApplicationTokens } from "../../applicationTokens";
|
|
6
|
+
import type { AppConfig } from "../../presentation/config/AppConfig";
|
|
7
|
+
import type { CredentialStore } from "./CredentialServices";
|
|
8
|
+
import {
|
|
9
|
+
CredentialFieldEnvOverlayService,
|
|
10
|
+
CredentialInstanceService,
|
|
11
|
+
CredentialMaterialResolver,
|
|
12
|
+
CredentialRuntimeMaterialService,
|
|
13
|
+
CredentialSecretCipher,
|
|
14
|
+
CredentialTypeRegistryImpl,
|
|
15
|
+
} from "./CredentialServices";
|
|
16
|
+
import { OAuth2ProviderRegistry } from "./OAuth2ProviderRegistry";
|
|
17
|
+
|
|
18
|
+
type JsonRecord = Readonly<Record<string, unknown>>;
|
|
19
|
+
|
|
20
|
+
export type OAuth2AuthRedirectResult = Readonly<{
|
|
21
|
+
instanceId: string;
|
|
22
|
+
redirectUri: string;
|
|
23
|
+
redirectUrl: string;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
export type OAuth2CallbackResult = Readonly<{
|
|
27
|
+
instanceId: string;
|
|
28
|
+
connectedEmail?: string;
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
@injectable()
|
|
32
|
+
export class OAuth2ConnectService {
|
|
33
|
+
private static readonly stateTtlMs = 10 * 60 * 1_000;
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
@inject(ApplicationTokens.CredentialStore)
|
|
37
|
+
private readonly credentialStore: CredentialStore,
|
|
38
|
+
@inject(CredentialInstanceService)
|
|
39
|
+
private readonly credentialInstanceService: CredentialInstanceService,
|
|
40
|
+
@inject(CredentialTypeRegistryImpl)
|
|
41
|
+
private readonly credentialTypeRegistry: CredentialTypeRegistryImpl,
|
|
42
|
+
@inject(CredentialRuntimeMaterialService)
|
|
43
|
+
private readonly credentialRuntimeMaterialService: CredentialRuntimeMaterialService,
|
|
44
|
+
@inject(CredentialFieldEnvOverlayService)
|
|
45
|
+
private readonly credentialFieldEnvOverlayService: CredentialFieldEnvOverlayService,
|
|
46
|
+
@inject(CredentialMaterialResolver)
|
|
47
|
+
private readonly credentialMaterialResolver: CredentialMaterialResolver,
|
|
48
|
+
@inject(CredentialSecretCipher)
|
|
49
|
+
private readonly credentialSecretCipher: CredentialSecretCipher,
|
|
50
|
+
@inject(OAuth2ProviderRegistry)
|
|
51
|
+
private readonly oauth2ProviderRegistry: OAuth2ProviderRegistry,
|
|
52
|
+
@inject(ApplicationTokens.AppConfig)
|
|
53
|
+
private readonly appConfig: AppConfig,
|
|
54
|
+
) {}
|
|
55
|
+
|
|
56
|
+
async createAuthRedirect(instanceId: string, requestOrigin: string): Promise<OAuth2AuthRedirectResult> {
|
|
57
|
+
const instance = await this.credentialInstanceService.requireInstance(instanceId);
|
|
58
|
+
const credentialType = this.requireOAuth2Type(instance.typeId);
|
|
59
|
+
const emptyMaterial = await this.credentialMaterialResolver.resolveMaterial(instance);
|
|
60
|
+
const { resolvedPublicConfig } = this.credentialFieldEnvOverlayService.apply({
|
|
61
|
+
definition: credentialType.definition,
|
|
62
|
+
publicConfig: instance.publicConfig,
|
|
63
|
+
material: emptyMaterial,
|
|
64
|
+
});
|
|
65
|
+
const provider = this.oauth2ProviderRegistry.resolve(credentialType.definition, resolvedPublicConfig);
|
|
66
|
+
const redirectUri = this.getRedirectUri(requestOrigin);
|
|
67
|
+
const state = this.createOpaqueValue();
|
|
68
|
+
const codeVerifier = this.createOpaqueValue();
|
|
69
|
+
const codeChallenge = this.createPkceCodeChallenge(codeVerifier);
|
|
70
|
+
const createdAt = new Date();
|
|
71
|
+
const expiresAt = new Date(createdAt.getTime() + OAuth2ConnectService.stateTtlMs);
|
|
72
|
+
await this.credentialStore.createOAuth2State({
|
|
73
|
+
state,
|
|
74
|
+
instanceId,
|
|
75
|
+
codeVerifier,
|
|
76
|
+
providerId: provider.providerId,
|
|
77
|
+
requestedScopes: credentialType.definition.auth!.scopes,
|
|
78
|
+
createdAt: createdAt.toISOString(),
|
|
79
|
+
expiresAt: expiresAt.toISOString(),
|
|
80
|
+
});
|
|
81
|
+
const authorizeUrl = new URL(provider.authorizeUrl);
|
|
82
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
83
|
+
authorizeUrl.searchParams.set(
|
|
84
|
+
"client_id",
|
|
85
|
+
this.oauth2ProviderRegistry.resolveClientId(credentialType.definition.auth!, resolvedPublicConfig),
|
|
86
|
+
);
|
|
87
|
+
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
|
|
88
|
+
authorizeUrl.searchParams.set("scope", credentialType.definition.auth!.scopes.join(" "));
|
|
89
|
+
authorizeUrl.searchParams.set("state", state);
|
|
90
|
+
authorizeUrl.searchParams.set("code_challenge", codeChallenge);
|
|
91
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
92
|
+
if (provider.providerId === "google") {
|
|
93
|
+
authorizeUrl.searchParams.set("access_type", "offline");
|
|
94
|
+
authorizeUrl.searchParams.set("prompt", "consent");
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
instanceId,
|
|
98
|
+
redirectUri,
|
|
99
|
+
redirectUrl: authorizeUrl.toString(),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async handleCallback(
|
|
104
|
+
args: Readonly<{
|
|
105
|
+
code?: string | null;
|
|
106
|
+
state?: string | null;
|
|
107
|
+
requestOrigin: string;
|
|
108
|
+
}>,
|
|
109
|
+
): Promise<OAuth2CallbackResult> {
|
|
110
|
+
const code = args.code?.trim();
|
|
111
|
+
const state = args.state?.trim();
|
|
112
|
+
if (!code || !state) {
|
|
113
|
+
throw new ApplicationRequestError(400, "OAuth2 callback requires both code and state.");
|
|
114
|
+
}
|
|
115
|
+
const storedState = await this.credentialStore.consumeOAuth2State(state);
|
|
116
|
+
if (!storedState) {
|
|
117
|
+
throw new ApplicationRequestError(400, "OAuth2 state is invalid or has already been used.");
|
|
118
|
+
}
|
|
119
|
+
if (new Date(storedState.expiresAt).getTime() <= Date.now()) {
|
|
120
|
+
throw new ApplicationRequestError(400, "OAuth2 state has expired. Start the connection flow again.");
|
|
121
|
+
}
|
|
122
|
+
const instance = await this.credentialInstanceService.requireInstance(storedState.instanceId);
|
|
123
|
+
const credentialType = this.requireOAuth2Type(instance.typeId);
|
|
124
|
+
const auth = credentialType.definition.auth!;
|
|
125
|
+
const composedMaterial = await this.credentialRuntimeMaterialService.compose(instance);
|
|
126
|
+
const { resolvedPublicConfig, resolvedMaterial } = this.credentialFieldEnvOverlayService.apply({
|
|
127
|
+
definition: credentialType.definition,
|
|
128
|
+
publicConfig: instance.publicConfig,
|
|
129
|
+
material: composedMaterial,
|
|
130
|
+
});
|
|
131
|
+
const provider = this.oauth2ProviderRegistry.resolve(credentialType.definition, resolvedPublicConfig);
|
|
132
|
+
const redirectUri = this.getRedirectUri(args.requestOrigin);
|
|
133
|
+
const tokenResponse = await this.exchangeAuthorizationCode({
|
|
134
|
+
auth,
|
|
135
|
+
code,
|
|
136
|
+
codeVerifier: storedState.codeVerifier,
|
|
137
|
+
provider,
|
|
138
|
+
publicConfig: resolvedPublicConfig,
|
|
139
|
+
redirectUri,
|
|
140
|
+
secretMaterial: resolvedMaterial,
|
|
141
|
+
});
|
|
142
|
+
const nowIso = new Date().toISOString();
|
|
143
|
+
const existingMaterial = await this.credentialStore.getOAuth2Material(instance.instanceId);
|
|
144
|
+
const mergedTokenMaterial = this.mergeTokenMaterial(
|
|
145
|
+
existingMaterial ? this.credentialSecretCipher.decrypt(existingMaterial) : undefined,
|
|
146
|
+
tokenResponse,
|
|
147
|
+
nowIso,
|
|
148
|
+
storedState.requestedScopes,
|
|
149
|
+
);
|
|
150
|
+
const encryptedMaterial = this.credentialSecretCipher.encrypt(mergedTokenMaterial);
|
|
151
|
+
const connectedEmail = await this.resolveConnectedEmail(provider.userInfoUrl, tokenResponse.access_token);
|
|
152
|
+
await this.credentialStore.saveOAuth2Material({
|
|
153
|
+
instanceId: instance.instanceId,
|
|
154
|
+
encryptedJson: encryptedMaterial.encryptedJson,
|
|
155
|
+
encryptionKeyId: encryptedMaterial.encryptionKeyId,
|
|
156
|
+
schemaVersion: encryptedMaterial.schemaVersion,
|
|
157
|
+
metadata: {
|
|
158
|
+
providerId: provider.providerId,
|
|
159
|
+
connectedEmail,
|
|
160
|
+
connectedAt: nowIso,
|
|
161
|
+
scopes: this.resolveGrantedScopes(tokenResponse.scope, storedState.requestedScopes),
|
|
162
|
+
updatedAt: nowIso,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
await this.credentialInstanceService.markOAuth2Connected(instance.instanceId, nowIso);
|
|
166
|
+
return {
|
|
167
|
+
instanceId: instance.instanceId,
|
|
168
|
+
connectedEmail,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getRedirectUri(requestOrigin: string): string {
|
|
173
|
+
const rawBase = this.appConfig.env.CODEMATION_PUBLIC_BASE_URL?.trim() || requestOrigin.trim();
|
|
174
|
+
if (!rawBase) {
|
|
175
|
+
throw new Error("Unable to resolve the public base URL for OAuth2 redirect URI generation.");
|
|
176
|
+
}
|
|
177
|
+
const baseUrl = this.ensureAbsoluteUrlForOAuth2Base(rawBase);
|
|
178
|
+
try {
|
|
179
|
+
return new URL("/api/oauth2/callback", this.normalizeBaseUrl(baseUrl)).toString();
|
|
180
|
+
} catch {
|
|
181
|
+
throw new ApplicationRequestError(
|
|
182
|
+
500,
|
|
183
|
+
`Invalid public base URL for OAuth2 redirect URI generation: "${rawBase}". Use a full URL (e.g. http://localhost:3000) for CODEMATION_PUBLIC_BASE_URL or ensure the request has a valid Host / forwarded headers.`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* `new URL(path, base)` requires `base` to be an absolute URL with a scheme.
|
|
190
|
+
* Misconfigured CODEMATION_PUBLIC_BASE_URL (e.g. `localhost:3000` without http://) or odd
|
|
191
|
+
* forwarded headers otherwise throw TypeError: Invalid URL.
|
|
192
|
+
*
|
|
193
|
+
* Comma-separated values (proxy chains or copy-paste mistakes like `http,http`) use the
|
|
194
|
+
* first segment only; obviously invalid hostnames are rejected.
|
|
195
|
+
*/
|
|
196
|
+
private ensureAbsoluteUrlForOAuth2Base(raw: string): string {
|
|
197
|
+
const segments = raw
|
|
198
|
+
.split(",")
|
|
199
|
+
.map((s) => s.trim())
|
|
200
|
+
.filter((s) => s.length > 0);
|
|
201
|
+
let candidate = segments[0] ?? raw.trim();
|
|
202
|
+
if (!candidate) {
|
|
203
|
+
throw new Error("Unable to resolve the public base URL for OAuth2 redirect URI generation.");
|
|
204
|
+
}
|
|
205
|
+
if (!/^https?:\/\//i.test(candidate)) {
|
|
206
|
+
candidate = `http://${candidate}`;
|
|
207
|
+
}
|
|
208
|
+
let parsed: URL;
|
|
209
|
+
try {
|
|
210
|
+
parsed = new URL(candidate);
|
|
211
|
+
} catch {
|
|
212
|
+
throw new ApplicationRequestError(
|
|
213
|
+
500,
|
|
214
|
+
`Invalid public base URL for OAuth2 redirect URI generation: "${raw}". Use a single full URL (e.g. http://localhost:3000) for CODEMATION_PUBLIC_BASE_URL.`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (parsed.hostname === "http" || parsed.hostname === "https") {
|
|
218
|
+
throw new ApplicationRequestError(
|
|
219
|
+
500,
|
|
220
|
+
`Invalid OAuth2 public base URL (hostname "${parsed.hostname}"). Set CODEMATION_PUBLIC_BASE_URL to one full URL with a real host, e.g. http://localhost:3000 — not "http,http" or other typos.`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return candidate;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private requireOAuth2Type(typeId: string) {
|
|
227
|
+
const credentialType = this.credentialTypeRegistry.getCredentialType(typeId);
|
|
228
|
+
if (!credentialType) {
|
|
229
|
+
throw new ApplicationRequestError(400, `Unknown credential type: ${typeId}`);
|
|
230
|
+
}
|
|
231
|
+
if (credentialType.definition.auth?.kind !== "oauth2") {
|
|
232
|
+
throw new ApplicationRequestError(400, `Credential type ${typeId} is not configured for OAuth2.`);
|
|
233
|
+
}
|
|
234
|
+
return credentialType;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private async exchangeAuthorizationCode(
|
|
238
|
+
args: Readonly<{
|
|
239
|
+
auth: CredentialOAuth2AuthDefinition;
|
|
240
|
+
code: string;
|
|
241
|
+
codeVerifier?: string;
|
|
242
|
+
provider: Readonly<{
|
|
243
|
+
authorizeUrl: string;
|
|
244
|
+
tokenUrl: string;
|
|
245
|
+
userInfoUrl?: string;
|
|
246
|
+
providerId: string;
|
|
247
|
+
}>;
|
|
248
|
+
publicConfig: JsonRecord;
|
|
249
|
+
redirectUri: string;
|
|
250
|
+
secretMaterial: JsonRecord;
|
|
251
|
+
}>,
|
|
252
|
+
): Promise<Readonly<Record<string, unknown>>> {
|
|
253
|
+
const requestBody = new URLSearchParams();
|
|
254
|
+
requestBody.set("grant_type", "authorization_code");
|
|
255
|
+
requestBody.set("code", args.code);
|
|
256
|
+
requestBody.set("redirect_uri", args.redirectUri);
|
|
257
|
+
requestBody.set("client_id", this.oauth2ProviderRegistry.resolveClientId(args.auth, args.publicConfig));
|
|
258
|
+
const clientSecretFieldKey = this.oauth2ProviderRegistry.resolveClientSecretFieldKey(args.auth);
|
|
259
|
+
const clientSecret = String(args.secretMaterial[clientSecretFieldKey] ?? "");
|
|
260
|
+
if (!clientSecret) {
|
|
261
|
+
throw new ApplicationRequestError(
|
|
262
|
+
400,
|
|
263
|
+
`OAuth2 client secret is missing from secret field "${clientSecretFieldKey}".`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
requestBody.set("client_secret", clientSecret);
|
|
267
|
+
if (args.codeVerifier) {
|
|
268
|
+
requestBody.set("code_verifier", args.codeVerifier);
|
|
269
|
+
}
|
|
270
|
+
const response = await fetch(args.provider.tokenUrl, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: {
|
|
273
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
274
|
+
},
|
|
275
|
+
body: requestBody.toString(),
|
|
276
|
+
});
|
|
277
|
+
const responseText = await response.text();
|
|
278
|
+
const responseBody = this.parseJsonRecord(responseText);
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
throw new ApplicationRequestError(400, this.createTokenExchangeErrorMessage(responseBody, responseText));
|
|
281
|
+
}
|
|
282
|
+
return responseBody;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private mergeTokenMaterial(
|
|
286
|
+
existingMaterial: JsonRecord | undefined,
|
|
287
|
+
tokenResponse: Readonly<Record<string, unknown>>,
|
|
288
|
+
nowIso: string,
|
|
289
|
+
requestedScopes: ReadonlyArray<string>,
|
|
290
|
+
): JsonRecord {
|
|
291
|
+
const accessToken = tokenResponse.access_token;
|
|
292
|
+
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
293
|
+
throw new ApplicationRequestError(400, "OAuth2 token exchange did not return an access_token.");
|
|
294
|
+
}
|
|
295
|
+
const nextRefreshToken = tokenResponse.refresh_token ?? existingMaterial?.refresh_token;
|
|
296
|
+
const nextScope = tokenResponse.scope ?? requestedScopes.join(" ");
|
|
297
|
+
const expiry = this.resolveExpiry(tokenResponse, nowIso);
|
|
298
|
+
return Object.freeze({
|
|
299
|
+
access_token: accessToken,
|
|
300
|
+
refresh_token: nextRefreshToken,
|
|
301
|
+
token_type: tokenResponse.token_type,
|
|
302
|
+
scope: nextScope,
|
|
303
|
+
expiry,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private resolveExpiry(tokenResponse: Readonly<Record<string, unknown>>, nowIso: string): string | undefined {
|
|
308
|
+
const expiresInSeconds = Number(tokenResponse.expires_in);
|
|
309
|
+
if (Number.isFinite(expiresInSeconds) && expiresInSeconds > 0) {
|
|
310
|
+
return new Date(new Date(nowIso).getTime() + expiresInSeconds * 1000).toISOString();
|
|
311
|
+
}
|
|
312
|
+
const explicitExpiry = tokenResponse.expiry;
|
|
313
|
+
return typeof explicitExpiry === "string" && explicitExpiry.length > 0 ? explicitExpiry : undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private resolveGrantedScopes(
|
|
317
|
+
grantedScopeValue: unknown,
|
|
318
|
+
requestedScopes: ReadonlyArray<string>,
|
|
319
|
+
): ReadonlyArray<string> {
|
|
320
|
+
if (typeof grantedScopeValue !== "string" || grantedScopeValue.trim().length === 0) {
|
|
321
|
+
return [...requestedScopes];
|
|
322
|
+
}
|
|
323
|
+
return grantedScopeValue
|
|
324
|
+
.split(/\s+/)
|
|
325
|
+
.map((entry) => entry.trim())
|
|
326
|
+
.filter((entry) => entry.length > 0);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private async resolveConnectedEmail(
|
|
330
|
+
userInfoUrl: string | undefined,
|
|
331
|
+
accessToken: unknown,
|
|
332
|
+
): Promise<string | undefined> {
|
|
333
|
+
if (!userInfoUrl || typeof accessToken !== "string" || accessToken.length === 0) {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
const response = await fetch(userInfoUrl, {
|
|
337
|
+
headers: {
|
|
338
|
+
authorization: `Bearer ${accessToken}`,
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
if (!response.ok) {
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
const responseBody = this.parseJsonRecord(await response.text());
|
|
345
|
+
return this.findEmail(responseBody);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private findEmail(value: JsonRecord): string | undefined {
|
|
349
|
+
const email = value.email;
|
|
350
|
+
if (typeof email === "string" && email.length > 0) {
|
|
351
|
+
return email;
|
|
352
|
+
}
|
|
353
|
+
const nestedUser = value.user;
|
|
354
|
+
if (nestedUser && typeof nestedUser === "object" && "email" in nestedUser) {
|
|
355
|
+
const nestedEmail = (nestedUser as Readonly<Record<string, unknown>>).email;
|
|
356
|
+
return typeof nestedEmail === "string" && nestedEmail.length > 0 ? nestedEmail : undefined;
|
|
357
|
+
}
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private parseJsonRecord(value: string): JsonRecord {
|
|
362
|
+
if (!value) {
|
|
363
|
+
return {};
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const parsed = JSON.parse(value) as unknown;
|
|
367
|
+
return parsed && typeof parsed === "object" ? (parsed as JsonRecord) : {};
|
|
368
|
+
} catch {
|
|
369
|
+
return {};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private createTokenExchangeErrorMessage(responseBody: JsonRecord, rawText: string): string {
|
|
374
|
+
const description = responseBody.error_description;
|
|
375
|
+
if (typeof description === "string" && description.length > 0) {
|
|
376
|
+
return description;
|
|
377
|
+
}
|
|
378
|
+
const error = responseBody.error;
|
|
379
|
+
if (typeof error === "string" && error.length > 0) {
|
|
380
|
+
return error;
|
|
381
|
+
}
|
|
382
|
+
return rawText || "OAuth2 token exchange failed.";
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private createOpaqueValue(): string {
|
|
386
|
+
return randomBytes(32).toString("base64url");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private createPkceCodeChallenge(codeVerifier: string): string {
|
|
390
|
+
return createHash("sha256").update(codeVerifier).digest("base64url");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private normalizeBaseUrl(baseUrl: string): string {
|
|
394
|
+
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { CredentialOAuth2AuthDefinition, CredentialTypeDefinition } from "@codemation/core";
|
|
2
|
+
import { injectable } from "@codemation/core";
|
|
3
|
+
|
|
4
|
+
type JsonRecord = Readonly<Record<string, unknown>>;
|
|
5
|
+
|
|
6
|
+
export type OAuth2ResolvedProvider = Readonly<{
|
|
7
|
+
providerId: string;
|
|
8
|
+
authorizeUrl: string;
|
|
9
|
+
tokenUrl: string;
|
|
10
|
+
userInfoUrl?: string;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
@injectable()
|
|
14
|
+
export class OAuth2ProviderRegistry {
|
|
15
|
+
private static readonly googleProvider = Object.freeze({
|
|
16
|
+
providerId: "google",
|
|
17
|
+
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
18
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
19
|
+
userInfoUrl: "https://openidconnect.googleapis.com/v1/userinfo",
|
|
20
|
+
} satisfies OAuth2ResolvedProvider);
|
|
21
|
+
|
|
22
|
+
resolve(definition: CredentialTypeDefinition, publicConfig: JsonRecord): OAuth2ResolvedProvider {
|
|
23
|
+
const auth = definition.auth;
|
|
24
|
+
if (auth?.kind !== "oauth2") {
|
|
25
|
+
throw new Error(`Credential type ${definition.typeId} does not use OAuth2.`);
|
|
26
|
+
}
|
|
27
|
+
if ("providerId" in auth) {
|
|
28
|
+
return this.resolveBuiltInProvider(auth);
|
|
29
|
+
}
|
|
30
|
+
return this.resolvePublicConfigProvider(auth, publicConfig, definition.typeId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
resolveClientId(auth: CredentialOAuth2AuthDefinition, publicConfig: JsonRecord): string {
|
|
34
|
+
const clientIdFieldKey = auth.clientIdFieldKey ?? "clientId";
|
|
35
|
+
const clientId = String(publicConfig[clientIdFieldKey] ?? "");
|
|
36
|
+
if (!clientId) {
|
|
37
|
+
throw new Error(`OAuth2 client id is missing from public field "${clientIdFieldKey}".`);
|
|
38
|
+
}
|
|
39
|
+
return clientId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
resolveClientSecretFieldKey(auth: CredentialOAuth2AuthDefinition): string {
|
|
43
|
+
return auth.clientSecretFieldKey ?? "clientSecret";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private resolveBuiltInProvider(
|
|
47
|
+
auth: Extract<CredentialOAuth2AuthDefinition, { providerId: string }>,
|
|
48
|
+
): OAuth2ResolvedProvider {
|
|
49
|
+
if (auth.providerId === "google") {
|
|
50
|
+
return OAuth2ProviderRegistry.googleProvider;
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Unsupported OAuth2 provider id: ${auth.providerId}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private resolvePublicConfigProvider(
|
|
56
|
+
auth: Extract<CredentialOAuth2AuthDefinition, { providerFromPublicConfig: object }>,
|
|
57
|
+
publicConfig: JsonRecord,
|
|
58
|
+
typeId: string,
|
|
59
|
+
): OAuth2ResolvedProvider {
|
|
60
|
+
const authorizeUrl = String(publicConfig[auth.providerFromPublicConfig.authorizeUrlFieldKey] ?? "");
|
|
61
|
+
const tokenUrl = String(publicConfig[auth.providerFromPublicConfig.tokenUrlFieldKey] ?? "");
|
|
62
|
+
const userInfoUrl = auth.providerFromPublicConfig.userInfoUrlFieldKey
|
|
63
|
+
? String(publicConfig[auth.providerFromPublicConfig.userInfoUrlFieldKey] ?? "")
|
|
64
|
+
: "";
|
|
65
|
+
if (!authorizeUrl || !tokenUrl) {
|
|
66
|
+
throw new Error(`OAuth2 provider URLs are incomplete for credential type ${typeId}.`);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
providerId: "custom",
|
|
70
|
+
authorizeUrl,
|
|
71
|
+
tokenUrl,
|
|
72
|
+
userInfoUrl: userInfoUrl || undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|