@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,270 @@
|
|
|
1
|
+
import type { WorkflowDefinition } from "@codemation/core";
|
|
2
|
+
import { access, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import type { NamespacedUnregister } from "tsx/esm/api";
|
|
6
|
+
import { register } from "tsx/esm/api";
|
|
7
|
+
import type { CodemationConfig } from "../config/CodemationConfig";
|
|
8
|
+
import { CodemationConfigNormalizer } from "../config/CodemationConfigNormalizer";
|
|
9
|
+
import { logLevelPolicyFactory } from "../../infrastructure/logging/LogLevelPolicyFactory";
|
|
10
|
+
import { ServerLoggerFactory } from "../../infrastructure/logging/ServerLoggerFactory";
|
|
11
|
+
import { DiscoveredWorkflowsEmptyMessageFactory } from "./DiscoveredWorkflowsEmptyMessageFactory";
|
|
12
|
+
import { CodemationConsumerConfigExportsResolver } from "./CodemationConsumerConfigExportsResolver";
|
|
13
|
+
import { WorkflowDefinitionExportsResolver } from "./WorkflowDefinitionExportsResolver";
|
|
14
|
+
import { WorkflowDiscoveryPathSegmentsComputer } from "./WorkflowDiscoveryPathSegmentsComputer";
|
|
15
|
+
import { WorkflowModulePathFinder } from "./WorkflowModulePathFinder";
|
|
16
|
+
|
|
17
|
+
export type CodemationConsumerConfigResolution = Readonly<{
|
|
18
|
+
config: CodemationConfig;
|
|
19
|
+
bootstrapSource: string | null;
|
|
20
|
+
workflowSources: ReadonlyArray<string>;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
export class CodemationConsumerConfigLoader {
|
|
24
|
+
private static readonly importerRegistrationsByTsconfig = new Map<string, NamespacedUnregister>();
|
|
25
|
+
private readonly configExportsResolver = new CodemationConsumerConfigExportsResolver();
|
|
26
|
+
private readonly configNormalizer = new CodemationConfigNormalizer();
|
|
27
|
+
private readonly workflowModulePathFinder = new WorkflowModulePathFinder();
|
|
28
|
+
private readonly workflowDefinitionExportsResolver = new WorkflowDefinitionExportsResolver();
|
|
29
|
+
private readonly discoveredWorkflowsEmptyMessageFactory = new DiscoveredWorkflowsEmptyMessageFactory();
|
|
30
|
+
private readonly pathSegmentsComputer = new WorkflowDiscoveryPathSegmentsComputer();
|
|
31
|
+
private readonly performanceDiagnosticsLogger = new ServerLoggerFactory(
|
|
32
|
+
logLevelPolicyFactory,
|
|
33
|
+
).createPerformanceDiagnostics("codemation-config-loader.timing");
|
|
34
|
+
|
|
35
|
+
async load(
|
|
36
|
+
args: Readonly<{ consumerRoot: string; configPathOverride?: string }>,
|
|
37
|
+
): Promise<CodemationConsumerConfigResolution> {
|
|
38
|
+
const loadStarted = performance.now();
|
|
39
|
+
let mark = loadStarted;
|
|
40
|
+
const phaseMs = (label: string): void => {
|
|
41
|
+
const now = performance.now();
|
|
42
|
+
const delta = now - mark;
|
|
43
|
+
mark = now;
|
|
44
|
+
this.performanceDiagnosticsLogger.info(
|
|
45
|
+
`load.${label} +${delta.toFixed(1)}ms (cumulative ${(now - loadStarted).toFixed(1)}ms)`,
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
const bootstrapSource = await this.resolveConfigPath(args.consumerRoot, args.configPathOverride);
|
|
49
|
+
phaseMs("resolveConfigPath");
|
|
50
|
+
if (!bootstrapSource) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'Codemation config not found. Expected "codemation.config.ts" in the consumer project root or "src/".',
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const moduleExports = await this.importModule(bootstrapSource);
|
|
56
|
+
phaseMs("importConfigModule");
|
|
57
|
+
const rawConfig = this.configExportsResolver.resolveConfig(moduleExports);
|
|
58
|
+
if (!rawConfig) {
|
|
59
|
+
throw new Error(`Config file does not export a Codemation config object: ${bootstrapSource}`);
|
|
60
|
+
}
|
|
61
|
+
const config = this.configNormalizer.normalize(rawConfig);
|
|
62
|
+
const workflowSources = await this.resolveWorkflowSources(args.consumerRoot, config);
|
|
63
|
+
phaseMs("resolveWorkflowSources");
|
|
64
|
+
const workflows = this.mergeWorkflows(
|
|
65
|
+
config.workflows ?? [],
|
|
66
|
+
await this.loadDiscoveredWorkflows(args.consumerRoot, config, workflowSources),
|
|
67
|
+
);
|
|
68
|
+
phaseMs("loadDiscoveredWorkflows");
|
|
69
|
+
const resolvedConfig: CodemationConfig = {
|
|
70
|
+
...config,
|
|
71
|
+
workflows,
|
|
72
|
+
};
|
|
73
|
+
logLevelPolicyFactory.create().applyCodemationLogConfig(resolvedConfig.log);
|
|
74
|
+
return {
|
|
75
|
+
config: resolvedConfig,
|
|
76
|
+
bootstrapSource,
|
|
77
|
+
workflowSources,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private async resolveConfigPath(
|
|
82
|
+
consumerRoot: string,
|
|
83
|
+
configPathOverride: string | undefined,
|
|
84
|
+
): Promise<string | null> {
|
|
85
|
+
if (configPathOverride) {
|
|
86
|
+
const explicitPath = path.isAbsolute(configPathOverride)
|
|
87
|
+
? configPathOverride
|
|
88
|
+
: path.resolve(consumerRoot, configPathOverride);
|
|
89
|
+
if (!(await this.exists(explicitPath))) {
|
|
90
|
+
throw new Error(`Config file not found: ${explicitPath}`);
|
|
91
|
+
}
|
|
92
|
+
return explicitPath;
|
|
93
|
+
}
|
|
94
|
+
for (const candidate of this.getConventionCandidates(consumerRoot)) {
|
|
95
|
+
if (await this.exists(candidate)) {
|
|
96
|
+
return candidate;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private getConventionCandidates(consumerRoot: string): ReadonlyArray<string> {
|
|
103
|
+
return [
|
|
104
|
+
path.resolve(consumerRoot, "codemation.config.ts"),
|
|
105
|
+
path.resolve(consumerRoot, "codemation.config.js"),
|
|
106
|
+
path.resolve(consumerRoot, "src", "codemation.config.ts"),
|
|
107
|
+
path.resolve(consumerRoot, "src", "codemation.config.js"),
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private async resolveWorkflowSources(consumerRoot: string, config: CodemationConfig): Promise<ReadonlyArray<string>> {
|
|
112
|
+
if ((config.workflowDiscovery?.directories?.length ?? 0) === 0) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
const discoveredPaths = await this.workflowModulePathFinder.discoverModulePaths({
|
|
116
|
+
consumerRoot,
|
|
117
|
+
workflowDirectories: config.workflowDiscovery?.directories,
|
|
118
|
+
exists: (absolutePath) => this.exists(absolutePath),
|
|
119
|
+
});
|
|
120
|
+
return [...discoveredPaths].sort((left: string, right: string) => left.localeCompare(right));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private async loadDiscoveredWorkflows(
|
|
124
|
+
consumerRoot: string,
|
|
125
|
+
config: CodemationConfig,
|
|
126
|
+
workflowSources: ReadonlyArray<string>,
|
|
127
|
+
): Promise<ReadonlyArray<WorkflowDefinition>> {
|
|
128
|
+
const workflowDiscoveryDirectories = config.workflowDiscovery?.directories ?? [];
|
|
129
|
+
const workflowsById = new Map<string, WorkflowDefinition>();
|
|
130
|
+
const loadedWorkflowModules = await Promise.all(
|
|
131
|
+
workflowSources.map(async (workflowSource: string) => ({
|
|
132
|
+
workflowSource,
|
|
133
|
+
segments: this.pathSegmentsComputer.compute({
|
|
134
|
+
consumerRoot,
|
|
135
|
+
workflowDiscoveryDirectories,
|
|
136
|
+
absoluteWorkflowModulePath: workflowSource,
|
|
137
|
+
}),
|
|
138
|
+
moduleExports: await this.importModule(workflowSource),
|
|
139
|
+
})),
|
|
140
|
+
);
|
|
141
|
+
for (const loadedWorkflowModule of loadedWorkflowModules) {
|
|
142
|
+
for (const workflow of this.workflowDefinitionExportsResolver.resolve(loadedWorkflowModule.moduleExports)) {
|
|
143
|
+
const enriched =
|
|
144
|
+
loadedWorkflowModule.segments && loadedWorkflowModule.segments.length > 0
|
|
145
|
+
? ({ ...workflow, discoveryPathSegments: loadedWorkflowModule.segments } satisfies WorkflowDefinition)
|
|
146
|
+
: workflow;
|
|
147
|
+
workflowsById.set(workflow.id, enriched);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (workflowsById.size === 0 && workflowSources.length > 0) {
|
|
151
|
+
throw new Error(this.discoveredWorkflowsEmptyMessageFactory.create(workflowSources));
|
|
152
|
+
}
|
|
153
|
+
return [...workflowsById.values()];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private mergeWorkflows(
|
|
157
|
+
configuredWorkflows: ReadonlyArray<WorkflowDefinition>,
|
|
158
|
+
discoveredWorkflows: ReadonlyArray<WorkflowDefinition>,
|
|
159
|
+
): ReadonlyArray<WorkflowDefinition> {
|
|
160
|
+
const workflowsById = new Map<string, WorkflowDefinition>();
|
|
161
|
+
for (const workflow of discoveredWorkflows) {
|
|
162
|
+
workflowsById.set(workflow.id, workflow);
|
|
163
|
+
}
|
|
164
|
+
for (const workflow of configuredWorkflows) {
|
|
165
|
+
workflowsById.set(workflow.id, workflow);
|
|
166
|
+
}
|
|
167
|
+
return [...workflowsById.values()];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private async importModule(modulePath: string): Promise<Record<string, unknown>> {
|
|
171
|
+
if (this.shouldUseNativeRuntimeImport()) {
|
|
172
|
+
return await this.importModuleWithNativeRuntime(modulePath);
|
|
173
|
+
}
|
|
174
|
+
const tsconfigPath = await this.resolveTsconfigPath(modulePath);
|
|
175
|
+
const importSpecifier = await this.createImportSpecifier(modulePath);
|
|
176
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
177
|
+
try {
|
|
178
|
+
const importedModule = await this.getOrCreateImporter(tsconfigPath).import(importSpecifier, import.meta.url);
|
|
179
|
+
return importedModule as Record<string, unknown>;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (!this.isStoppedTransformServiceError(error) || attempt === 2) {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
await this.resetImporter(tsconfigPath);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`Failed to import consumer module after retries: ${modulePath}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private async importModuleWithNativeRuntime(modulePath: string): Promise<Record<string, unknown>> {
|
|
191
|
+
const importedModule = await import(await this.createImportSpecifier(modulePath));
|
|
192
|
+
return importedModule as Record<string, unknown>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private async resolveTsconfigPath(modulePath: string): Promise<string | false> {
|
|
196
|
+
const overridePath = process.env.CODEMATION_TSCONFIG_PATH;
|
|
197
|
+
if (overridePath && (await this.exists(overridePath))) {
|
|
198
|
+
return overridePath;
|
|
199
|
+
}
|
|
200
|
+
const discoveredPath = await this.findNearestTsconfig(modulePath);
|
|
201
|
+
return discoveredPath ?? false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private getOrCreateImporter(tsconfigPath: string | false): NamespacedUnregister {
|
|
205
|
+
const cacheKey = tsconfigPath || "default";
|
|
206
|
+
const existingImporter = CodemationConsumerConfigLoader.importerRegistrationsByTsconfig.get(cacheKey);
|
|
207
|
+
if (existingImporter) {
|
|
208
|
+
return existingImporter;
|
|
209
|
+
}
|
|
210
|
+
const nextImporter = register({
|
|
211
|
+
namespace: this.toNamespace(cacheKey),
|
|
212
|
+
tsconfig: tsconfigPath,
|
|
213
|
+
});
|
|
214
|
+
CodemationConsumerConfigLoader.importerRegistrationsByTsconfig.set(cacheKey, nextImporter);
|
|
215
|
+
return nextImporter;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private async resetImporter(tsconfigPath: string | false): Promise<void> {
|
|
219
|
+
const cacheKey = tsconfigPath || "default";
|
|
220
|
+
const existingImporter = CodemationConsumerConfigLoader.importerRegistrationsByTsconfig.get(cacheKey);
|
|
221
|
+
if (!existingImporter) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
CodemationConsumerConfigLoader.importerRegistrationsByTsconfig.delete(cacheKey);
|
|
225
|
+
await existingImporter.unregister().catch(() => null);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private toNamespace(cacheKey: string): string {
|
|
229
|
+
return `codemation_consumer_${cacheKey.replace(/[^a-zA-Z0-9_-]+/g, "_")}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private async findNearestTsconfig(modulePath: string): Promise<string | null> {
|
|
233
|
+
let currentDirectory = path.dirname(modulePath);
|
|
234
|
+
while (true) {
|
|
235
|
+
const candidate = path.resolve(currentDirectory, "tsconfig.json");
|
|
236
|
+
if (await this.exists(candidate)) {
|
|
237
|
+
return candidate;
|
|
238
|
+
}
|
|
239
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
240
|
+
if (parentDirectory === currentDirectory) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
currentDirectory = parentDirectory;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private async createImportSpecifier(modulePath: string): Promise<string> {
|
|
248
|
+
const moduleUrl = pathToFileURL(modulePath);
|
|
249
|
+
const moduleStats = await stat(modulePath);
|
|
250
|
+
moduleUrl.searchParams.set("t", String(moduleStats.mtimeMs));
|
|
251
|
+
return moduleUrl.href;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private shouldUseNativeRuntimeImport(): boolean {
|
|
255
|
+
return process.env.CODEMATION_TS_RUNTIME === "ts-node";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private isStoppedTransformServiceError(error: unknown): boolean {
|
|
259
|
+
return error instanceof Error && error.message.includes("The service is no longer running");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private async exists(filePath: string): Promise<boolean> {
|
|
263
|
+
try {
|
|
264
|
+
await access(filePath);
|
|
265
|
+
return true;
|
|
266
|
+
} catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { CodemationPackageManifest } from "../config/CodemationPackageManifest";
|
|
2
|
+
import type { CodemationPlugin } from "../config/CodemationPlugin";
|
|
3
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
export type CodemationDiscoveredPluginPackage = Readonly<{
|
|
8
|
+
packageName: string;
|
|
9
|
+
packageRoot: string;
|
|
10
|
+
manifest: NonNullable<CodemationPackageManifest["plugin"]>;
|
|
11
|
+
developmentEntry?: string;
|
|
12
|
+
}>;
|
|
13
|
+
|
|
14
|
+
export type CodemationResolvedPluginPackage = Readonly<
|
|
15
|
+
CodemationDiscoveredPluginPackage & {
|
|
16
|
+
plugin: CodemationPlugin;
|
|
17
|
+
}
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
type PackageJsonShape = Readonly<{
|
|
21
|
+
codemation?: CodemationPackageManifest;
|
|
22
|
+
name?: string;
|
|
23
|
+
exports?: Readonly<Record<string, unknown>>;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
export class CodemationPluginDiscovery {
|
|
27
|
+
async discover(consumerRoot: string): Promise<ReadonlyArray<CodemationDiscoveredPluginPackage>> {
|
|
28
|
+
const nodeModulesRoot = path.resolve(consumerRoot, "node_modules");
|
|
29
|
+
const packageRoots = await this.collectPackageRoots(nodeModulesRoot);
|
|
30
|
+
const discoveredPackages: CodemationDiscoveredPluginPackage[] = [];
|
|
31
|
+
for (const packageRoot of packageRoots) {
|
|
32
|
+
const packageJson = await this.readPackageJson(path.resolve(packageRoot, "package.json"));
|
|
33
|
+
const pluginManifest = packageJson.codemation?.plugin;
|
|
34
|
+
if (!packageJson.name || !pluginManifest || pluginManifest.kind !== "plugin") {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
discoveredPackages.push({
|
|
38
|
+
packageName: packageJson.name,
|
|
39
|
+
packageRoot,
|
|
40
|
+
manifest: pluginManifest,
|
|
41
|
+
developmentEntry: this.resolveDevelopmentPluginEntry(packageJson),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return discoveredPackages.sort((left, right) => left.packageName.localeCompare(right.packageName));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async resolvePlugins(consumerRoot: string): Promise<ReadonlyArray<CodemationResolvedPluginPackage>> {
|
|
48
|
+
const discoveredPackages = await this.discover(consumerRoot);
|
|
49
|
+
return await this.resolveDiscoveredPackages(discoveredPackages);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async resolveDiscoveredPackages(
|
|
53
|
+
discoveredPackages: ReadonlyArray<CodemationDiscoveredPluginPackage>,
|
|
54
|
+
): Promise<ReadonlyArray<CodemationResolvedPluginPackage>> {
|
|
55
|
+
const resolvedPackages: CodemationResolvedPluginPackage[] = [];
|
|
56
|
+
for (const discoveredPackage of discoveredPackages) {
|
|
57
|
+
resolvedPackages.push({
|
|
58
|
+
...discoveredPackage,
|
|
59
|
+
plugin: await this.loadPlugin(discoveredPackage),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return resolvedPackages;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private async collectPackageRoots(nodeModulesRoot: string): Promise<ReadonlyArray<string>> {
|
|
66
|
+
try {
|
|
67
|
+
const entries = await readdir(nodeModulesRoot, { withFileTypes: true });
|
|
68
|
+
const packageRoots: string[] = [];
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (entry.name.startsWith("@")) {
|
|
74
|
+
const scopedEntries = await readdir(path.resolve(nodeModulesRoot, entry.name), { withFileTypes: true });
|
|
75
|
+
for (const scopedEntry of scopedEntries) {
|
|
76
|
+
if (scopedEntry.isDirectory() || scopedEntry.isSymbolicLink()) {
|
|
77
|
+
packageRoots.push(path.resolve(nodeModulesRoot, entry.name, scopedEntry.name));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
packageRoots.push(path.resolve(nodeModulesRoot, entry.name));
|
|
83
|
+
}
|
|
84
|
+
return packageRoots;
|
|
85
|
+
} catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async readPackageJson(packageJsonPath: string): Promise<PackageJsonShape> {
|
|
91
|
+
try {
|
|
92
|
+
const rawPackageJson = await readFile(packageJsonPath, "utf8");
|
|
93
|
+
return JSON.parse(rawPackageJson) as PackageJsonShape;
|
|
94
|
+
} catch {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private async loadPlugin(discoveredPackage: CodemationDiscoveredPluginPackage): Promise<CodemationPlugin> {
|
|
100
|
+
const pluginModulePath = path.resolve(discoveredPackage.packageRoot, this.resolvePluginEntry(discoveredPackage));
|
|
101
|
+
const importedModule = (await import(pathToFileURL(pluginModulePath).href)) as Record<string, unknown>;
|
|
102
|
+
const pluginExportName = discoveredPackage.manifest.exportName;
|
|
103
|
+
const explicitExport = pluginExportName ? importedModule[pluginExportName] : undefined;
|
|
104
|
+
const exportedValue = explicitExport ?? importedModule.default ?? importedModule.codemationPlugin;
|
|
105
|
+
const plugin = this.resolvePluginValue(exportedValue);
|
|
106
|
+
if (!plugin) {
|
|
107
|
+
throw new Error(`Plugin package "${discoveredPackage.packageName}" did not export a Codemation plugin instance.`);
|
|
108
|
+
}
|
|
109
|
+
return plugin;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private resolvePluginValue(value: unknown): CodemationPlugin | null {
|
|
113
|
+
if (this.isPlugin(value)) {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
if (this.isPluginConstructor(value)) {
|
|
117
|
+
return new value();
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private isPlugin(value: unknown): value is CodemationPlugin {
|
|
123
|
+
return (
|
|
124
|
+
Boolean(value) && typeof value === "object" && typeof (value as { register?: unknown }).register === "function"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private isPluginConstructor(value: unknown): value is new () => CodemationPlugin {
|
|
129
|
+
return typeof value === "function" && this.isPlugin(value.prototype);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private resolvePluginEntry(discoveredPackage: CodemationDiscoveredPluginPackage): string {
|
|
133
|
+
if (
|
|
134
|
+
process.env.CODEMATION_PREFER_PLUGIN_SOURCE_ENTRY === "true" &&
|
|
135
|
+
typeof discoveredPackage.developmentEntry === "string" &&
|
|
136
|
+
discoveredPackage.developmentEntry.trim().length > 0
|
|
137
|
+
) {
|
|
138
|
+
return discoveredPackage.developmentEntry;
|
|
139
|
+
}
|
|
140
|
+
return discoveredPackage.manifest.entry;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private resolveDevelopmentPluginEntry(packageJson: PackageJsonShape): string | undefined {
|
|
144
|
+
const exportRecord = packageJson.exports?.["./codemation-plugin"];
|
|
145
|
+
if (!exportRecord || typeof exportRecord !== "object") {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
const importPath = (exportRecord as { import?: unknown }).import;
|
|
149
|
+
return typeof importPath === "string" && importPath.trim().length > 0 ? importPath : undefined;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class CodemationTsyringeParamInfoReader {
|
|
2
|
+
private static readonly injectionTokenMetadataKey = "injectionTokens";
|
|
3
|
+
private static readonly designParamTypesMetadataKey = "design:paramtypes";
|
|
4
|
+
|
|
5
|
+
static read(target: object): ReadonlyArray<unknown> {
|
|
6
|
+
const designParamTypes = this.readDesignParamTypes(target);
|
|
7
|
+
const injectionTokens = this.readInjectionTokens(target);
|
|
8
|
+
Object.keys(injectionTokens).forEach((key: string) => {
|
|
9
|
+
designParamTypes[Number(key)] = injectionTokens[key];
|
|
10
|
+
});
|
|
11
|
+
return designParamTypes;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private static readDesignParamTypes(target: object): unknown[] {
|
|
15
|
+
const reflected = Reflect.getMetadata?.(this.designParamTypesMetadataKey, target);
|
|
16
|
+
return Array.isArray(reflected) ? [...reflected] : [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private static readInjectionTokens(target: object): Record<string, unknown> {
|
|
20
|
+
const reflected = Reflect.getOwnMetadata?.(this.injectionTokenMetadataKey, target);
|
|
21
|
+
if (!reflected || typeof reflected !== "object") {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
return reflected as Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Container, TypeToken, WorkflowDefinition } from "@codemation/core";
|
|
2
|
+
|
|
3
|
+
import { CodemationTsyringeParamInfoReader } from "./CodemationTsyringeParamInfoReader";
|
|
4
|
+
|
|
5
|
+
type InjectionDescriptor = Readonly<{
|
|
6
|
+
token?: unknown;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
export class CodemationTsyringeTypeInfoRegistrar {
|
|
10
|
+
private readonly visitedTokens = new Set<unknown>();
|
|
11
|
+
private readonly visitedConfigObjects = new Set<object>();
|
|
12
|
+
|
|
13
|
+
constructor(private readonly container: Container) {}
|
|
14
|
+
|
|
15
|
+
registerWorkflowDefinitions(workflows: ReadonlyArray<WorkflowDefinition>): void {
|
|
16
|
+
for (const workflow of workflows) {
|
|
17
|
+
for (const node of workflow.nodes) {
|
|
18
|
+
this.registerTypeToken(node.type);
|
|
19
|
+
this.registerConfigTokens(node.config);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
registerTypeToken(token: unknown): void {
|
|
25
|
+
if (typeof token !== "function" || this.visitedTokens.has(token)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.visitedTokens.add(token);
|
|
29
|
+
const paramInfo = CodemationTsyringeParamInfoReader.read(token);
|
|
30
|
+
for (const dependency of paramInfo) {
|
|
31
|
+
this.registerDependency(dependency);
|
|
32
|
+
}
|
|
33
|
+
this.registerFactoryProvider(token as new (...args: ReadonlyArray<unknown>) => unknown, paramInfo);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private registerDependency(dependency: unknown): void {
|
|
37
|
+
const token = this.resolveDependencyToken(dependency);
|
|
38
|
+
if (typeof token !== "function") {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!this.container.isRegistered(token as TypeToken<unknown>, true)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.registerTypeToken(token);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private registerConfigTokens(value: unknown): void {
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
value.forEach((entry: unknown) => this.registerConfigTokens(entry));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!value || typeof value !== "object") {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (this.visitedConfigObjects.has(value)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.visitedConfigObjects.add(value);
|
|
59
|
+
if ("type" in value && typeof value.type === "function") {
|
|
60
|
+
this.registerTypeToken(value.type);
|
|
61
|
+
}
|
|
62
|
+
Object.values(value).forEach((entry: unknown) => this.registerConfigTokens(entry));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private registerFactoryProvider(
|
|
66
|
+
token: new (...args: ReadonlyArray<unknown>) => unknown,
|
|
67
|
+
paramInfo: ReadonlyArray<unknown>,
|
|
68
|
+
): void {
|
|
69
|
+
if (this.container.isRegistered(token as TypeToken<unknown>, true)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const classToken = token as unknown as TypeToken<unknown>;
|
|
73
|
+
const constructorToken = token as unknown as new (...args: ReadonlyArray<unknown>) => unknown;
|
|
74
|
+
this.container.register(classToken, {
|
|
75
|
+
useFactory: (dependencyContainer) => {
|
|
76
|
+
const dependencies = paramInfo.map((dependency: unknown) =>
|
|
77
|
+
this.resolveFactoryDependency(dependencyContainer, dependency),
|
|
78
|
+
);
|
|
79
|
+
return new constructorToken(...dependencies);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private resolveDependencyToken(dependency: unknown): unknown {
|
|
85
|
+
if (this.isInjectionDescriptor(dependency)) {
|
|
86
|
+
return dependency.token;
|
|
87
|
+
}
|
|
88
|
+
return dependency;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private resolveFactoryDependency(dependencyContainer: Container, dependency: unknown): unknown {
|
|
92
|
+
const token = this.resolveDependencyToken(dependency);
|
|
93
|
+
if (typeof token === "function") {
|
|
94
|
+
if (dependencyContainer.isRegistered(token as TypeToken<unknown>, true)) {
|
|
95
|
+
try {
|
|
96
|
+
return dependencyContainer.resolve(token as TypeToken<unknown>);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (!this.isMissingTypeInfoError(error)) {
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
this.registerTypeToken(token);
|
|
104
|
+
const constructorToken = token as unknown as new (...args: ReadonlyArray<unknown>) => unknown;
|
|
105
|
+
const paramInfo = CodemationTsyringeParamInfoReader.read(token);
|
|
106
|
+
const nestedDependencies = paramInfo.map((entry: unknown) =>
|
|
107
|
+
this.resolveFactoryDependency(dependencyContainer, entry),
|
|
108
|
+
);
|
|
109
|
+
return new constructorToken(...nestedDependencies);
|
|
110
|
+
}
|
|
111
|
+
return dependencyContainer.resolve(token as TypeToken<unknown>);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private isInjectionDescriptor(value: unknown): value is InjectionDescriptor {
|
|
115
|
+
return value !== null && typeof value === "object" && "token" in value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private isMissingTypeInfoError(error: unknown): boolean {
|
|
119
|
+
return error instanceof Error && error.message.includes("TypeInfo not known for");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type DevelopmentRuntimeSignal =
|
|
2
|
+
| Readonly<{ kind: "buildStarted"; buildVersion?: string }>
|
|
3
|
+
| Readonly<{ kind: "buildCompleted"; buildVersion?: string }>
|
|
4
|
+
| Readonly<{ kind: "buildFailed"; message: string }>;
|
|
5
|
+
|
|
6
|
+
export class DevelopmentRuntimeRouteGuard {
|
|
7
|
+
static isAuthorized(request: Request): boolean {
|
|
8
|
+
if (this.isLoopbackRequest(request)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
const expectedToken = process.env.CODEMATION_DEV_SERVER_TOKEN;
|
|
12
|
+
if (!expectedToken) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return request.headers.get("x-codemation-dev-token") === expectedToken;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private static isLoopbackRequest(request: Request): boolean {
|
|
19
|
+
const requestUrl = new URL(request.url);
|
|
20
|
+
return requestUrl.hostname === "127.0.0.1" || requestUrl.hostname === "localhost" || requestUrl.hostname === "::1";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static async parseSignal(request: Request): Promise<DevelopmentRuntimeSignal> {
|
|
24
|
+
const payload = (await request.json()) as Readonly<{
|
|
25
|
+
kind?: unknown;
|
|
26
|
+
buildVersion?: unknown;
|
|
27
|
+
message?: unknown;
|
|
28
|
+
}>;
|
|
29
|
+
return DevelopmentRuntimeRouteGuard.parseSignalFromPayload(payload);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static parseSignalFromPayload(
|
|
33
|
+
payload: Readonly<{
|
|
34
|
+
kind?: unknown;
|
|
35
|
+
buildVersion?: unknown;
|
|
36
|
+
message?: unknown;
|
|
37
|
+
}>,
|
|
38
|
+
): DevelopmentRuntimeSignal {
|
|
39
|
+
if (payload.kind === "buildStarted") {
|
|
40
|
+
return {
|
|
41
|
+
kind: payload.kind,
|
|
42
|
+
buildVersion: typeof payload.buildVersion === "string" ? payload.buildVersion : undefined,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (payload.kind === "buildCompleted") {
|
|
46
|
+
return {
|
|
47
|
+
kind: payload.kind,
|
|
48
|
+
buildVersion: typeof payload.buildVersion === "string" ? payload.buildVersion : undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (payload.kind === "buildFailed" && typeof payload.message === "string" && payload.message.length > 0) {
|
|
52
|
+
return {
|
|
53
|
+
kind: payload.kind,
|
|
54
|
+
message: payload.message,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
throw new Error("Unsupported development runtime signal.");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class DiscoveredWorkflowsEmptyMessageFactory {
|
|
2
|
+
create(discoveredPaths: ReadonlyArray<string>): string {
|
|
3
|
+
const lines = discoveredPaths.map((p) => ` - ${p}`).join("\n");
|
|
4
|
+
return [
|
|
5
|
+
`Discovered ${discoveredPaths.length} file(s) under workflow discovery, but none export a WorkflowDefinition.`,
|
|
6
|
+
lines,
|
|
7
|
+
"",
|
|
8
|
+
"Move shared helpers outside the discovery directories (for example src/lib), or export at least one object with id, name, nodes, and edges from a workflow module.",
|
|
9
|
+
].join("\n");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WorkflowDefinition } from "@codemation/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Collects exported values that match the {@link WorkflowDefinition} shape.
|
|
5
|
+
* Other exports (helpers, constants, type-only re-exports) are ignored.
|
|
6
|
+
*/
|
|
7
|
+
export class WorkflowDefinitionExportsResolver {
|
|
8
|
+
resolve(moduleExports: Readonly<Record<string, unknown>>): ReadonlyArray<WorkflowDefinition> {
|
|
9
|
+
const workflows: WorkflowDefinition[] = [];
|
|
10
|
+
for (const exportedValue of Object.values(moduleExports)) {
|
|
11
|
+
if (this.isWorkflowDefinition(exportedValue)) {
|
|
12
|
+
workflows.push(exportedValue);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return workflows;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private isWorkflowDefinition(value: unknown): value is WorkflowDefinition {
|
|
19
|
+
if (!value || typeof value !== "object") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return "id" in value && "name" in value && "nodes" in value && "edges" in value;
|
|
23
|
+
}
|
|
24
|
+
}
|