@smthrs/control 0.0.0-stage → 1.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +194 -0
- package/LICENSE +21 -0
- package/README.md +168 -2
- package/dist/cjs/ApprovalAuthority.d.ts +73 -0
- package/dist/cjs/ApprovalAuthority.d.ts.map +1 -0
- package/dist/cjs/ApprovalAuthority.js +62 -0
- package/dist/cjs/ApprovalAuthority.js.map +7 -0
- package/dist/cjs/Cancellation.d.ts +107 -0
- package/dist/cjs/Cancellation.d.ts.map +1 -0
- package/dist/cjs/Cancellation.js +72 -0
- package/dist/cjs/Cancellation.js.map +7 -0
- package/dist/cjs/Channels.d.ts +170 -0
- package/dist/cjs/Channels.d.ts.map +1 -0
- package/dist/cjs/Channels.js +278 -0
- package/dist/cjs/Channels.js.map +7 -0
- package/dist/cjs/Control.d.ts +202 -0
- package/dist/cjs/Control.d.ts.map +1 -0
- package/dist/cjs/Control.js +47 -0
- package/dist/cjs/Control.js.map +7 -0
- package/dist/cjs/ControlClient.d.ts +52 -0
- package/dist/cjs/ControlClient.d.ts.map +1 -0
- package/dist/cjs/ControlClient.js +191 -0
- package/dist/cjs/ControlClient.js.map +7 -0
- package/dist/cjs/ControlError.d.ts +318 -0
- package/dist/cjs/ControlError.d.ts.map +1 -0
- package/dist/cjs/ControlError.js +249 -0
- package/dist/cjs/ControlError.js.map +7 -0
- package/dist/cjs/ControlExecutor.d.ts +372 -0
- package/dist/cjs/ControlExecutor.d.ts.map +1 -0
- package/dist/cjs/ControlExecutor.js +123 -0
- package/dist/cjs/ControlExecutor.js.map +7 -0
- package/dist/cjs/ControlFacts.d.ts +454 -0
- package/dist/cjs/ControlFacts.d.ts.map +1 -0
- package/dist/cjs/ControlFacts.js +261 -0
- package/dist/cjs/ControlFacts.js.map +7 -0
- package/dist/cjs/ControlLive.d.ts +23 -0
- package/dist/cjs/ControlLive.d.ts.map +1 -0
- package/dist/cjs/ControlLive.js +1293 -0
- package/dist/cjs/ControlLive.js.map +7 -0
- package/dist/cjs/ControlRpcs.d.ts +1204 -0
- package/dist/cjs/ControlRpcs.d.ts.map +1 -0
- package/dist/cjs/ControlRpcs.js +247 -0
- package/dist/cjs/ControlRpcs.js.map +7 -0
- package/dist/cjs/ControlRuntime.d.ts +635 -0
- package/dist/cjs/ControlRuntime.d.ts.map +1 -0
- package/dist/cjs/ControlRuntime.js +744 -0
- package/dist/cjs/ControlRuntime.js.map +7 -0
- package/dist/cjs/ControlSchema.d.ts +2642 -0
- package/dist/cjs/ControlSchema.d.ts.map +1 -0
- package/dist/cjs/ControlSchema.js +634 -0
- package/dist/cjs/ControlSchema.js.map +7 -0
- package/dist/cjs/ControlServer.d.ts +51 -0
- package/dist/cjs/ControlServer.d.ts.map +1 -0
- package/dist/cjs/ControlServer.js +121 -0
- package/dist/cjs/ControlServer.js.map +7 -0
- package/dist/cjs/Credential.d.ts +136 -0
- package/dist/cjs/Credential.d.ts.map +1 -0
- package/dist/cjs/Credential.js +168 -0
- package/dist/cjs/Credential.js.map +7 -0
- package/dist/cjs/CredentialCipher.d.ts +90 -0
- package/dist/cjs/CredentialCipher.d.ts.map +1 -0
- package/dist/cjs/CredentialCipher.js +45 -0
- package/dist/cjs/CredentialCipher.js.map +7 -0
- package/dist/cjs/CredentialStore.d.ts +97 -0
- package/dist/cjs/CredentialStore.d.ts.map +1 -0
- package/dist/cjs/CredentialStore.js +81 -0
- package/dist/cjs/CredentialStore.js.map +7 -0
- package/dist/cjs/DispatchReader.d.ts +112 -0
- package/dist/cjs/DispatchReader.d.ts.map +1 -0
- package/dist/cjs/DispatchReader.js +45 -0
- package/dist/cjs/DispatchReader.js.map +7 -0
- package/dist/cjs/Health.d.ts +333 -0
- package/dist/cjs/Health.d.ts.map +1 -0
- package/dist/cjs/Health.js +311 -0
- package/dist/cjs/Health.js.map +7 -0
- package/dist/cjs/JevSessionChecker.d.ts +57 -0
- package/dist/cjs/JevSessionChecker.d.ts.map +1 -0
- package/dist/cjs/JevSessionChecker.js +113 -0
- package/dist/cjs/JevSessionChecker.js.map +7 -0
- package/dist/cjs/Lineage.d.ts +131 -0
- package/dist/cjs/Lineage.d.ts.map +1 -0
- package/dist/cjs/Lineage.js +81 -0
- package/dist/cjs/Lineage.js.map +7 -0
- package/dist/cjs/Migrations.d.ts +34 -0
- package/dist/cjs/Migrations.d.ts.map +1 -0
- package/dist/cjs/Migrations.js +60 -0
- package/dist/cjs/Migrations.js.map +7 -0
- package/dist/cjs/Monitor.d.ts +282 -0
- package/dist/cjs/Monitor.d.ts.map +1 -0
- package/dist/cjs/Monitor.js +283 -0
- package/dist/cjs/Monitor.js.map +7 -0
- package/dist/cjs/ScopedToken.d.ts +193 -0
- package/dist/cjs/ScopedToken.d.ts.map +1 -0
- package/dist/cjs/ScopedToken.js +135 -0
- package/dist/cjs/ScopedToken.js.map +7 -0
- package/dist/cjs/SqlControlRuntime.d.ts +161 -0
- package/dist/cjs/SqlControlRuntime.d.ts.map +1 -0
- package/dist/cjs/SqlControlRuntime.js +1522 -0
- package/dist/cjs/SqlControlRuntime.js.map +7 -0
- package/dist/cjs/SqlCredentialStore.d.ts +43 -0
- package/dist/cjs/SqlCredentialStore.d.ts.map +1 -0
- package/dist/cjs/SqlCredentialStore.js +113 -0
- package/dist/cjs/SqlCredentialStore.js.map +7 -0
- package/dist/cjs/Steering.d.ts +69 -0
- package/dist/cjs/Steering.d.ts.map +1 -0
- package/dist/cjs/Steering.js +49 -0
- package/dist/cjs/Steering.js.map +7 -0
- package/dist/cjs/SystemFlows.d.ts +223 -0
- package/dist/cjs/SystemFlows.d.ts.map +1 -0
- package/dist/cjs/SystemFlows.js +195 -0
- package/dist/cjs/SystemFlows.js.map +7 -0
- package/dist/cjs/WebCryptoCipher.d.ts +49 -0
- package/dist/cjs/WebCryptoCipher.d.ts.map +1 -0
- package/dist/cjs/WebCryptoCipher.js +129 -0
- package/dist/cjs/WebCryptoCipher.js.map +7 -0
- package/dist/cjs/WebhookChannel.d.ts +113 -0
- package/dist/cjs/WebhookChannel.d.ts.map +1 -0
- package/dist/cjs/WebhookChannel.js +98 -0
- package/dist/cjs/WebhookChannel.js.map +7 -0
- package/dist/cjs/index.d.ts +160 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +91 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/MutationBoundary.d.ts +27 -0
- package/dist/cjs/internal/MutationBoundary.d.ts.map +1 -0
- package/dist/cjs/internal/MutationBoundary.js +50 -0
- package/dist/cjs/internal/MutationBoundary.js.map +7 -0
- package/dist/cjs/internal/activeFibers.d.ts +12 -0
- package/dist/cjs/internal/activeFibers.d.ts.map +1 -0
- package/dist/cjs/internal/activeFibers.js +30 -0
- package/dist/cjs/internal/activeFibers.js.map +7 -0
- package/dist/cjs/internal/issues.d.ts +28 -0
- package/dist/cjs/internal/issues.d.ts.map +1 -0
- package/dist/cjs/internal/issues.js +34 -0
- package/dist/cjs/internal/issues.js.map +7 -0
- package/dist/cjs/internal/planning.d.ts +347 -0
- package/dist/cjs/internal/planning.d.ts.map +1 -0
- package/dist/cjs/internal/planning.js +137 -0
- package/dist/cjs/internal/planning.js.map +7 -0
- package/dist/cjs/internal/sqlSchemaErrors.d.ts +18 -0
- package/dist/cjs/internal/sqlSchemaErrors.d.ts.map +1 -0
- package/dist/cjs/internal/sqlSchemaErrors.js +40 -0
- package/dist/cjs/internal/sqlSchemaErrors.js.map +7 -0
- package/dist/cjs/migrations/0001_control_tables.d.ts +18 -0
- package/dist/cjs/migrations/0001_control_tables.d.ts.map +1 -0
- package/dist/cjs/migrations/0001_control_tables.js +115 -0
- package/dist/cjs/migrations/0001_control_tables.js.map +7 -0
- package/dist/cjs/migrations/0002_run_keys.d.ts +15 -0
- package/dist/cjs/migrations/0002_run_keys.d.ts.map +1 -0
- package/dist/cjs/migrations/0002_run_keys.js +44 -0
- package/dist/cjs/migrations/0002_run_keys.js.map +7 -0
- package/dist/cjs/migrations/0003_signal_commands.d.ts +13 -0
- package/dist/cjs/migrations/0003_signal_commands.d.ts.map +1 -0
- package/dist/cjs/migrations/0003_signal_commands.js +50 -0
- package/dist/cjs/migrations/0003_signal_commands.js.map +7 -0
- package/dist/cjs/migrations/0004_approval_decisions.d.ts +13 -0
- package/dist/cjs/migrations/0004_approval_decisions.d.ts.map +1 -0
- package/dist/cjs/migrations/0004_approval_decisions.js +45 -0
- package/dist/cjs/migrations/0004_approval_decisions.js.map +7 -0
- package/dist/cjs/migrations/0005_signal_principals.d.ts +18 -0
- package/dist/cjs/migrations/0005_signal_principals.d.ts.map +1 -0
- package/dist/cjs/migrations/0005_signal_principals.js +45 -0
- package/dist/cjs/migrations/0005_signal_principals.js.map +7 -0
- package/dist/cjs/migrations/0006_run_principals.d.ts +18 -0
- package/dist/cjs/migrations/0006_run_principals.d.ts.map +1 -0
- package/dist/cjs/migrations/0006_run_principals.js +49 -0
- package/dist/cjs/migrations/0006_run_principals.js.map +7 -0
- package/dist/cjs/migrations/0007_resume_consent.d.ts +18 -0
- package/dist/cjs/migrations/0007_resume_consent.d.ts.map +1 -0
- package/dist/cjs/migrations/0007_resume_consent.js +46 -0
- package/dist/cjs/migrations/0007_resume_consent.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestControl.d.ts +19 -0
- package/dist/cjs/test/TestControl.d.ts.map +1 -0
- package/dist/cjs/test/TestControl.js +62 -0
- package/dist/cjs/test/TestControl.js.map +7 -0
- package/dist/esm/ApprovalAuthority.d.ts +73 -0
- package/dist/esm/ApprovalAuthority.d.ts.map +1 -0
- package/dist/esm/ApprovalAuthority.js +72 -0
- package/dist/esm/ApprovalAuthority.js.map +1 -0
- package/dist/esm/Cancellation.d.ts +107 -0
- package/dist/esm/Cancellation.d.ts.map +1 -0
- package/dist/esm/Cancellation.js +116 -0
- package/dist/esm/Cancellation.js.map +1 -0
- package/dist/esm/Channels.d.ts +170 -0
- package/dist/esm/Channels.d.ts.map +1 -0
- package/dist/esm/Channels.js +312 -0
- package/dist/esm/Channels.js.map +1 -0
- package/dist/esm/Control.d.ts +202 -0
- package/dist/esm/Control.d.ts.map +1 -0
- package/dist/esm/Control.js +42 -0
- package/dist/esm/Control.js.map +1 -0
- package/dist/esm/ControlClient.d.ts +52 -0
- package/dist/esm/ControlClient.d.ts.map +1 -0
- package/dist/esm/ControlClient.js +217 -0
- package/dist/esm/ControlClient.js.map +1 -0
- package/dist/esm/ControlError.d.ts +318 -0
- package/dist/esm/ControlError.d.ts.map +1 -0
- package/dist/esm/ControlError.js +359 -0
- package/dist/esm/ControlError.js.map +1 -0
- package/dist/esm/ControlExecutor.d.ts +372 -0
- package/dist/esm/ControlExecutor.d.ts.map +1 -0
- package/dist/esm/ControlExecutor.js +212 -0
- package/dist/esm/ControlExecutor.js.map +1 -0
- package/dist/esm/ControlFacts.d.ts +454 -0
- package/dist/esm/ControlFacts.d.ts.map +1 -0
- package/dist/esm/ControlFacts.js +324 -0
- package/dist/esm/ControlFacts.js.map +1 -0
- package/dist/esm/ControlLive.d.ts +23 -0
- package/dist/esm/ControlLive.d.ts.map +1 -0
- package/dist/esm/ControlLive.js +1589 -0
- package/dist/esm/ControlLive.js.map +1 -0
- package/dist/esm/ControlRpcs.d.ts +1204 -0
- package/dist/esm/ControlRpcs.d.ts.map +1 -0
- package/dist/esm/ControlRpcs.js +299 -0
- package/dist/esm/ControlRpcs.js.map +1 -0
- package/dist/esm/ControlRuntime.d.ts +635 -0
- package/dist/esm/ControlRuntime.d.ts.map +1 -0
- package/dist/esm/ControlRuntime.js +808 -0
- package/dist/esm/ControlRuntime.js.map +1 -0
- package/dist/esm/ControlSchema.d.ts +2642 -0
- package/dist/esm/ControlSchema.d.ts.map +1 -0
- package/dist/esm/ControlSchema.js +1030 -0
- package/dist/esm/ControlSchema.js.map +1 -0
- package/dist/esm/ControlServer.d.ts +51 -0
- package/dist/esm/ControlServer.d.ts.map +1 -0
- package/dist/esm/ControlServer.js +145 -0
- package/dist/esm/ControlServer.js.map +1 -0
- package/dist/esm/Credential.d.ts +136 -0
- package/dist/esm/Credential.d.ts.map +1 -0
- package/dist/esm/Credential.js +190 -0
- package/dist/esm/Credential.js.map +1 -0
- package/dist/esm/CredentialCipher.d.ts +90 -0
- package/dist/esm/CredentialCipher.d.ts.map +1 -0
- package/dist/esm/CredentialCipher.js +56 -0
- package/dist/esm/CredentialCipher.js.map +1 -0
- package/dist/esm/CredentialStore.d.ts +97 -0
- package/dist/esm/CredentialStore.d.ts.map +1 -0
- package/dist/esm/CredentialStore.js +101 -0
- package/dist/esm/CredentialStore.js.map +1 -0
- package/dist/esm/DispatchReader.d.ts +112 -0
- package/dist/esm/DispatchReader.d.ts.map +1 -0
- package/dist/esm/DispatchReader.js +76 -0
- package/dist/esm/DispatchReader.js.map +1 -0
- package/dist/esm/Health.d.ts +333 -0
- package/dist/esm/Health.d.ts.map +1 -0
- package/dist/esm/Health.js +400 -0
- package/dist/esm/Health.js.map +1 -0
- package/dist/esm/JevSessionChecker.d.ts +57 -0
- package/dist/esm/JevSessionChecker.d.ts.map +1 -0
- package/dist/esm/JevSessionChecker.js +108 -0
- package/dist/esm/JevSessionChecker.js.map +1 -0
- package/dist/esm/Lineage.d.ts +131 -0
- package/dist/esm/Lineage.d.ts.map +1 -0
- package/dist/esm/Lineage.js +174 -0
- package/dist/esm/Lineage.js.map +1 -0
- package/dist/esm/Migrations.d.ts +34 -0
- package/dist/esm/Migrations.d.ts.map +1 -0
- package/dist/esm/Migrations.js +53 -0
- package/dist/esm/Migrations.js.map +1 -0
- package/dist/esm/Monitor.d.ts +282 -0
- package/dist/esm/Monitor.d.ts.map +1 -0
- package/dist/esm/Monitor.js +415 -0
- package/dist/esm/Monitor.js.map +1 -0
- package/dist/esm/ScopedToken.d.ts +193 -0
- package/dist/esm/ScopedToken.d.ts.map +1 -0
- package/dist/esm/ScopedToken.js +224 -0
- package/dist/esm/ScopedToken.js.map +1 -0
- package/dist/esm/SqlControlRuntime.d.ts +161 -0
- package/dist/esm/SqlControlRuntime.d.ts.map +1 -0
- package/dist/esm/SqlControlRuntime.js +1756 -0
- package/dist/esm/SqlControlRuntime.js.map +1 -0
- package/dist/esm/SqlCredentialStore.d.ts +43 -0
- package/dist/esm/SqlCredentialStore.d.ts.map +1 -0
- package/dist/esm/SqlCredentialStore.js +97 -0
- package/dist/esm/SqlCredentialStore.js.map +1 -0
- package/dist/esm/Steering.d.ts +69 -0
- package/dist/esm/Steering.d.ts.map +1 -0
- package/dist/esm/Steering.js +89 -0
- package/dist/esm/Steering.js.map +1 -0
- package/dist/esm/SystemFlows.d.ts +223 -0
- package/dist/esm/SystemFlows.d.ts.map +1 -0
- package/dist/esm/SystemFlows.js +198 -0
- package/dist/esm/SystemFlows.js.map +1 -0
- package/dist/esm/WebCryptoCipher.d.ts +49 -0
- package/dist/esm/WebCryptoCipher.d.ts.map +1 -0
- package/dist/esm/WebCryptoCipher.js +123 -0
- package/dist/esm/WebCryptoCipher.js.map +1 -0
- package/dist/esm/WebhookChannel.d.ts +113 -0
- package/dist/esm/WebhookChannel.d.ts.map +1 -0
- package/dist/esm/WebhookChannel.js +109 -0
- package/dist/esm/WebhookChannel.js.map +1 -0
- package/dist/esm/index.d.ts +160 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +160 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/MutationBoundary.d.ts +27 -0
- package/dist/esm/internal/MutationBoundary.d.ts.map +1 -0
- package/dist/esm/internal/MutationBoundary.js +40 -0
- package/dist/esm/internal/MutationBoundary.js.map +1 -0
- package/dist/esm/internal/activeFibers.d.ts +12 -0
- package/dist/esm/internal/activeFibers.d.ts.map +1 -0
- package/dist/esm/internal/activeFibers.js +17 -0
- package/dist/esm/internal/activeFibers.js.map +1 -0
- package/dist/esm/internal/issues.d.ts +28 -0
- package/dist/esm/internal/issues.d.ts.map +1 -0
- package/dist/esm/internal/issues.js +35 -0
- package/dist/esm/internal/issues.js.map +1 -0
- package/dist/esm/internal/planning.d.ts +347 -0
- package/dist/esm/internal/planning.d.ts.map +1 -0
- package/dist/esm/internal/planning.js +199 -0
- package/dist/esm/internal/planning.js.map +1 -0
- package/dist/esm/internal/sqlSchemaErrors.d.ts +18 -0
- package/dist/esm/internal/sqlSchemaErrors.d.ts.map +1 -0
- package/dist/esm/internal/sqlSchemaErrors.js +38 -0
- package/dist/esm/internal/sqlSchemaErrors.js.map +1 -0
- package/dist/esm/migrations/0001_control_tables.d.ts +18 -0
- package/dist/esm/migrations/0001_control_tables.d.ts.map +1 -0
- package/dist/esm/migrations/0001_control_tables.js +96 -0
- package/dist/esm/migrations/0001_control_tables.js.map +1 -0
- package/dist/esm/migrations/0002_run_keys.d.ts +15 -0
- package/dist/esm/migrations/0002_run_keys.d.ts.map +1 -0
- package/dist/esm/migrations/0002_run_keys.js +22 -0
- package/dist/esm/migrations/0002_run_keys.js.map +1 -0
- package/dist/esm/migrations/0003_signal_commands.d.ts +13 -0
- package/dist/esm/migrations/0003_signal_commands.d.ts.map +1 -0
- package/dist/esm/migrations/0003_signal_commands.js +26 -0
- package/dist/esm/migrations/0003_signal_commands.js.map +1 -0
- package/dist/esm/migrations/0004_approval_decisions.d.ts +13 -0
- package/dist/esm/migrations/0004_approval_decisions.d.ts.map +1 -0
- package/dist/esm/migrations/0004_approval_decisions.js +25 -0
- package/dist/esm/migrations/0004_approval_decisions.js.map +1 -0
- package/dist/esm/migrations/0005_signal_principals.d.ts +18 -0
- package/dist/esm/migrations/0005_signal_principals.d.ts.map +1 -0
- package/dist/esm/migrations/0005_signal_principals.js +27 -0
- package/dist/esm/migrations/0005_signal_principals.js.map +1 -0
- package/dist/esm/migrations/0006_run_principals.d.ts +18 -0
- package/dist/esm/migrations/0006_run_principals.d.ts.map +1 -0
- package/dist/esm/migrations/0006_run_principals.js +30 -0
- package/dist/esm/migrations/0006_run_principals.js.map +1 -0
- package/dist/esm/migrations/0007_resume_consent.d.ts +18 -0
- package/dist/esm/migrations/0007_resume_consent.d.ts.map +1 -0
- package/dist/esm/migrations/0007_resume_consent.js +27 -0
- package/dist/esm/migrations/0007_resume_consent.js.map +1 -0
- package/dist/esm/test/TestControl.d.ts +19 -0
- package/dist/esm/test/TestControl.d.ts.map +1 -0
- package/dist/esm/test/TestControl.js +30 -0
- package/dist/esm/test/TestControl.js.map +1 -0
- package/docs/README.md +189 -0
- package/docs/api.md +982 -0
- package/docs/concepts/authority.md +109 -0
- package/docs/concepts/cancellation.md +129 -0
- package/docs/concepts/lineage.md +132 -0
- package/docs/concepts/ownership.md +139 -0
- package/docs/concepts/projections.md +203 -0
- package/docs/concepts/receipts.md +128 -0
- package/docs/guides/approvals.md +284 -0
- package/docs/guides/cancel-and-resume.md +162 -0
- package/docs/guides/durable-storage.md +147 -0
- package/docs/guides/implement-an-executor.md +173 -0
- package/docs/guides/ingest-a-webhook.md +177 -0
- package/docs/guides/list-runs.md +160 -0
- package/docs/guides/monitor-runs.md +176 -0
- package/docs/guides/observe-health.md +147 -0
- package/docs/guides/postgres-tests.md +7 -0
- package/docs/guides/serve-over-rpc.md +220 -0
- package/docs/guides/signal-a-run.md +53 -0
- package/docs/guides/steer-a-run.md +138 -0
- package/docs/guides/store-credentials.md +164 -0
- package/docs/guides/testing.md +139 -0
- package/docs/guides/watch-a-run.md +154 -0
- package/docs/installation.md +106 -0
- package/docs/quickstart.md +163 -0
- package/docs/troubleshooting.md +208 -0
- package/package.json +405 -3
- package/src/ApprovalAuthority.ts +114 -0
- package/src/Cancellation.ts +172 -0
- package/src/Channels.ts +493 -0
- package/src/Control.ts +337 -0
- package/src/ControlClient.ts +319 -0
- package/src/ControlError.ts +378 -0
- package/src/ControlExecutor.ts +490 -0
- package/src/ControlFacts.ts +383 -0
- package/src/ControlLive.ts +2127 -0
- package/src/ControlRpcs.ts +443 -0
- package/src/ControlRuntime.ts +1601 -0
- package/src/ControlSchema.ts +1380 -0
- package/src/ControlServer.ts +182 -0
- package/src/Credential.ts +310 -0
- package/src/CredentialCipher.ts +110 -0
- package/src/CredentialStore.ts +152 -0
- package/src/DispatchReader.ts +122 -0
- package/src/Health.ts +591 -0
- package/src/JevSessionChecker.ts +127 -0
- package/src/Lineage.ts +203 -0
- package/src/Migrations.ts +56 -0
- package/src/Monitor.ts +600 -0
- package/src/ScopedToken.ts +306 -0
- package/src/SqlControlRuntime.ts +2478 -0
- package/src/SqlCredentialStore.ts +148 -0
- package/src/Steering.ts +96 -0
- package/src/SystemFlows.ts +225 -0
- package/src/WebCryptoCipher.ts +169 -0
- package/src/WebhookChannel.ts +166 -0
- package/src/index.ts +188 -0
- package/src/internal/MutationBoundary.ts +46 -0
- package/src/internal/activeFibers.ts +22 -0
- package/src/internal/issues.ts +40 -0
- package/src/internal/planning.ts +262 -0
- package/src/internal/sqlSchemaErrors.ts +37 -0
- package/src/migrations/0001_control_tables.ts +99 -0
- package/src/migrations/0002_run_keys.ts +23 -0
- package/src/migrations/0003_signal_commands.ts +27 -0
- package/src/migrations/0004_approval_decisions.ts +25 -0
- package/src/migrations/0005_signal_principals.ts +27 -0
- package/src/migrations/0006_run_principals.ts +31 -0
- package/src/migrations/0007_resume_consent.ts +28 -0
- package/src/test/TestControl.ts +47 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Acceptance port from the control plane into a real run executor.
|
|
3
|
+
*
|
|
4
|
+
* Governing contract: `docs/pages/control/index.md`.
|
|
5
|
+
*
|
|
6
|
+
* @since 0.1.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as Sha256 from "@smthrs/crypto/Sha256"
|
|
10
|
+
import * as DurableDeferred from "@smthrs/flow/DurableDeferred"
|
|
11
|
+
import type { ExecutionFact } from "@smthrs/journal"
|
|
12
|
+
import { Context, Effect, Layer, Option, Schema } from "effect"
|
|
13
|
+
import type { LaunchFailed, PersistenceError } from "./ControlError.ts"
|
|
14
|
+
import type { StoredPlan } from "./ControlRuntime.ts"
|
|
15
|
+
import type {
|
|
16
|
+
ApprovalTarget,
|
|
17
|
+
ExecutionBatch,
|
|
18
|
+
PendingWait,
|
|
19
|
+
Principal,
|
|
20
|
+
Receipt,
|
|
21
|
+
RunId,
|
|
22
|
+
RunSummary,
|
|
23
|
+
SignalPayload
|
|
24
|
+
} from "./ControlSchema.ts"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One stored plan and the run summary it is being started as.
|
|
28
|
+
*
|
|
29
|
+
* @category models
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
*/
|
|
32
|
+
export interface Launch {
|
|
33
|
+
readonly plan: StoredPlan
|
|
34
|
+
readonly run: RunSummary
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether the executor took the launch now or queued it.
|
|
39
|
+
*
|
|
40
|
+
* @category models
|
|
41
|
+
* @since 0.1.0
|
|
42
|
+
*/
|
|
43
|
+
export type Acceptance = "accepted" | "pending"
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* One run whose cancellation has to become durable on the engine row.
|
|
47
|
+
*
|
|
48
|
+
* @category models
|
|
49
|
+
* @since 0.1.0
|
|
50
|
+
*/
|
|
51
|
+
export interface CancelRequest {
|
|
52
|
+
readonly runId: RunId
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The engine row a cancel request arrived too late for.
|
|
57
|
+
*
|
|
58
|
+
* It carries the ENGINE's status rather than a bare marker because the control
|
|
59
|
+
* plane cannot read that row: in the shipped CLI the two `flows_runs` tables
|
|
60
|
+
* live in two files (`.flows/control.db` and `.flows/engine.db`), so the port
|
|
61
|
+
* is the only place the plane learns what actually became of the run.
|
|
62
|
+
*
|
|
63
|
+
* @category models
|
|
64
|
+
* @since 0.1.0
|
|
65
|
+
*/
|
|
66
|
+
export interface CancelTerminal {
|
|
67
|
+
readonly _tag: "Terminal"
|
|
68
|
+
readonly status: "completed" | "failed" | "cancelled"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* What the executor did with a cancel request.
|
|
73
|
+
*
|
|
74
|
+
* `recorded` means THIS call set `cancel_requested_at_ms` on the engine row, so
|
|
75
|
+
* the owning process stops the run at its next cancel poll whichever process
|
|
76
|
+
* asked. `already-requested` means the column was already set when this call
|
|
77
|
+
* arrived: the request is just as durable, and the cancellation it belongs to
|
|
78
|
+
* was somebody else's. `Control.cancel` keys its attribution event on that
|
|
79
|
+
* difference — the write is first-writer-wins and every repeat re-runs the
|
|
80
|
+
* whole mutation, so answering `recorded` to all of them left one journaled
|
|
81
|
+
* `control.run.cancel-requested` per ask for a single cancellation (release validation
|
|
82
|
+
* smoke: three `cancel` calls and one `down` against one parked run left four).
|
|
83
|
+
* `unknown` means this executor's engine has no row for the run at all, which
|
|
84
|
+
* is the honest answer for a run another composition launched into a database
|
|
85
|
+
* this one does not share. A {@link CancelTerminal} means the engine row has
|
|
86
|
+
* already settled, so there is nothing left to stop: recording intent on it
|
|
87
|
+
* would be a request no process can ever act on, and answering `recorded` let
|
|
88
|
+
* `Control.cancel` write a terminal control status the engine row does not have
|
|
89
|
+
* (triage B-11).
|
|
90
|
+
*
|
|
91
|
+
* @category models
|
|
92
|
+
* @since 0.1.0
|
|
93
|
+
*/
|
|
94
|
+
export type CancelRecord = "recorded" | "already-requested" | "unknown" | CancelTerminal
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* One parked run that has been told to resume.
|
|
98
|
+
*
|
|
99
|
+
* @category models
|
|
100
|
+
* @since 0.1.0
|
|
101
|
+
*/
|
|
102
|
+
export interface ResumeRequest {
|
|
103
|
+
readonly runId: RunId
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* What the executor did with a resume request.
|
|
108
|
+
*
|
|
109
|
+
* `resuming` means this executor hosts the run's execution, has taken the
|
|
110
|
+
* control row's fence, and is re-driving it: the caller can stop, and the run
|
|
111
|
+
* moves on its own. `unknown` means this executor drives no execution for the
|
|
112
|
+
* run, which is the answer an operator's CLI, a gateway, or any second process
|
|
113
|
+
* gives, and it is why the control plane records the delegation durably
|
|
114
|
+
* instead of treating its own journal entry as the delivery (triage B-15).
|
|
115
|
+
*
|
|
116
|
+
* @category models
|
|
117
|
+
* @since 0.1.0
|
|
118
|
+
*/
|
|
119
|
+
export type ResumeUptake = "resuming" | "unknown"
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* One signal to deliver to a run's open wait point.
|
|
123
|
+
*
|
|
124
|
+
* @category models
|
|
125
|
+
* @since 0.1.0
|
|
126
|
+
*/
|
|
127
|
+
export interface Signal {
|
|
128
|
+
/** Actor-scoped durable admission identity, present on control-plane delivery. */
|
|
129
|
+
readonly commandId?: string
|
|
130
|
+
/** An existing immutable binding survives a crash before acknowledgment. */
|
|
131
|
+
readonly token?: string | null
|
|
132
|
+
readonly runId: RunId
|
|
133
|
+
readonly signal: SignalPayload
|
|
134
|
+
/**
|
|
135
|
+
* Who admitted the signal. A human wait ({@link humanWaitReason}) completes
|
|
136
|
+
* only when `ApprovalAuthority` authorizes this principal to approve the
|
|
137
|
+
* wait's `Node` target; without one the signal is `refused`.
|
|
138
|
+
*/
|
|
139
|
+
readonly principal?: Principal | undefined
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* What the executor did with a signal.
|
|
144
|
+
*
|
|
145
|
+
* `delivered` means the `WaitFor` deferred the run is parked on was completed
|
|
146
|
+
* with the signal's payload and the run was woken. `no-match` means the run IS
|
|
147
|
+
* parked and is waiting for something else, which `Control.signal` refuses
|
|
148
|
+
* rather than recording a delivery nothing consumes. `unknown` means this
|
|
149
|
+
* executor is driving no execution for the run at all — another process may
|
|
150
|
+
* be, or none is yet — so the recorded message is the whole delivery and the
|
|
151
|
+
* executor that eventually drives the run replays it at its next start.
|
|
152
|
+
* `refused` means the signal named an open human wait and `ApprovalAuthority`
|
|
153
|
+
* did not authorize its principal to answer it: the wait stays open, and
|
|
154
|
+
* `Control.signal` fails `Unauthorized`.
|
|
155
|
+
*
|
|
156
|
+
* @category models
|
|
157
|
+
* @since 0.1.0
|
|
158
|
+
*/
|
|
159
|
+
export type SignalDelivery = "delivered" | "no-match" | "refused" | "unknown"
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Current engine observation. Missing execution is distinct from a running one.
|
|
163
|
+
* @category models
|
|
164
|
+
* @since 1.0.0
|
|
165
|
+
*/
|
|
166
|
+
export type ExecutionObservation =
|
|
167
|
+
| { readonly _tag: "Missing" }
|
|
168
|
+
| {
|
|
169
|
+
readonly _tag: "Observed"
|
|
170
|
+
readonly executionView?: ExecutionFact.View | undefined
|
|
171
|
+
readonly status: "accepted" | "running" | "parked" | "waiting-approval" | "completed" | "failed" | "cancelled"
|
|
172
|
+
readonly waitingReason?: string | undefined
|
|
173
|
+
readonly parentRunId?: string | undefined
|
|
174
|
+
readonly lineageId?: string | undefined
|
|
175
|
+
readonly roundOrdinal?: number | undefined
|
|
176
|
+
/**
|
|
177
|
+
* Open human waits anywhere in this execution's tree.
|
|
178
|
+
*
|
|
179
|
+
* The control plane cannot compute these. It keeps its own coordination
|
|
180
|
+
* copy of `flows_runs`, and the executions a flow spawns — where a nested
|
|
181
|
+
* `HumanTask` actually parks — exist only in the executor's database, with
|
|
182
|
+
* the edges that link them. `readExecution` is the one place that reads
|
|
183
|
+
* both, so it is where a run tree's open questions become visible to
|
|
184
|
+
* anything else.
|
|
185
|
+
*
|
|
186
|
+
* Absent when nothing in the tree is waiting on a person. `status` rolls
|
|
187
|
+
* up with them: an execution parked while a descendant holds a human wait
|
|
188
|
+
* is observed as `waiting-approval`.
|
|
189
|
+
*/
|
|
190
|
+
readonly pendingWaits?: ReadonlyArray<PendingWait> | undefined
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The waiting reason a human wait parks under.
|
|
195
|
+
*
|
|
196
|
+
* `HumanTask` and the agent's own `ask` both declare it
|
|
197
|
+
* (`@smthrs/flow` `FlowRuntime.annotateWaiting({ reason: "approval" })`), so
|
|
198
|
+
* it is the one value that separates "somebody has to answer this" from every
|
|
199
|
+
* other park.
|
|
200
|
+
*
|
|
201
|
+
* @category models
|
|
202
|
+
* @since 1.0.0
|
|
203
|
+
*/
|
|
204
|
+
export const humanWaitReason = "approval"
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The wait point a durable token addresses, as a person reads it.
|
|
208
|
+
*
|
|
209
|
+
* A `HumanTask` attempt is its own wait point named `WaitFor/<task>#<attempt>`
|
|
210
|
+
* (`@smthrs/flow` `HumanTask`), and a plain `WaitFor` gate is `WaitFor/<name>`.
|
|
211
|
+
* Splitting the two out of the token is what lets a client address an answer by
|
|
212
|
+
* the name a person was asked under rather than by a base64 blob, and lets an
|
|
213
|
+
* inbox say which of three attempts is open.
|
|
214
|
+
*
|
|
215
|
+
* A token this plane cannot parse names nothing extra; the token itself still
|
|
216
|
+
* travels, so the wait stays answerable.
|
|
217
|
+
*/
|
|
218
|
+
/** The engine's own decoder: tokens are UTF-8 JSON in base64url, which `atob` misreads. */
|
|
219
|
+
const decodeToken = Schema.decodeUnknownOption(DurableDeferred.TokenParsed.FromString)
|
|
220
|
+
|
|
221
|
+
const waitPointOf = (token: string): { readonly name?: string; readonly attempt?: number } => {
|
|
222
|
+
const decoded = decodeToken(token)
|
|
223
|
+
if (Option.isNone(decoded)) return {}
|
|
224
|
+
const deferredName = decoded.value.deferredName
|
|
225
|
+
if (!deferredName.startsWith("WaitFor/")) return {}
|
|
226
|
+
const point = deferredName.slice("WaitFor/".length)
|
|
227
|
+
const marker = point.lastIndexOf("#")
|
|
228
|
+
if (marker < 0) return { name: point }
|
|
229
|
+
const attempt = Number(point.slice(marker + 1))
|
|
230
|
+
return Number.isSafeInteger(attempt) && attempt > 0
|
|
231
|
+
? { name: point.slice(0, marker), attempt }
|
|
232
|
+
: { name: point }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* One parked execution as a {@link PendingWait}, or nothing when it is not a
|
|
237
|
+
* wait a person or a named control signal can end.
|
|
238
|
+
*
|
|
239
|
+
* Shared by the two readers that produce these rows — the control plane's own
|
|
240
|
+
* SQL runtime, when it shares a database with the engine, and the executor's
|
|
241
|
+
* observation port, when it does not — so a wait reads the same either way.
|
|
242
|
+
*
|
|
243
|
+
* @category constructors
|
|
244
|
+
* @since 1.0.0
|
|
245
|
+
*/
|
|
246
|
+
export const pendingWaitOf = (row: {
|
|
247
|
+
readonly runId: string
|
|
248
|
+
readonly flowId?: string | undefined
|
|
249
|
+
readonly reason: string
|
|
250
|
+
readonly token: string | null | undefined
|
|
251
|
+
readonly request?: unknown
|
|
252
|
+
readonly createdAt: number
|
|
253
|
+
}): PendingWait | undefined => {
|
|
254
|
+
if (row.token === null || row.token === undefined) return undefined
|
|
255
|
+
const point = waitPointOf(row.token)
|
|
256
|
+
if (row.reason !== humanWaitReason && !(row.reason === "event" && point.name !== undefined)) return undefined
|
|
257
|
+
return {
|
|
258
|
+
runId: row.runId,
|
|
259
|
+
reason: row.reason,
|
|
260
|
+
token: row.token,
|
|
261
|
+
tokenDigest: Sha256.digestSync(row.token),
|
|
262
|
+
createdAt: row.createdAt,
|
|
263
|
+
...(row.flowId === undefined ? {} : { flowId: row.flowId }),
|
|
264
|
+
...point,
|
|
265
|
+
...(row.request === undefined ? {} : { request: row.request as PendingWait["request"] })
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* The wait a `Node` approval target addresses, when it addresses one.
|
|
271
|
+
*
|
|
272
|
+
* Most gates are a capability the run wants allowed: the target's `digest` is
|
|
273
|
+
* the request's own digest, a decision grants or refuses it, and the control
|
|
274
|
+
* plane holds a registered token for it. A `HumanTask` gate has no such token
|
|
275
|
+
* — nothing registers one, because the run parked itself on a durable wait
|
|
276
|
+
* rather than asking the control plane for permission — and `lookupApproval`
|
|
277
|
+
* answers a target with no token by reporting the RUN as missing. That is what
|
|
278
|
+
* an operator saw when the app submitted an answer as an ordinary approval:
|
|
279
|
+
* `/control/RunNotFound` naming a run that was listed, rendered, and waiting
|
|
280
|
+
* (workspace 4bb93306, run-1).
|
|
281
|
+
*
|
|
282
|
+
* So a decision has to be able to tell the two apart from the payload alone.
|
|
283
|
+
* The approvals projection publishes a human wait with the durable wait token
|
|
284
|
+
* as the `digest` and the wait point's own name as the `requestId`, and a
|
|
285
|
+
* durable deferred token is self-describing: it decodes to a flow, an
|
|
286
|
+
* execution, and a deferred name under `WaitFor/`. Nothing else produces one,
|
|
287
|
+
* so a target carrying one is a question, and the name it answers is right
|
|
288
|
+
* there.
|
|
289
|
+
*
|
|
290
|
+
* @category constructors
|
|
291
|
+
* @since 1.0.0
|
|
292
|
+
*/
|
|
293
|
+
export const answerableWait = (
|
|
294
|
+
target: ApprovalTarget
|
|
295
|
+
): { readonly name: string; readonly token: string } | undefined => {
|
|
296
|
+
if (target._tag !== "Node") return undefined
|
|
297
|
+
const point = waitPointOf(target.digest)
|
|
298
|
+
return point.name === undefined ? undefined : { name: target.requestId, token: target.digest }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* The executor port: the control plane hands work over to a real run executor
|
|
303
|
+
* and learns only what the executor did with it.
|
|
304
|
+
*
|
|
305
|
+
* `launch` is acceptance. `requestCancel`, `deliverSignal`, and `resumeRun`
|
|
306
|
+
* are the three requests that have to reach the engine to mean anything: a
|
|
307
|
+
* cancel is durable on the engine row so that whichever process owns the run
|
|
308
|
+
* stops it, a signal completes the wait point a parked run is actually waiting
|
|
309
|
+
* on, and a resume re-drives the execution an approval decision unblocked.
|
|
310
|
+
* Without them the control plane records facts nobody reads — a cancel that
|
|
311
|
+
* answers `ClaimLost` to every process but the owner, a signal a parked run
|
|
312
|
+
* never sees, and a resume event published into an in-process hub no other
|
|
313
|
+
* process subscribes to (the release policy; triage B-10, B-13, B-15).
|
|
314
|
+
*
|
|
315
|
+
* @category services
|
|
316
|
+
* @since 0.1.0
|
|
317
|
+
*/
|
|
318
|
+
export interface Service {
|
|
319
|
+
/** Read from the executor's database, never the control coordination copy. */
|
|
320
|
+
readonly readExecution?: (runId: RunId) => Effect.Effect<ExecutionObservation, PersistenceError>
|
|
321
|
+
/** Exact observations scoped to the authorized control root; unrelated native rows are never exposed. */
|
|
322
|
+
readonly readExecutions?: (input: {
|
|
323
|
+
readonly runId: RunId
|
|
324
|
+
readonly executionIds: ReadonlyArray<string>
|
|
325
|
+
}) => Effect.Effect<ExecutionBatch, PersistenceError>
|
|
326
|
+
readonly launch: (input: Launch) => Effect.Effect<Acceptance, LaunchFailed>
|
|
327
|
+
/**
|
|
328
|
+
* Host-only close of a retained run, after its last native module completed.
|
|
329
|
+
* @since 1.0.0
|
|
330
|
+
*/
|
|
331
|
+
readonly requestComplete?:
|
|
332
|
+
| ((input: { readonly runId: RunId; readonly receiptId: string }) => Effect.Effect<Receipt, PersistenceError>)
|
|
333
|
+
| undefined
|
|
334
|
+
/**
|
|
335
|
+
* Records a cancellation on the engine row, durably, regardless of which
|
|
336
|
+
* process owns the run.
|
|
337
|
+
*/
|
|
338
|
+
readonly requestCancel: (input: CancelRequest) => Effect.Effect<CancelRecord, PersistenceError>
|
|
339
|
+
/**
|
|
340
|
+
* Completes the run's open `WaitFor` wait point with the signal's payload.
|
|
341
|
+
*/
|
|
342
|
+
readonly deliverSignal: (input: Signal) => Effect.Effect<SignalDelivery, PersistenceError>
|
|
343
|
+
/**
|
|
344
|
+
* Takes up a resume: claims the control row and re-drives the execution,
|
|
345
|
+
* when this executor is the one hosting it.
|
|
346
|
+
*/
|
|
347
|
+
readonly resumeRun: (input: ResumeRequest) => Effect.Effect<ResumeUptake, PersistenceError>
|
|
348
|
+
/**
|
|
349
|
+
* Finishes a PARKED execution whose cancellation is already durable.
|
|
350
|
+
*
|
|
351
|
+
* A park has no owner — that is what makes it resumable — so nothing is
|
|
352
|
+
* driving the run and nothing reads the request {@link requestCancel} wrote.
|
|
353
|
+
* The engine's parked-run sweep does, once per heartbeat, but a `smithers
|
|
354
|
+
* cancel` process writes the request at the very end of its life and exits
|
|
355
|
+
* before that tick: the release validation watched an engine row stay
|
|
356
|
+
* `suspended` with `cancel_requested_at_ms` set through six more commands
|
|
357
|
+
* and fifteen seconds, so `gc` collected the run in `control.db` and
|
|
358
|
+
* skipped it in `engine.db`.
|
|
359
|
+
*
|
|
360
|
+
* Called AFTER the cancel mutation commits, never inside it: driving a run
|
|
361
|
+
* re-enters the engine, whose writes would wait on the writer the
|
|
362
|
+
* mutation's transaction holds. An executor that does not host the run, or
|
|
363
|
+
* one whose run is not parked, does nothing and leaves the durable request
|
|
364
|
+
* standing for the host that does.
|
|
365
|
+
*/
|
|
366
|
+
readonly settleCancelledPark: (input: CancelRequest) => Effect.Effect<void, PersistenceError>
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* The {@link Service} tag.
|
|
371
|
+
*
|
|
372
|
+
* @category services
|
|
373
|
+
* @since 0.1.0
|
|
374
|
+
*/
|
|
375
|
+
export class ControlExecutor extends Context.Service<ControlExecutor, Service>()(
|
|
376
|
+
"/control/ControlExecutor"
|
|
377
|
+
) {}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Builds a {@link Service} from an implementation of its methods.
|
|
381
|
+
*
|
|
382
|
+
* @category constructors
|
|
383
|
+
* @since 0.1.0
|
|
384
|
+
*/
|
|
385
|
+
export const make = (implementation: Service): Service => ControlExecutor.of(implementation)
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* A {@link Service} that accepts every launch as `pending` and starts
|
|
389
|
+
* nothing. Overrides replace individual methods.
|
|
390
|
+
*
|
|
391
|
+
* @category constructors
|
|
392
|
+
* @since 0.1.0
|
|
393
|
+
*/
|
|
394
|
+
export const makeNoop = (overrides: Partial<Service> = {}): Service =>
|
|
395
|
+
make({
|
|
396
|
+
launch: Effect.fn("ControlExecutor.launch")(() => Effect.succeed("pending" as const)),
|
|
397
|
+
// An executor that starts nothing owns no engine row and no wait point, so
|
|
398
|
+
// it records no cancel and matches no signal. `Control.cancel` still writes
|
|
399
|
+
// its journal entry and interrupts a local fiber, and `Control.signal`
|
|
400
|
+
// still records the message: both are what the port's absence already did.
|
|
401
|
+
requestCancel: Effect.fn("ControlExecutor.requestCancel")(() => Effect.succeed("unknown" as const)),
|
|
402
|
+
deliverSignal: Effect.fn("ControlExecutor.deliverSignal")(() => Effect.succeed("unknown" as const)),
|
|
403
|
+
resumeRun: Effect.fn("ControlExecutor.resumeRun")(() => Effect.succeed("unknown" as const)),
|
|
404
|
+
settleCancelledPark: Effect.fn("ControlExecutor.settleCancelledPark")(() => Effect.void),
|
|
405
|
+
...overrides
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Provides {@link ControlExecutor} from an implementation.
|
|
410
|
+
*
|
|
411
|
+
* @category layers
|
|
412
|
+
* @since 0.1.0
|
|
413
|
+
*/
|
|
414
|
+
export const layer = (implementation: Service): Layer.Layer<ControlExecutor> =>
|
|
415
|
+
Layer.succeed(ControlExecutor)(make(implementation))
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* The same executor with methods that drive a run refused.
|
|
419
|
+
*
|
|
420
|
+
* A host composed to observe runs rather than drive them has no completion
|
|
421
|
+
* judge, so it must not be able to start or resume one: without this a verb
|
|
422
|
+
* misclassified as a read would admit a run and lose it at its first
|
|
423
|
+
* completion, which is the outcome the boot-time judge requirement exists to
|
|
424
|
+
* prevent. Reaching either method is a composition defect, not an operator
|
|
425
|
+
* error, so it dies rather than failing.
|
|
426
|
+
*
|
|
427
|
+
* Observation, cancellation and signal delivery stay live. A cancel and a
|
|
428
|
+
* signal record a durable request that the process driving the run picks up,
|
|
429
|
+
* so none of them reaches a completion, and refusing them would stop an
|
|
430
|
+
* operator ending a run on a host with no gateway key, which is the whole
|
|
431
|
+
* reason a read host exists.
|
|
432
|
+
*
|
|
433
|
+
* @category constructors
|
|
434
|
+
* @since 1.0.0
|
|
435
|
+
*/
|
|
436
|
+
export const makeObserving = (service: Service): Service => {
|
|
437
|
+
const refuse = (method: string) =>
|
|
438
|
+
Effect.die(
|
|
439
|
+
new Error(
|
|
440
|
+
`This host observes runs and drives none, so ControlExecutor.${method} is unreachable on it. ` +
|
|
441
|
+
"Compose it with startsRuns to start or resume a run."
|
|
442
|
+
)
|
|
443
|
+
)
|
|
444
|
+
return make({
|
|
445
|
+
...service,
|
|
446
|
+
requestComplete: undefined,
|
|
447
|
+
launch: Effect.fn("ControlExecutor.launch")(() => refuse("launch")),
|
|
448
|
+
resumeRun: Effect.fn("ControlExecutor.resumeRun")(() => refuse("resumeRun"))
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* An executor that only reads the engine: every method that drives or
|
|
454
|
+
* changes a run dies.
|
|
455
|
+
*
|
|
456
|
+
* A host composed over read-only stores carries it. Unlike
|
|
457
|
+
* {@link makeObserving}, it records no cancel and delivers no signal, because
|
|
458
|
+
* such a host can write neither. Without `readExecution` a listing answers
|
|
459
|
+
* from the control plane's coordination copy alone.
|
|
460
|
+
*
|
|
461
|
+
* @category constructors
|
|
462
|
+
* @since 1.0.0
|
|
463
|
+
*/
|
|
464
|
+
export const makeReadOnly = (
|
|
465
|
+
{ readExecution, readExecutions }: {
|
|
466
|
+
readonly readExecution?: Service["readExecution"]
|
|
467
|
+
readonly readExecutions?: Service["readExecutions"]
|
|
468
|
+
} = {}
|
|
469
|
+
): Service => {
|
|
470
|
+
const refuse = (method: string) =>
|
|
471
|
+
Effect.die(new Error(`This host only observes runs, so ControlExecutor.${method} is unreachable on it.`))
|
|
472
|
+
return make({
|
|
473
|
+
...(readExecution === undefined ? {} : { readExecution }),
|
|
474
|
+
...(readExecutions === undefined ? {} : { readExecutions }),
|
|
475
|
+
launch: Effect.fn("ControlExecutor.launch")(() => refuse("launch")),
|
|
476
|
+
requestCancel: Effect.fn("ControlExecutor.requestCancel")(() => refuse("requestCancel")),
|
|
477
|
+
deliverSignal: Effect.fn("ControlExecutor.deliverSignal")(() => refuse("deliverSignal")),
|
|
478
|
+
resumeRun: Effect.fn("ControlExecutor.resumeRun")(() => refuse("resumeRun")),
|
|
479
|
+
settleCancelledPark: Effect.fn("ControlExecutor.settleCancelledPark")(() => refuse("settleCancelledPark"))
|
|
480
|
+
})
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Provides {@link makeNoop}.
|
|
485
|
+
*
|
|
486
|
+
* @category layers
|
|
487
|
+
* @since 0.1.0
|
|
488
|
+
*/
|
|
489
|
+
export const layerNoop = (overrides: Partial<Service> = {}): Layer.Layer<ControlExecutor> =>
|
|
490
|
+
Layer.succeed(ControlExecutor)(makeNoop(overrides))
|