@smthrs/engine-store 0.0.0-stage → 1.0.0-rc.3
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 +247 -0
- package/LICENSE +21 -0
- package/README.md +146 -2
- package/dist/cjs/ArtifactGc.d.ts +235 -0
- package/dist/cjs/ArtifactGc.d.ts.map +1 -0
- package/dist/cjs/ArtifactGc.js +183 -0
- package/dist/cjs/ArtifactGc.js.map +7 -0
- package/dist/cjs/ArtifactSync.d.ts +164 -0
- package/dist/cjs/ArtifactSync.d.ts.map +1 -0
- package/dist/cjs/ArtifactSync.js +144 -0
- package/dist/cjs/ArtifactSync.js.map +7 -0
- package/dist/cjs/CacheSync.d.ts +112 -0
- package/dist/cjs/CacheSync.d.ts.map +1 -0
- package/dist/cjs/CacheSync.js +64 -0
- package/dist/cjs/CacheSync.js.map +7 -0
- package/dist/cjs/DeferredClockFold.d.ts +354 -0
- package/dist/cjs/DeferredClockFold.d.ts.map +1 -0
- package/dist/cjs/DeferredClockFold.js +335 -0
- package/dist/cjs/DeferredClockFold.js.map +7 -0
- package/dist/cjs/DisasterRecovery.d.ts +431 -0
- package/dist/cjs/DisasterRecovery.d.ts.map +1 -0
- package/dist/cjs/DisasterRecovery.js +453 -0
- package/dist/cjs/DisasterRecovery.js.map +7 -0
- package/dist/cjs/DurableEngineState.d.ts +776 -0
- package/dist/cjs/DurableEngineState.d.ts.map +1 -0
- package/dist/cjs/DurableEngineState.js +1536 -0
- package/dist/cjs/DurableEngineState.js.map +7 -0
- package/dist/cjs/EngineStore.d.ts +158 -0
- package/dist/cjs/EngineStore.d.ts.map +1 -0
- package/dist/cjs/EngineStore.js +398 -0
- package/dist/cjs/EngineStore.js.map +7 -0
- package/dist/cjs/EngineStoreMetrics.d.ts +276 -0
- package/dist/cjs/EngineStoreMetrics.d.ts.map +1 -0
- package/dist/cjs/EngineStoreMetrics.js +135 -0
- package/dist/cjs/EngineStoreMetrics.js.map +7 -0
- package/dist/cjs/Errors.d.ts +22 -0
- package/dist/cjs/Errors.d.ts.map +1 -0
- package/dist/cjs/Errors.js +39 -0
- package/dist/cjs/Errors.js.map +7 -0
- package/dist/cjs/EventTypes.d.ts +27 -0
- package/dist/cjs/EventTypes.d.ts.map +1 -0
- package/dist/cjs/EventTypes.js +37 -0
- package/dist/cjs/EventTypes.js.map +7 -0
- package/dist/cjs/ExecutionFacts.d.ts +59 -0
- package/dist/cjs/ExecutionFacts.d.ts.map +1 -0
- package/dist/cjs/ExecutionFacts.js +123 -0
- package/dist/cjs/ExecutionFacts.js.map +7 -0
- package/dist/cjs/ExecutionSnapshot.d.ts +117 -0
- package/dist/cjs/ExecutionSnapshot.d.ts.map +1 -0
- package/dist/cjs/ExecutionSnapshot.js +111 -0
- package/dist/cjs/ExecutionSnapshot.js.map +7 -0
- package/dist/cjs/Inconsistency.d.ts +154 -0
- package/dist/cjs/Inconsistency.d.ts.map +1 -0
- package/dist/cjs/Inconsistency.js +135 -0
- package/dist/cjs/Inconsistency.js.map +7 -0
- package/dist/cjs/Migrations.d.ts +56 -0
- package/dist/cjs/Migrations.d.ts.map +1 -0
- package/dist/cjs/Migrations.js +78 -0
- package/dist/cjs/Migrations.js.map +7 -0
- package/dist/cjs/OwnerIdentity.d.ts +91 -0
- package/dist/cjs/OwnerIdentity.d.ts.map +1 -0
- package/dist/cjs/OwnerIdentity.js +65 -0
- package/dist/cjs/OwnerIdentity.js.map +7 -0
- package/dist/cjs/PlanInputStore.d.ts +110 -0
- package/dist/cjs/PlanInputStore.d.ts.map +1 -0
- package/dist/cjs/PlanInputStore.js +224 -0
- package/dist/cjs/PlanInputStore.js.map +7 -0
- package/dist/cjs/PlanMergeStore.d.ts +113 -0
- package/dist/cjs/PlanMergeStore.d.ts.map +1 -0
- package/dist/cjs/PlanMergeStore.js +255 -0
- package/dist/cjs/PlanMergeStore.js.map +7 -0
- package/dist/cjs/PlanScheduler.d.ts +363 -0
- package/dist/cjs/PlanScheduler.d.ts.map +1 -0
- package/dist/cjs/PlanScheduler.js +1090 -0
- package/dist/cjs/PlanScheduler.js.map +7 -0
- package/dist/cjs/Reconciliation.d.ts +175 -0
- package/dist/cjs/Reconciliation.d.ts.map +1 -0
- package/dist/cjs/Reconciliation.js +119 -0
- package/dist/cjs/Reconciliation.js.map +7 -0
- package/dist/cjs/ReplayOnly.d.ts +91 -0
- package/dist/cjs/ReplayOnly.d.ts.map +1 -0
- package/dist/cjs/ReplayOnly.js +52 -0
- package/dist/cjs/ReplayOnly.js.map +7 -0
- package/dist/cjs/Retention.d.ts +113 -0
- package/dist/cjs/Retention.d.ts.map +1 -0
- package/dist/cjs/Retention.js +99 -0
- package/dist/cjs/Retention.js.map +7 -0
- package/dist/cjs/RunCatalogRead.d.ts +17 -0
- package/dist/cjs/RunCatalogRead.d.ts.map +1 -0
- package/dist/cjs/RunCatalogRead.js +33 -0
- package/dist/cjs/RunCatalogRead.js.map +7 -0
- package/dist/cjs/RunChangeFeed.d.ts +75 -0
- package/dist/cjs/RunChangeFeed.d.ts.map +1 -0
- package/dist/cjs/RunChangeFeed.js +96 -0
- package/dist/cjs/RunChangeFeed.js.map +7 -0
- package/dist/cjs/RunState.d.ts +116 -0
- package/dist/cjs/RunState.d.ts.map +1 -0
- package/dist/cjs/RunState.js +76 -0
- package/dist/cjs/RunState.js.map +7 -0
- package/dist/cjs/Selection.d.ts +495 -0
- package/dist/cjs/Selection.d.ts.map +1 -0
- package/dist/cjs/Selection.js +288 -0
- package/dist/cjs/Selection.js.map +7 -0
- package/dist/cjs/SelectionStore.d.ts +141 -0
- package/dist/cjs/SelectionStore.d.ts.map +1 -0
- package/dist/cjs/SelectionStore.js +229 -0
- package/dist/cjs/SelectionStore.js.map +7 -0
- package/dist/cjs/StepBoundary.d.ts +501 -0
- package/dist/cjs/StepBoundary.d.ts.map +1 -0
- package/dist/cjs/StepBoundary.js +619 -0
- package/dist/cjs/StepBoundary.js.map +7 -0
- package/dist/cjs/StepCacheFold.d.ts +148 -0
- package/dist/cjs/StepCacheFold.d.ts.map +1 -0
- package/dist/cjs/StepCacheFold.js +131 -0
- package/dist/cjs/StepCacheFold.js.map +7 -0
- package/dist/cjs/StepSandbox.d.ts +78 -0
- package/dist/cjs/StepSandbox.d.ts.map +1 -0
- package/dist/cjs/StepSandbox.js +75 -0
- package/dist/cjs/StepSandbox.js.map +7 -0
- package/dist/cjs/WakeBus.d.ts +116 -0
- package/dist/cjs/WakeBus.d.ts.map +1 -0
- package/dist/cjs/WakeBus.js +89 -0
- package/dist/cjs/WakeBus.js.map +7 -0
- package/dist/cjs/WorkspaceSandbox.d.ts +619 -0
- package/dist/cjs/WorkspaceSandbox.d.ts.map +1 -0
- package/dist/cjs/WorkspaceSandbox.js +711 -0
- package/dist/cjs/WorkspaceSandbox.js.map +7 -0
- package/dist/cjs/index.d.ts +157 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +95 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ActionPersistence.d.ts +276 -0
- package/dist/cjs/internal/ActionPersistence.d.ts.map +1 -0
- package/dist/cjs/internal/ActionPersistence.js +1395 -0
- package/dist/cjs/internal/ActionPersistence.js.map +7 -0
- package/dist/cjs/internal/ArtifactRoots.d.ts +38 -0
- package/dist/cjs/internal/ArtifactRoots.d.ts.map +1 -0
- package/dist/cjs/internal/ArtifactRoots.js +89 -0
- package/dist/cjs/internal/ArtifactRoots.js.map +7 -0
- package/dist/cjs/internal/AttemptAdmission.d.ts +53 -0
- package/dist/cjs/internal/AttemptAdmission.d.ts.map +1 -0
- package/dist/cjs/internal/AttemptAdmission.js +52 -0
- package/dist/cjs/internal/AttemptAdmission.js.map +7 -0
- package/dist/cjs/internal/AttemptLifecycle.d.ts +245 -0
- package/dist/cjs/internal/AttemptLifecycle.d.ts.map +1 -0
- package/dist/cjs/internal/AttemptLifecycle.js +112 -0
- package/dist/cjs/internal/AttemptLifecycle.js.map +7 -0
- package/dist/cjs/internal/AttemptProbe.d.ts +53 -0
- package/dist/cjs/internal/AttemptProbe.d.ts.map +1 -0
- package/dist/cjs/internal/AttemptProbe.js +66 -0
- package/dist/cjs/internal/AttemptProbe.js.map +7 -0
- package/dist/cjs/internal/CacheAdmission.d.ts +79 -0
- package/dist/cjs/internal/CacheAdmission.d.ts.map +1 -0
- package/dist/cjs/internal/CacheAdmission.js +71 -0
- package/dist/cjs/internal/CacheAdmission.js.map +7 -0
- package/dist/cjs/internal/CacheAgeHistory.d.ts +27 -0
- package/dist/cjs/internal/CacheAgeHistory.d.ts.map +1 -0
- package/dist/cjs/internal/CacheAgeHistory.js +61 -0
- package/dist/cjs/internal/CacheAgeHistory.js.map +7 -0
- package/dist/cjs/internal/CacheAgeVerdicts.d.ts +15 -0
- package/dist/cjs/internal/CacheAgeVerdicts.d.ts.map +1 -0
- package/dist/cjs/internal/CacheAgeVerdicts.js +73 -0
- package/dist/cjs/internal/CacheAgeVerdicts.js.map +7 -0
- package/dist/cjs/internal/CacheOutputPolicy.d.ts +24 -0
- package/dist/cjs/internal/CacheOutputPolicy.d.ts.map +1 -0
- package/dist/cjs/internal/CacheOutputPolicy.js +75 -0
- package/dist/cjs/internal/CacheOutputPolicy.js.map +7 -0
- package/dist/cjs/internal/CachePublication.d.ts +97 -0
- package/dist/cjs/internal/CachePublication.d.ts.map +1 -0
- package/dist/cjs/internal/CachePublication.js +80 -0
- package/dist/cjs/internal/CachePublication.js.map +7 -0
- package/dist/cjs/internal/CallFacts.d.ts +15 -0
- package/dist/cjs/internal/CallFacts.d.ts.map +1 -0
- package/dist/cjs/internal/CallFacts.js +97 -0
- package/dist/cjs/internal/CallFacts.js.map +7 -0
- package/dist/cjs/internal/CopiedRecord.d.ts +61 -0
- package/dist/cjs/internal/CopiedRecord.d.ts.map +1 -0
- package/dist/cjs/internal/CopiedRecord.js +134 -0
- package/dist/cjs/internal/CopiedRecord.js.map +7 -0
- package/dist/cjs/internal/DeferredPersistence.d.ts +108 -0
- package/dist/cjs/internal/DeferredPersistence.d.ts.map +1 -0
- package/dist/cjs/internal/DeferredPersistence.js +270 -0
- package/dist/cjs/internal/DeferredPersistence.js.map +7 -0
- package/dist/cjs/internal/EffectRecords.d.ts +92 -0
- package/dist/cjs/internal/EffectRecords.d.ts.map +1 -0
- package/dist/cjs/internal/EffectRecords.js +63 -0
- package/dist/cjs/internal/EffectRecords.js.map +7 -0
- package/dist/cjs/internal/EngineJj.d.ts +30 -0
- package/dist/cjs/internal/EngineJj.d.ts.map +1 -0
- package/dist/cjs/internal/EngineJj.js +42 -0
- package/dist/cjs/internal/EngineJj.js.map +7 -0
- package/dist/cjs/internal/EngineStateSchema.d.ts +68 -0
- package/dist/cjs/internal/EngineStateSchema.d.ts.map +1 -0
- package/dist/cjs/internal/EngineStateSchema.js +58 -0
- package/dist/cjs/internal/EngineStateSchema.js.map +7 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.d.ts +162 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.d.ts.map +1 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.js +199 -0
- package/dist/cjs/internal/ExecutionSnapshotRead.js.map +7 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.d.ts +39 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.d.ts.map +1 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.js +105 -0
- package/dist/cjs/internal/ExecutionSnapshotRelations.js.map +7 -0
- package/dist/cjs/internal/ExitEncoding.d.ts +180 -0
- package/dist/cjs/internal/ExitEncoding.d.ts.map +1 -0
- package/dist/cjs/internal/ExitEncoding.js +236 -0
- package/dist/cjs/internal/ExitEncoding.js.map +7 -0
- package/dist/cjs/internal/FileBoundarySnapshot.d.ts +14 -0
- package/dist/cjs/internal/FileBoundarySnapshot.d.ts.map +1 -0
- package/dist/cjs/internal/FileBoundarySnapshot.js +51 -0
- package/dist/cjs/internal/FileBoundarySnapshot.js.map +7 -0
- package/dist/cjs/internal/FileEnumeration.d.ts +127 -0
- package/dist/cjs/internal/FileEnumeration.d.ts.map +1 -0
- package/dist/cjs/internal/FileEnumeration.js +194 -0
- package/dist/cjs/internal/FileEnumeration.js.map +7 -0
- package/dist/cjs/internal/HostReflection.d.ts +80 -0
- package/dist/cjs/internal/HostReflection.d.ts.map +1 -0
- package/dist/cjs/internal/HostReflection.js +43 -0
- package/dist/cjs/internal/HostReflection.js.map +7 -0
- package/dist/cjs/internal/JournalRecords.d.ts +325 -0
- package/dist/cjs/internal/JournalRecords.d.ts.map +1 -0
- package/dist/cjs/internal/JournalRecords.js +122 -0
- package/dist/cjs/internal/JournalRecords.js.map +7 -0
- package/dist/cjs/internal/NodeJournal.d.ts +157 -0
- package/dist/cjs/internal/NodeJournal.d.ts.map +1 -0
- package/dist/cjs/internal/NodeJournal.js +192 -0
- package/dist/cjs/internal/NodeJournal.js.map +7 -0
- package/dist/cjs/internal/Ordering.d.ts +14 -0
- package/dist/cjs/internal/Ordering.d.ts.map +1 -0
- package/dist/cjs/internal/Ordering.js +25 -0
- package/dist/cjs/internal/Ordering.js.map +7 -0
- package/dist/cjs/internal/PlanInputs.d.ts +14 -0
- package/dist/cjs/internal/PlanInputs.d.ts.map +1 -0
- package/dist/cjs/internal/PlanInputs.js +78 -0
- package/dist/cjs/internal/PlanInputs.js.map +7 -0
- package/dist/cjs/internal/PlanMerges.d.ts +178 -0
- package/dist/cjs/internal/PlanMerges.d.ts.map +1 -0
- package/dist/cjs/internal/PlanMerges.js +125 -0
- package/dist/cjs/internal/PlanMerges.js.map +7 -0
- package/dist/cjs/internal/ProvenanceSlot.d.ts +30 -0
- package/dist/cjs/internal/ProvenanceSlot.d.ts.map +1 -0
- package/dist/cjs/internal/ProvenanceSlot.js +53 -0
- package/dist/cjs/internal/ProvenanceSlot.js.map +7 -0
- package/dist/cjs/internal/ResultEnvelope.d.ts +88 -0
- package/dist/cjs/internal/ResultEnvelope.d.ts.map +1 -0
- package/dist/cjs/internal/ResultEnvelope.js +90 -0
- package/dist/cjs/internal/ResultEnvelope.js.map +7 -0
- package/dist/cjs/internal/RetentionOps.d.ts +414 -0
- package/dist/cjs/internal/RetentionOps.d.ts.map +1 -0
- package/dist/cjs/internal/RetentionOps.js +354 -0
- package/dist/cjs/internal/RetentionOps.js.map +7 -0
- package/dist/cjs/internal/RunCatalogOps.d.ts +118 -0
- package/dist/cjs/internal/RunCatalogOps.d.ts.map +1 -0
- package/dist/cjs/internal/RunCatalogOps.js +85 -0
- package/dist/cjs/internal/RunCatalogOps.js.map +7 -0
- package/dist/cjs/internal/RunCoordinator.d.ts +78 -0
- package/dist/cjs/internal/RunCoordinator.d.ts.map +1 -0
- package/dist/cjs/internal/RunCoordinator.js +123 -0
- package/dist/cjs/internal/RunCoordinator.js.map +7 -0
- package/dist/cjs/internal/RunDriver.d.ts +192 -0
- package/dist/cjs/internal/RunDriver.d.ts.map +1 -0
- package/dist/cjs/internal/RunDriver.js +1584 -0
- package/dist/cjs/internal/RunDriver.js.map +7 -0
- package/dist/cjs/internal/RunLifecycle.d.ts +22 -0
- package/dist/cjs/internal/RunLifecycle.d.ts.map +1 -0
- package/dist/cjs/internal/RunLifecycle.js +63 -0
- package/dist/cjs/internal/RunLifecycle.js.map +7 -0
- package/dist/cjs/internal/RunListing.d.ts +78 -0
- package/dist/cjs/internal/RunListing.d.ts.map +1 -0
- package/dist/cjs/internal/RunListing.js +171 -0
- package/dist/cjs/internal/RunListing.js.map +7 -0
- package/dist/cjs/internal/RunListingError.d.ts +35 -0
- package/dist/cjs/internal/RunListingError.d.ts.map +1 -0
- package/dist/cjs/internal/RunListingError.js +47 -0
- package/dist/cjs/internal/RunListingError.js.map +7 -0
- package/dist/cjs/internal/RuntimeGraph.d.ts +73 -0
- package/dist/cjs/internal/RuntimeGraph.d.ts.map +1 -0
- package/dist/cjs/internal/RuntimeGraph.js +180 -0
- package/dist/cjs/internal/RuntimeGraph.js.map +7 -0
- package/dist/cjs/internal/SandboxedExecution.d.ts +80 -0
- package/dist/cjs/internal/SandboxedExecution.d.ts.map +1 -0
- package/dist/cjs/internal/SandboxedExecution.js +104 -0
- package/dist/cjs/internal/SandboxedExecution.js.map +7 -0
- package/dist/cjs/internal/StateTransaction.d.ts +44 -0
- package/dist/cjs/internal/StateTransaction.d.ts.map +1 -0
- package/dist/cjs/internal/StateTransaction.js +52 -0
- package/dist/cjs/internal/StateTransaction.js.map +7 -0
- package/dist/cjs/internal/StepFacts.d.ts +14 -0
- package/dist/cjs/internal/StepFacts.d.ts.map +1 -0
- package/dist/cjs/internal/StepFacts.js +58 -0
- package/dist/cjs/internal/StepFacts.js.map +7 -0
- package/dist/cjs/internal/TypedEvents.d.ts +14 -0
- package/dist/cjs/internal/TypedEvents.d.ts.map +1 -0
- package/dist/cjs/internal/TypedEvents.js +37 -0
- package/dist/cjs/internal/TypedEvents.js.map +7 -0
- package/dist/cjs/migrations/0001_initial.d.ts +17 -0
- package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
- package/dist/cjs/migrations/0001_initial.js +61 -0
- package/dist/cjs/migrations/0001_initial.js.map +7 -0
- package/dist/cjs/migrations/0002_selection_store.d.ts +15 -0
- package/dist/cjs/migrations/0002_selection_store.d.ts.map +1 -0
- package/dist/cjs/migrations/0002_selection_store.js +56 -0
- package/dist/cjs/migrations/0002_selection_store.js.map +7 -0
- package/dist/cjs/migrations/0003_plan_inputs.d.ts +13 -0
- package/dist/cjs/migrations/0003_plan_inputs.d.ts.map +1 -0
- package/dist/cjs/migrations/0003_plan_inputs.js +90 -0
- package/dist/cjs/migrations/0003_plan_inputs.js.map +7 -0
- package/dist/cjs/migrations/0004_plan_environment.d.ts +14 -0
- package/dist/cjs/migrations/0004_plan_environment.d.ts.map +1 -0
- package/dist/cjs/migrations/0004_plan_environment.js +56 -0
- package/dist/cjs/migrations/0004_plan_environment.js.map +7 -0
- package/dist/cjs/migrations/0005_plan_merges.d.ts +14 -0
- package/dist/cjs/migrations/0005_plan_merges.d.ts.map +1 -0
- package/dist/cjs/migrations/0005_plan_merges.js +82 -0
- package/dist/cjs/migrations/0005_plan_merges.js.map +7 -0
- package/dist/cjs/migrations/0006_execution_listing.d.ts +15 -0
- package/dist/cjs/migrations/0006_execution_listing.d.ts.map +1 -0
- package/dist/cjs/migrations/0006_execution_listing.js +101 -0
- package/dist/cjs/migrations/0006_execution_listing.js.map +7 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.d.ts +15 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.d.ts.map +1 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.js +40 -0
- package/dist/cjs/migrations/0007_run_parent_sequence.js.map +7 -0
- package/dist/cjs/migrations/0008_deferred_consumption.d.ts +16 -0
- package/dist/cjs/migrations/0008_deferred_consumption.d.ts.map +1 -0
- package/dist/cjs/migrations/0008_deferred_consumption.js +45 -0
- package/dist/cjs/migrations/0008_deferred_consumption.js.map +7 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.d.ts +24 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.d.ts.map +1 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.js +141 -0
- package/dist/cjs/migrations/0009_deferred_clock_facts.js.map +7 -0
- package/dist/cjs/migrations/0010_pending_cancellation.d.ts +15 -0
- package/dist/cjs/migrations/0010_pending_cancellation.d.ts.map +1 -0
- package/dist/cjs/migrations/0010_pending_cancellation.js +41 -0
- package/dist/cjs/migrations/0010_pending_cancellation.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestStores.d.ts +89 -0
- package/dist/cjs/test/TestStores.d.ts.map +1 -0
- package/dist/cjs/test/TestStores.js +98 -0
- package/dist/cjs/test/TestStores.js.map +7 -0
- package/dist/esm/ArtifactGc.d.ts +235 -0
- package/dist/esm/ArtifactGc.d.ts.map +1 -0
- package/dist/esm/ArtifactGc.js +268 -0
- package/dist/esm/ArtifactGc.js.map +1 -0
- package/dist/esm/ArtifactSync.d.ts +164 -0
- package/dist/esm/ArtifactSync.d.ts.map +1 -0
- package/dist/esm/ArtifactSync.js +176 -0
- package/dist/esm/ArtifactSync.js.map +1 -0
- package/dist/esm/CacheSync.d.ts +112 -0
- package/dist/esm/CacheSync.d.ts.map +1 -0
- package/dist/esm/CacheSync.js +94 -0
- package/dist/esm/CacheSync.js.map +1 -0
- package/dist/esm/DeferredClockFold.d.ts +354 -0
- package/dist/esm/DeferredClockFold.d.ts.map +1 -0
- package/dist/esm/DeferredClockFold.js +472 -0
- package/dist/esm/DeferredClockFold.js.map +1 -0
- package/dist/esm/DisasterRecovery.d.ts +431 -0
- package/dist/esm/DisasterRecovery.d.ts.map +1 -0
- package/dist/esm/DisasterRecovery.js +523 -0
- package/dist/esm/DisasterRecovery.js.map +1 -0
- package/dist/esm/DurableEngineState.d.ts +776 -0
- package/dist/esm/DurableEngineState.d.ts.map +1 -0
- package/dist/esm/DurableEngineState.js +1648 -0
- package/dist/esm/DurableEngineState.js.map +1 -0
- package/dist/esm/EngineStore.d.ts +158 -0
- package/dist/esm/EngineStore.d.ts.map +1 -0
- package/dist/esm/EngineStore.js +403 -0
- package/dist/esm/EngineStore.js.map +1 -0
- package/dist/esm/EngineStoreMetrics.d.ts +276 -0
- package/dist/esm/EngineStoreMetrics.d.ts.map +1 -0
- package/dist/esm/EngineStoreMetrics.js +260 -0
- package/dist/esm/EngineStoreMetrics.js.map +1 -0
- package/dist/esm/Errors.d.ts +22 -0
- package/dist/esm/Errors.d.ts.map +1 -0
- package/dist/esm/Errors.js +22 -0
- package/dist/esm/Errors.js.map +1 -0
- package/dist/esm/EventTypes.d.ts +27 -0
- package/dist/esm/EventTypes.d.ts.map +1 -0
- package/dist/esm/EventTypes.js +27 -0
- package/dist/esm/EventTypes.js.map +1 -0
- package/dist/esm/ExecutionFacts.d.ts +59 -0
- package/dist/esm/ExecutionFacts.d.ts.map +1 -0
- package/dist/esm/ExecutionFacts.js +96 -0
- package/dist/esm/ExecutionFacts.js.map +1 -0
- package/dist/esm/ExecutionSnapshot.d.ts +117 -0
- package/dist/esm/ExecutionSnapshot.d.ts.map +1 -0
- package/dist/esm/ExecutionSnapshot.js +95 -0
- package/dist/esm/ExecutionSnapshot.js.map +1 -0
- package/dist/esm/Inconsistency.d.ts +154 -0
- package/dist/esm/Inconsistency.d.ts.map +1 -0
- package/dist/esm/Inconsistency.js +133 -0
- package/dist/esm/Inconsistency.js.map +1 -0
- package/dist/esm/Migrations.d.ts +56 -0
- package/dist/esm/Migrations.d.ts.map +1 -0
- package/dist/esm/Migrations.js +91 -0
- package/dist/esm/Migrations.js.map +1 -0
- package/dist/esm/OwnerIdentity.d.ts +91 -0
- package/dist/esm/OwnerIdentity.d.ts.map +1 -0
- package/dist/esm/OwnerIdentity.js +99 -0
- package/dist/esm/OwnerIdentity.js.map +1 -0
- package/dist/esm/PlanInputStore.d.ts +110 -0
- package/dist/esm/PlanInputStore.d.ts.map +1 -0
- package/dist/esm/PlanInputStore.js +207 -0
- package/dist/esm/PlanInputStore.js.map +1 -0
- package/dist/esm/PlanMergeStore.d.ts +113 -0
- package/dist/esm/PlanMergeStore.d.ts.map +1 -0
- package/dist/esm/PlanMergeStore.js +244 -0
- package/dist/esm/PlanMergeStore.js.map +1 -0
- package/dist/esm/PlanScheduler.d.ts +363 -0
- package/dist/esm/PlanScheduler.d.ts.map +1 -0
- package/dist/esm/PlanScheduler.js +1321 -0
- package/dist/esm/PlanScheduler.js.map +1 -0
- package/dist/esm/Reconciliation.d.ts +175 -0
- package/dist/esm/Reconciliation.d.ts.map +1 -0
- package/dist/esm/Reconciliation.js +175 -0
- package/dist/esm/Reconciliation.js.map +1 -0
- package/dist/esm/ReplayOnly.d.ts +91 -0
- package/dist/esm/ReplayOnly.d.ts.map +1 -0
- package/dist/esm/ReplayOnly.js +59 -0
- package/dist/esm/ReplayOnly.js.map +1 -0
- package/dist/esm/Retention.d.ts +113 -0
- package/dist/esm/Retention.d.ts.map +1 -0
- package/dist/esm/Retention.js +139 -0
- package/dist/esm/Retention.js.map +1 -0
- package/dist/esm/RunCatalogRead.d.ts +17 -0
- package/dist/esm/RunCatalogRead.d.ts.map +1 -0
- package/dist/esm/RunCatalogRead.js +17 -0
- package/dist/esm/RunCatalogRead.js.map +1 -0
- package/dist/esm/RunChangeFeed.d.ts +75 -0
- package/dist/esm/RunChangeFeed.d.ts.map +1 -0
- package/dist/esm/RunChangeFeed.js +81 -0
- package/dist/esm/RunChangeFeed.js.map +1 -0
- package/dist/esm/RunState.d.ts +116 -0
- package/dist/esm/RunState.d.ts.map +1 -0
- package/dist/esm/RunState.js +109 -0
- package/dist/esm/RunState.js.map +1 -0
- package/dist/esm/Selection.d.ts +495 -0
- package/dist/esm/Selection.d.ts.map +1 -0
- package/dist/esm/Selection.js +482 -0
- package/dist/esm/Selection.js.map +1 -0
- package/dist/esm/SelectionStore.d.ts +141 -0
- package/dist/esm/SelectionStore.d.ts.map +1 -0
- package/dist/esm/SelectionStore.js +215 -0
- package/dist/esm/SelectionStore.js.map +1 -0
- package/dist/esm/StepBoundary.d.ts +501 -0
- package/dist/esm/StepBoundary.d.ts.map +1 -0
- package/dist/esm/StepBoundary.js +919 -0
- package/dist/esm/StepBoundary.js.map +1 -0
- package/dist/esm/StepCacheFold.d.ts +148 -0
- package/dist/esm/StepCacheFold.d.ts.map +1 -0
- package/dist/esm/StepCacheFold.js +201 -0
- package/dist/esm/StepCacheFold.js.map +1 -0
- package/dist/esm/StepSandbox.d.ts +78 -0
- package/dist/esm/StepSandbox.d.ts.map +1 -0
- package/dist/esm/StepSandbox.js +70 -0
- package/dist/esm/StepSandbox.js.map +1 -0
- package/dist/esm/WakeBus.d.ts +116 -0
- package/dist/esm/WakeBus.d.ts.map +1 -0
- package/dist/esm/WakeBus.js +121 -0
- package/dist/esm/WakeBus.js.map +1 -0
- package/dist/esm/WorkspaceSandbox.d.ts +619 -0
- package/dist/esm/WorkspaceSandbox.d.ts.map +1 -0
- package/dist/esm/WorkspaceSandbox.js +985 -0
- package/dist/esm/WorkspaceSandbox.js.map +1 -0
- package/dist/esm/index.d.ts +157 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +157 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ActionPersistence.d.ts +276 -0
- package/dist/esm/internal/ActionPersistence.d.ts.map +1 -0
- package/dist/esm/internal/ActionPersistence.js +2306 -0
- package/dist/esm/internal/ActionPersistence.js.map +1 -0
- package/dist/esm/internal/ArtifactRoots.d.ts +38 -0
- package/dist/esm/internal/ArtifactRoots.d.ts.map +1 -0
- package/dist/esm/internal/ArtifactRoots.js +76 -0
- package/dist/esm/internal/ArtifactRoots.js.map +1 -0
- package/dist/esm/internal/AttemptAdmission.d.ts +53 -0
- package/dist/esm/internal/AttemptAdmission.d.ts.map +1 -0
- package/dist/esm/internal/AttemptAdmission.js +52 -0
- package/dist/esm/internal/AttemptAdmission.js.map +1 -0
- package/dist/esm/internal/AttemptLifecycle.d.ts +245 -0
- package/dist/esm/internal/AttemptLifecycle.d.ts.map +1 -0
- package/dist/esm/internal/AttemptLifecycle.js +104 -0
- package/dist/esm/internal/AttemptLifecycle.js.map +1 -0
- package/dist/esm/internal/AttemptProbe.d.ts +53 -0
- package/dist/esm/internal/AttemptProbe.d.ts.map +1 -0
- package/dist/esm/internal/AttemptProbe.js +83 -0
- package/dist/esm/internal/AttemptProbe.js.map +1 -0
- package/dist/esm/internal/CacheAdmission.d.ts +79 -0
- package/dist/esm/internal/CacheAdmission.d.ts.map +1 -0
- package/dist/esm/internal/CacheAdmission.js +72 -0
- package/dist/esm/internal/CacheAdmission.js.map +1 -0
- package/dist/esm/internal/CacheAgeHistory.d.ts +27 -0
- package/dist/esm/internal/CacheAgeHistory.d.ts.map +1 -0
- package/dist/esm/internal/CacheAgeHistory.js +49 -0
- package/dist/esm/internal/CacheAgeHistory.js.map +1 -0
- package/dist/esm/internal/CacheAgeVerdicts.d.ts +15 -0
- package/dist/esm/internal/CacheAgeVerdicts.d.ts.map +1 -0
- package/dist/esm/internal/CacheAgeVerdicts.js +54 -0
- package/dist/esm/internal/CacheAgeVerdicts.js.map +1 -0
- package/dist/esm/internal/CacheOutputPolicy.d.ts +24 -0
- package/dist/esm/internal/CacheOutputPolicy.d.ts.map +1 -0
- package/dist/esm/internal/CacheOutputPolicy.js +73 -0
- package/dist/esm/internal/CacheOutputPolicy.js.map +1 -0
- package/dist/esm/internal/CachePublication.d.ts +97 -0
- package/dist/esm/internal/CachePublication.d.ts.map +1 -0
- package/dist/esm/internal/CachePublication.js +116 -0
- package/dist/esm/internal/CachePublication.js.map +1 -0
- package/dist/esm/internal/CallFacts.d.ts +15 -0
- package/dist/esm/internal/CallFacts.d.ts.map +1 -0
- package/dist/esm/internal/CallFacts.js +83 -0
- package/dist/esm/internal/CallFacts.js.map +1 -0
- package/dist/esm/internal/CopiedRecord.d.ts +61 -0
- package/dist/esm/internal/CopiedRecord.d.ts.map +1 -0
- package/dist/esm/internal/CopiedRecord.js +165 -0
- package/dist/esm/internal/CopiedRecord.js.map +1 -0
- package/dist/esm/internal/DeferredPersistence.d.ts +108 -0
- package/dist/esm/internal/DeferredPersistence.d.ts.map +1 -0
- package/dist/esm/internal/DeferredPersistence.js +240 -0
- package/dist/esm/internal/DeferredPersistence.js.map +1 -0
- package/dist/esm/internal/EffectRecords.d.ts +92 -0
- package/dist/esm/internal/EffectRecords.d.ts.map +1 -0
- package/dist/esm/internal/EffectRecords.js +94 -0
- package/dist/esm/internal/EffectRecords.js.map +1 -0
- package/dist/esm/internal/EngineJj.d.ts +30 -0
- package/dist/esm/internal/EngineJj.d.ts.map +1 -0
- package/dist/esm/internal/EngineJj.js +29 -0
- package/dist/esm/internal/EngineJj.js.map +1 -0
- package/dist/esm/internal/EngineStateSchema.d.ts +68 -0
- package/dist/esm/internal/EngineStateSchema.d.ts.map +1 -0
- package/dist/esm/internal/EngineStateSchema.js +53 -0
- package/dist/esm/internal/EngineStateSchema.js.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRead.d.ts +162 -0
- package/dist/esm/internal/ExecutionSnapshotRead.d.ts.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRead.js +218 -0
- package/dist/esm/internal/ExecutionSnapshotRead.js.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.d.ts +39 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.d.ts.map +1 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.js +81 -0
- package/dist/esm/internal/ExecutionSnapshotRelations.js.map +1 -0
- package/dist/esm/internal/ExitEncoding.d.ts +180 -0
- package/dist/esm/internal/ExitEncoding.d.ts.map +1 -0
- package/dist/esm/internal/ExitEncoding.js +361 -0
- package/dist/esm/internal/ExitEncoding.js.map +1 -0
- package/dist/esm/internal/FileBoundarySnapshot.d.ts +14 -0
- package/dist/esm/internal/FileBoundarySnapshot.d.ts.map +1 -0
- package/dist/esm/internal/FileBoundarySnapshot.js +29 -0
- package/dist/esm/internal/FileBoundarySnapshot.js.map +1 -0
- package/dist/esm/internal/FileEnumeration.d.ts +127 -0
- package/dist/esm/internal/FileEnumeration.d.ts.map +1 -0
- package/dist/esm/internal/FileEnumeration.js +236 -0
- package/dist/esm/internal/FileEnumeration.js.map +1 -0
- package/dist/esm/internal/HostReflection.d.ts +80 -0
- package/dist/esm/internal/HostReflection.d.ts.map +1 -0
- package/dist/esm/internal/HostReflection.js +98 -0
- package/dist/esm/internal/HostReflection.js.map +1 -0
- package/dist/esm/internal/JournalRecords.d.ts +325 -0
- package/dist/esm/internal/JournalRecords.d.ts.map +1 -0
- package/dist/esm/internal/JournalRecords.js +306 -0
- package/dist/esm/internal/JournalRecords.js.map +1 -0
- package/dist/esm/internal/NodeJournal.d.ts +157 -0
- package/dist/esm/internal/NodeJournal.d.ts.map +1 -0
- package/dist/esm/internal/NodeJournal.js +298 -0
- package/dist/esm/internal/NodeJournal.js.map +1 -0
- package/dist/esm/internal/Ordering.d.ts +14 -0
- package/dist/esm/internal/Ordering.d.ts.map +1 -0
- package/dist/esm/internal/Ordering.js +14 -0
- package/dist/esm/internal/Ordering.js.map +1 -0
- package/dist/esm/internal/PlanInputs.d.ts +14 -0
- package/dist/esm/internal/PlanInputs.d.ts.map +1 -0
- package/dist/esm/internal/PlanInputs.js +57 -0
- package/dist/esm/internal/PlanInputs.js.map +1 -0
- package/dist/esm/internal/PlanMerges.d.ts +178 -0
- package/dist/esm/internal/PlanMerges.d.ts.map +1 -0
- package/dist/esm/internal/PlanMerges.js +113 -0
- package/dist/esm/internal/PlanMerges.js.map +1 -0
- package/dist/esm/internal/ProvenanceSlot.d.ts +30 -0
- package/dist/esm/internal/ProvenanceSlot.d.ts.map +1 -0
- package/dist/esm/internal/ProvenanceSlot.js +39 -0
- package/dist/esm/internal/ProvenanceSlot.js.map +1 -0
- package/dist/esm/internal/ResultEnvelope.d.ts +88 -0
- package/dist/esm/internal/ResultEnvelope.d.ts.map +1 -0
- package/dist/esm/internal/ResultEnvelope.js +83 -0
- package/dist/esm/internal/ResultEnvelope.js.map +1 -0
- package/dist/esm/internal/RetentionOps.d.ts +414 -0
- package/dist/esm/internal/RetentionOps.d.ts.map +1 -0
- package/dist/esm/internal/RetentionOps.js +576 -0
- package/dist/esm/internal/RetentionOps.js.map +1 -0
- package/dist/esm/internal/RunCatalogOps.d.ts +118 -0
- package/dist/esm/internal/RunCatalogOps.d.ts.map +1 -0
- package/dist/esm/internal/RunCatalogOps.js +120 -0
- package/dist/esm/internal/RunCatalogOps.js.map +1 -0
- package/dist/esm/internal/RunCoordinator.d.ts +78 -0
- package/dist/esm/internal/RunCoordinator.d.ts.map +1 -0
- package/dist/esm/internal/RunCoordinator.js +115 -0
- package/dist/esm/internal/RunCoordinator.js.map +1 -0
- package/dist/esm/internal/RunDriver.d.ts +192 -0
- package/dist/esm/internal/RunDriver.d.ts.map +1 -0
- package/dist/esm/internal/RunDriver.js +2334 -0
- package/dist/esm/internal/RunDriver.js.map +1 -0
- package/dist/esm/internal/RunLifecycle.d.ts +22 -0
- package/dist/esm/internal/RunLifecycle.d.ts.map +1 -0
- package/dist/esm/internal/RunLifecycle.js +49 -0
- package/dist/esm/internal/RunLifecycle.js.map +1 -0
- package/dist/esm/internal/RunListing.d.ts +78 -0
- package/dist/esm/internal/RunListing.d.ts.map +1 -0
- package/dist/esm/internal/RunListing.js +146 -0
- package/dist/esm/internal/RunListing.js.map +1 -0
- package/dist/esm/internal/RunListingError.d.ts +35 -0
- package/dist/esm/internal/RunListingError.d.ts.map +1 -0
- package/dist/esm/internal/RunListingError.js +27 -0
- package/dist/esm/internal/RunListingError.js.map +1 -0
- package/dist/esm/internal/RuntimeGraph.d.ts +73 -0
- package/dist/esm/internal/RuntimeGraph.d.ts.map +1 -0
- package/dist/esm/internal/RuntimeGraph.js +182 -0
- package/dist/esm/internal/RuntimeGraph.js.map +1 -0
- package/dist/esm/internal/SandboxedExecution.d.ts +80 -0
- package/dist/esm/internal/SandboxedExecution.d.ts.map +1 -0
- package/dist/esm/internal/SandboxedExecution.js +107 -0
- package/dist/esm/internal/SandboxedExecution.js.map +1 -0
- package/dist/esm/internal/StateTransaction.d.ts +44 -0
- package/dist/esm/internal/StateTransaction.d.ts.map +1 -0
- package/dist/esm/internal/StateTransaction.js +54 -0
- package/dist/esm/internal/StateTransaction.js.map +1 -0
- package/dist/esm/internal/StepFacts.d.ts +14 -0
- package/dist/esm/internal/StepFacts.d.ts.map +1 -0
- package/dist/esm/internal/StepFacts.js +35 -0
- package/dist/esm/internal/StepFacts.js.map +1 -0
- package/dist/esm/internal/TypedEvents.d.ts +14 -0
- package/dist/esm/internal/TypedEvents.d.ts.map +1 -0
- package/dist/esm/internal/TypedEvents.js +14 -0
- package/dist/esm/internal/TypedEvents.js.map +1 -0
- package/dist/esm/migrations/0001_initial.d.ts +17 -0
- package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
- package/dist/esm/migrations/0001_initial.js +41 -0
- package/dist/esm/migrations/0001_initial.js.map +1 -0
- package/dist/esm/migrations/0002_selection_store.d.ts +15 -0
- package/dist/esm/migrations/0002_selection_store.d.ts.map +1 -0
- package/dist/esm/migrations/0002_selection_store.js +34 -0
- package/dist/esm/migrations/0002_selection_store.js.map +1 -0
- package/dist/esm/migrations/0003_plan_inputs.d.ts +13 -0
- package/dist/esm/migrations/0003_plan_inputs.d.ts.map +1 -0
- package/dist/esm/migrations/0003_plan_inputs.js +70 -0
- package/dist/esm/migrations/0003_plan_inputs.js.map +1 -0
- package/dist/esm/migrations/0004_plan_environment.d.ts +14 -0
- package/dist/esm/migrations/0004_plan_environment.d.ts.map +1 -0
- package/dist/esm/migrations/0004_plan_environment.js +33 -0
- package/dist/esm/migrations/0004_plan_environment.js.map +1 -0
- package/dist/esm/migrations/0005_plan_merges.d.ts +14 -0
- package/dist/esm/migrations/0005_plan_merges.d.ts.map +1 -0
- package/dist/esm/migrations/0005_plan_merges.js +59 -0
- package/dist/esm/migrations/0005_plan_merges.js.map +1 -0
- package/dist/esm/migrations/0006_execution_listing.d.ts +15 -0
- package/dist/esm/migrations/0006_execution_listing.d.ts.map +1 -0
- package/dist/esm/migrations/0006_execution_listing.js +81 -0
- package/dist/esm/migrations/0006_execution_listing.js.map +1 -0
- package/dist/esm/migrations/0007_run_parent_sequence.d.ts +15 -0
- package/dist/esm/migrations/0007_run_parent_sequence.d.ts.map +1 -0
- package/dist/esm/migrations/0007_run_parent_sequence.js +18 -0
- package/dist/esm/migrations/0007_run_parent_sequence.js.map +1 -0
- package/dist/esm/migrations/0008_deferred_consumption.d.ts +16 -0
- package/dist/esm/migrations/0008_deferred_consumption.d.ts.map +1 -0
- package/dist/esm/migrations/0008_deferred_consumption.js +24 -0
- package/dist/esm/migrations/0008_deferred_consumption.js.map +1 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.d.ts +24 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.d.ts.map +1 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.js +127 -0
- package/dist/esm/migrations/0009_deferred_clock_facts.js.map +1 -0
- package/dist/esm/migrations/0010_pending_cancellation.d.ts +15 -0
- package/dist/esm/migrations/0010_pending_cancellation.d.ts.map +1 -0
- package/dist/esm/migrations/0010_pending_cancellation.js +19 -0
- package/dist/esm/migrations/0010_pending_cancellation.js.map +1 -0
- package/dist/esm/test/TestStores.d.ts +89 -0
- package/dist/esm/test/TestStores.d.ts.map +1 -0
- package/dist/esm/test/TestStores.js +103 -0
- package/dist/esm/test/TestStores.js.map +1 -0
- package/docs/README.md +113 -0
- package/docs/api.md +1494 -0
- package/docs/concepts/attempts-and-replay.md +149 -0
- package/docs/concepts/cache-admission.md +265 -0
- package/docs/concepts/call-facts.md +86 -0
- package/docs/concepts/durable-waits.md +175 -0
- package/docs/concepts/execution-facts.md +90 -0
- package/docs/concepts/ownership-and-fencing.md +128 -0
- package/docs/concepts/step-boundaries.md +148 -0
- package/docs/concepts/workspace-transactions.md +199 -0
- package/docs/event-types.md +14 -0
- package/docs/guides/back-up-and-restore.md +164 -0
- package/docs/guides/collect-unreferenced-artifacts.md +128 -0
- package/docs/guides/compose-a-durable-engine.md +146 -0
- package/docs/guides/coordinate-two-processes.md +120 -0
- package/docs/guides/defer-work-with-selection.md +171 -0
- package/docs/guides/delete-old-run-history.md +154 -0
- package/docs/guides/drive-a-plan.md +314 -0
- package/docs/guides/observe-engine-metrics.md +98 -0
- package/docs/guides/observe-executions.md +187 -0
- package/docs/guides/reclaim-runs-from-a-dead-host.md +133 -0
- package/docs/guides/share-a-cache-across-machines.md +134 -0
- package/docs/guides/testing.md +123 -0
- package/docs/installation.md +102 -0
- package/docs/quickstart.md +215 -0
- package/docs/troubleshooting.md +347 -0
- package/package.json +438 -3
- package/src/ArtifactGc.ts +408 -0
- package/src/ArtifactSync.ts +278 -0
- package/src/CacheSync.ts +131 -0
- package/src/DeferredClockFold.ts +586 -0
- package/src/DisasterRecovery.ts +888 -0
- package/src/DurableEngineState.ts +2656 -0
- package/src/EngineStore.ts +656 -0
- package/src/EngineStoreMetrics.ts +331 -0
- package/src/Errors.ts +28 -0
- package/src/EventTypes.ts +27 -0
- package/src/ExecutionFacts.ts +125 -0
- package/src/ExecutionSnapshot.ts +186 -0
- package/src/Inconsistency.ts +253 -0
- package/src/Migrations.ts +95 -0
- package/src/OwnerIdentity.ts +125 -0
- package/src/PlanInputStore.ts +292 -0
- package/src/PlanMergeStore.ts +330 -0
- package/src/PlanScheduler.ts +1806 -0
- package/src/Reconciliation.ts +224 -0
- package/src/ReplayOnly.ts +95 -0
- package/src/Retention.ts +214 -0
- package/src/RunCatalogRead.ts +27 -0
- package/src/RunChangeFeed.ts +125 -0
- package/src/RunState.ts +137 -0
- package/src/Selection.ts +683 -0
- package/src/SelectionStore.ts +353 -0
- package/src/StepBoundary.ts +1165 -0
- package/src/StepCacheFold.ts +244 -0
- package/src/StepSandbox.ts +110 -0
- package/src/WakeBus.ts +167 -0
- package/src/WorkspaceSandbox.ts +1604 -0
- package/src/index.ts +185 -0
- package/src/internal/ActionPersistence.ts +2687 -0
- package/src/internal/ArtifactRoots.ts +92 -0
- package/src/internal/AttemptAdmission.ts +76 -0
- package/src/internal/AttemptLifecycle.ts +163 -0
- package/src/internal/AttemptProbe.ts +90 -0
- package/src/internal/CacheAdmission.ts +133 -0
- package/src/internal/CacheAgeHistory.ts +58 -0
- package/src/internal/CacheAgeVerdicts.ts +61 -0
- package/src/internal/CacheOutputPolicy.ts +82 -0
- package/src/internal/CachePublication.ts +153 -0
- package/src/internal/CallFacts.ts +87 -0
- package/src/internal/CopiedRecord.ts +201 -0
- package/src/internal/DeferredPersistence.ts +459 -0
- package/src/internal/EffectRecords.ts +136 -0
- package/src/internal/EngineJj.ts +30 -0
- package/src/internal/EngineStateSchema.ts +92 -0
- package/src/internal/ExecutionSnapshotRead.ts +298 -0
- package/src/internal/ExecutionSnapshotRelations.ts +121 -0
- package/src/internal/ExitEncoding.ts +453 -0
- package/src/internal/FileBoundarySnapshot.ts +39 -0
- package/src/internal/FileEnumeration.ts +334 -0
- package/src/internal/HostReflection.ts +128 -0
- package/src/internal/JournalRecords.ts +396 -0
- package/src/internal/NodeJournal.ts +340 -0
- package/src/internal/Ordering.ts +14 -0
- package/src/internal/PlanInputs.ts +62 -0
- package/src/internal/PlanMerges.ts +123 -0
- package/src/internal/ProvenanceSlot.ts +52 -0
- package/src/internal/ResultEnvelope.ts +118 -0
- package/src/internal/RetentionOps.ts +865 -0
- package/src/internal/RunCatalogOps.ts +174 -0
- package/src/internal/RunCoordinator.ts +215 -0
- package/src/internal/RunDriver.ts +3152 -0
- package/src/internal/RunLifecycle.ts +53 -0
- package/src/internal/RunListing.ts +210 -0
- package/src/internal/RunListingError.ts +37 -0
- package/src/internal/RuntimeGraph.ts +253 -0
- package/src/internal/SandboxedExecution.ts +165 -0
- package/src/internal/StateTransaction.ts +71 -0
- package/src/internal/StepFacts.ts +36 -0
- package/src/internal/TypedEvents.ts +29 -0
- package/src/migrations/0001_initial.ts +51 -0
- package/src/migrations/0002_selection_store.ts +38 -0
- package/src/migrations/0003_plan_inputs.ts +75 -0
- package/src/migrations/0004_plan_environment.ts +34 -0
- package/src/migrations/0005_plan_merges.ts +60 -0
- package/src/migrations/0006_execution_listing.ts +86 -0
- package/src/migrations/0007_run_parent_sequence.ts +19 -0
- package/src/migrations/0008_deferred_consumption.ts +27 -0
- package/src/migrations/0009_deferred_clock_facts.ts +163 -0
- package/src/migrations/0010_pending_cancellation.ts +20 -0
- package/src/test/TestStores.ts +145 -0
|
@@ -0,0 +1,1584 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var RunDriver_exports = {};
|
|
30
|
+
__export(RunDriver_exports, {
|
|
31
|
+
FlowCycleDetected: () => FlowCycleDetected,
|
|
32
|
+
make: () => make,
|
|
33
|
+
samePayload: () => samePayload,
|
|
34
|
+
spawnEffectKind: () => spawnEffectKind
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(RunDriver_exports);
|
|
37
|
+
var CapabilitySet = __toESM(require("@smthrs/capability/CapabilitySet"), 1);
|
|
38
|
+
var import_DurableWriter = require("@smthrs/database/DurableWriter");
|
|
39
|
+
var import_engine = require("@smthrs/engine");
|
|
40
|
+
var import_flow = require("@smthrs/flow");
|
|
41
|
+
var import_journal = require("@smthrs/journal");
|
|
42
|
+
var import_run_store = require("@smthrs/run-store");
|
|
43
|
+
var Cause = __toESM(require("effect/Cause"), 1);
|
|
44
|
+
var Clock = __toESM(require("effect/Clock"), 1);
|
|
45
|
+
var Deferred = __toESM(require("effect/Deferred"), 1);
|
|
46
|
+
var Duration = __toESM(require("effect/Duration"), 1);
|
|
47
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
48
|
+
var Exit = __toESM(require("effect/Exit"), 1);
|
|
49
|
+
var Metric = __toESM(require("effect/Metric"), 1);
|
|
50
|
+
var Option = __toESM(require("effect/Option"), 1);
|
|
51
|
+
var Result = __toESM(require("effect/Result"), 1);
|
|
52
|
+
var Schema = __toESM(require("effect/Schema"), 1);
|
|
53
|
+
var Scope = __toESM(require("effect/Scope"), 1);
|
|
54
|
+
var DurableEngineState = __toESM(require("../DurableEngineState.js"), 1);
|
|
55
|
+
var EngineStoreMetrics = __toESM(require("../EngineStoreMetrics.js"), 1);
|
|
56
|
+
var import_EventTypes = require("../EventTypes.js");
|
|
57
|
+
var ExecutionFacts = __toESM(require("../ExecutionFacts.js"), 1);
|
|
58
|
+
var import_RunState = require("../RunState.js");
|
|
59
|
+
var WakeBus = __toESM(require("../WakeBus.js"), 1);
|
|
60
|
+
var ActionPersistence = __toESM(require("./ActionPersistence.js"), 1);
|
|
61
|
+
var EffectRecords = __toESM(require("./EffectRecords.js"), 1);
|
|
62
|
+
var ExitEncoding = __toESM(require("./ExitEncoding.js"), 1);
|
|
63
|
+
var JournalRecords = __toESM(require("./JournalRecords.js"), 1);
|
|
64
|
+
var RunCoordinator = __toESM(require("./RunCoordinator.js"), 1);
|
|
65
|
+
var RunLifecycle = __toESM(require("./RunLifecycle.js"), 1);
|
|
66
|
+
var StateTransaction = __toESM(require("./StateTransaction.js"), 1);
|
|
67
|
+
const RunStateJson = Schema.fromJsonString(import_RunState.RunState);
|
|
68
|
+
const asDefects = (cause) => Cause.fromReasons(
|
|
69
|
+
cause.reasons.map(
|
|
70
|
+
(reason) => Cause.isFailReason(reason) ? Cause.makeDieReason(reason.error).annotate(Cause.reasonAnnotations(reason)) : reason
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
const FlowCycleDetected = import_flow.FlowRuntime.FlowCycleDetected;
|
|
74
|
+
const spawnEffectKind = import_EventTypes.EventTypes.childSpawnKind;
|
|
75
|
+
const snapshot = (row) => ({
|
|
76
|
+
status: row.status,
|
|
77
|
+
owner: row.owner,
|
|
78
|
+
heartbeatAtMs: row.heartbeatAtMs
|
|
79
|
+
});
|
|
80
|
+
const isPlainObject = (value) => {
|
|
81
|
+
if (value === null || typeof value !== "object") return false;
|
|
82
|
+
const prototype = Object.getPrototypeOf(value);
|
|
83
|
+
return prototype === Object.prototype || prototype === null;
|
|
84
|
+
};
|
|
85
|
+
const samePayload = (left, right) => {
|
|
86
|
+
if (Object.is(left, right)) return true;
|
|
87
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
88
|
+
return left === 0 && right === 0;
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(left)) {
|
|
91
|
+
if (!Array.isArray(right) || left.length !== right.length) return false;
|
|
92
|
+
for (let index = 0; index < left.length; index++) {
|
|
93
|
+
if (!samePayload(left[index], right[index])) return false;
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(right) || !isPlainObject(left) || !isPlainObject(right)) return false;
|
|
98
|
+
const leftKeys = Object.keys(left);
|
|
99
|
+
const rightKeys = Object.keys(right);
|
|
100
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
101
|
+
for (const key of leftKeys) {
|
|
102
|
+
if (!Object.hasOwn(right, key) || !samePayload(left[key], right[key])) return false;
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
};
|
|
106
|
+
const cancelRequested = { _tag: "CancelRequested" };
|
|
107
|
+
const staleRunningSweepBatch = 64;
|
|
108
|
+
const stealRefusalBackoffFirst = Duration.times(import_run_store.Ownership.heartbeatInterval, 2);
|
|
109
|
+
const stealRefusalBackoffMax = import_run_store.Ownership.heartbeatStaleAfter;
|
|
110
|
+
const withoutResult = (state) => {
|
|
111
|
+
const { cancellation: _, result: __, ...rest } = state;
|
|
112
|
+
return rest;
|
|
113
|
+
};
|
|
114
|
+
const make = (dependencies) => Effect.gen(function* () {
|
|
115
|
+
const journal = yield* import_journal.Journal.Journal;
|
|
116
|
+
const writer = yield* import_DurableWriter.DurableWriter;
|
|
117
|
+
const store = yield* import_run_store.RunStore.RunStore;
|
|
118
|
+
const engineState = yield* DurableEngineState.DurableEngineState;
|
|
119
|
+
const lifecycle = RunLifecycle.make(store, engineState);
|
|
120
|
+
const wakeBus = dependencies.wakeBus ?? WakeBus.makeNoop();
|
|
121
|
+
const suppliedIsAlive = dependencies.isAlive;
|
|
122
|
+
const isAlive = suppliedIsAlive ?? import_run_store.Ownership.leaseLiveness(import_run_store.Ownership.heartbeatStaleAfter);
|
|
123
|
+
const livenessEvidenceKind = (expectedOwner) => suppliedIsAlive === void 0 ? "lease-expired" : import_run_store.Ownership.sameHostIncarnation(expectedOwner, dependencies.owner) ? "same-host-pid-dead" : "cross-host-unreachable-stale";
|
|
124
|
+
const sameOwner = (left, right) => left.hostId === right.hostId && left.pid === right.pid && left.nonce === right.nonce;
|
|
125
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
126
|
+
const warnedUnregistered = /* @__PURE__ */ new Set();
|
|
127
|
+
const stealRefusals = /* @__PURE__ */ new Map();
|
|
128
|
+
const deferSteal = (runId, heartbeatAtMs, nowMs) => {
|
|
129
|
+
const previous = stealRefusals.get(runId);
|
|
130
|
+
const attempt = previous !== void 0 && previous.heartbeatAtMs === heartbeatAtMs ? previous.attempt + 1 : 1;
|
|
131
|
+
const waitMs = Math.min(
|
|
132
|
+
Duration.toMillis(stealRefusalBackoffFirst) * Math.pow(2, attempt - 1),
|
|
133
|
+
Duration.toMillis(stealRefusalBackoffMax)
|
|
134
|
+
);
|
|
135
|
+
stealRefusals.set(runId, { heartbeatAtMs, nextProbeAtMs: nowMs + waitMs, attempt });
|
|
136
|
+
};
|
|
137
|
+
const stealRefusedSourceId = (owner, heartbeatAtMs, evidence) => `${dependencies.journalSource}:steal-refused:${evidence}:${JSON.stringify([owner.hostId, owner.pid, owner.nonce])}:${heartbeatAtMs}`;
|
|
138
|
+
const liveInstances = /* @__PURE__ */ new Map();
|
|
139
|
+
const retainedScopes = /* @__PURE__ */ new Map();
|
|
140
|
+
const releaseRetainedScope = (runId, exit) => Effect.suspend(() => {
|
|
141
|
+
const scope = retainedScopes.get(runId);
|
|
142
|
+
if (scope === void 0) return Effect.void;
|
|
143
|
+
retainedScopes.delete(runId);
|
|
144
|
+
return Scope.close(scope, exit).pipe(
|
|
145
|
+
Effect.catchCause(
|
|
146
|
+
(cause) => Effect.logWarning(
|
|
147
|
+
`engine-store: finalizers of parked run ${runId} failed while closing its retained flow scope`,
|
|
148
|
+
cause
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
const retainedRuns = Effect.sync(() => new Set(retainedScopes.keys()));
|
|
154
|
+
yield* Effect.addFinalizer(
|
|
155
|
+
() => Effect.forEach(
|
|
156
|
+
Array.from(retainedScopes.keys()),
|
|
157
|
+
(runId) => releaseRetainedScope(runId, Exit.void),
|
|
158
|
+
{ discard: true }
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
const encodeState = (state) => Schema.encodeEffect(RunStateJson)(state).pipe(Effect.orDie);
|
|
162
|
+
const decodeState = (stateJson) => Schema.decodeUnknownEffect(RunStateJson)(stateJson).pipe(Effect.orDie);
|
|
163
|
+
const recordResume = (executionId, parked, reason) => dependencies.requestResume === void 0 || !parked ? Effect.void : dependencies.requestResume(executionId, reason);
|
|
164
|
+
const recordParentResume = (parentExecutionId) => dependencies.requestResume === void 0 ? Effect.void : Effect.flatMap(
|
|
165
|
+
engineState.waiting(parentExecutionId),
|
|
166
|
+
(waiting) => recordResume(
|
|
167
|
+
parentExecutionId,
|
|
168
|
+
Option.isSome(waiting) && waiting.value.reason !== "quarantine",
|
|
169
|
+
"parent"
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
const executionFacts = ExecutionFacts.make({
|
|
173
|
+
runs: store,
|
|
174
|
+
state: engineState,
|
|
175
|
+
journal,
|
|
176
|
+
sourceId: dependencies.journalSource
|
|
177
|
+
});
|
|
178
|
+
const requestCancellation = (runId, at) => executionFacts.requestCancel(runId, at).pipe(
|
|
179
|
+
Effect.catchIf(
|
|
180
|
+
(error) => error._tag === "@smthrs/journal/JournalError" || error instanceof import_DurableWriter.DatabaseError,
|
|
181
|
+
Effect.die
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
const decisionRecord = (runId, payload, sourceId) => (
|
|
185
|
+
// Lifecycle decisions normally use the unfenced channel: they commit in the SAME transaction
|
|
186
|
+
// as the store-level owner CAS that is its fence (`transitionOwned`,
|
|
187
|
+
// `activate`, `claim`/`steal` outcomes), and by then the run is often no
|
|
188
|
+
// longer `running` under this owner, which is the exact predicate the
|
|
189
|
+
// journal fence asserts. Several call sites (claim-lost,
|
|
190
|
+
// steal-refused-owner-alive, activation-lost) also record decisions for
|
|
191
|
+
// runs this driver never owned at all — first-writer-wins evidence,
|
|
192
|
+
// which is what the unfenced channel exists for. Lease reconfirmation
|
|
193
|
+
// retains ownership and emits this record through the fenced channel.
|
|
194
|
+
JournalRecords.runDecision({
|
|
195
|
+
runId,
|
|
196
|
+
lineageId: import_engine.FlowEngine.Lineage.root(runId),
|
|
197
|
+
sourceId,
|
|
198
|
+
...sourceId === dependencies.journalSource ? {} : { sourceSeq: 0 }
|
|
199
|
+
}, payload)
|
|
200
|
+
);
|
|
201
|
+
const completeRunClocks = (runId, completedAtMs) => Effect.flatMap(engineState.completeRunClocks(runId, completedAtMs), (completed) => Effect.forEach(completed, (clock) => journal.emitDurableUnfenced(
|
|
202
|
+
JournalRecords.clockCompletedFor(
|
|
203
|
+
dependencies.journalSource,
|
|
204
|
+
import_engine.FlowEngine.Lineage.root(runId),
|
|
205
|
+
clock,
|
|
206
|
+
completedAtMs
|
|
207
|
+
)
|
|
208
|
+
), { discard: true }));
|
|
209
|
+
const emitDecision = (runId, payload, sourceId = dependencies.journalSource) => Effect.gen(function* () {
|
|
210
|
+
const decision = payload;
|
|
211
|
+
const authoritative = sourceId === dependencies.journalSource && decision.decision !== "wake-scheduled" && decision.decision !== "child-policy-applied";
|
|
212
|
+
const recorded = authoritative ? {
|
|
213
|
+
...decision,
|
|
214
|
+
executionFact: yield* executionFacts.fact(runId, decision.decision === "created" ? "created" : "legacy")
|
|
215
|
+
} : payload;
|
|
216
|
+
yield* journal.emitDurableUnfenced(decisionRecord(runId, recorded, sourceId));
|
|
217
|
+
}).pipe(Effect.orDie);
|
|
218
|
+
const emitEvidence = (runId, payload, sourceId = dependencies.journalSource) => journal.emitDurableUnfenced(decisionRecord(runId, payload, sourceId)).pipe(
|
|
219
|
+
Effect.flatMap(
|
|
220
|
+
(receipt) => receipt._tag === "Duplicate" ? Effect.logDebug(
|
|
221
|
+
`engine-store: run ${runId} already recorded this evidence`,
|
|
222
|
+
{ runId, sourceId, seq: receipt.seq }
|
|
223
|
+
) : Effect.void
|
|
224
|
+
),
|
|
225
|
+
Effect.catch(
|
|
226
|
+
(error) => Effect.logWarning(
|
|
227
|
+
`engine-store: evidence about run ${runId} was not admitted to the journal (${error.code}); the run is unaffected`,
|
|
228
|
+
{ runId, sourceId, code: error.code, payload }
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
const transactState = StateTransaction.make(engineState, journal);
|
|
233
|
+
const transitionAndRecord = (runId, toStatus, stateJson, decision, guard, afterTransitioned, waiting) => transactState(
|
|
234
|
+
Effect.gen(function* () {
|
|
235
|
+
if (waiting !== void 0 && waiting !== null) {
|
|
236
|
+
const parked = yield* engineState.park(runId, waiting, dependencies.owner);
|
|
237
|
+
if (parked._tag === "NotFound") {
|
|
238
|
+
return yield* Effect.fail({ _tag: "TransitionRefused", outcome: parked });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const transitioned = yield* store.transitionOwned(
|
|
242
|
+
runId,
|
|
243
|
+
dependencies.owner,
|
|
244
|
+
toStatus,
|
|
245
|
+
stateJson,
|
|
246
|
+
guard
|
|
247
|
+
).pipe(Effect.orDie);
|
|
248
|
+
if (transitioned._tag !== "Transitioned") {
|
|
249
|
+
return yield* Effect.fail({ _tag: "TransitionRefused", outcome: transitioned });
|
|
250
|
+
}
|
|
251
|
+
if (waiting === null) {
|
|
252
|
+
yield* engineState.wake(runId);
|
|
253
|
+
}
|
|
254
|
+
if (afterTransitioned !== void 0) {
|
|
255
|
+
yield* afterTransitioned;
|
|
256
|
+
}
|
|
257
|
+
if (toStatus === "completed" || toStatus === "failed") {
|
|
258
|
+
yield* completeRunClocks(runId, yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor)));
|
|
259
|
+
}
|
|
260
|
+
yield* emitDecision(runId, { ...decision, state: JSON.parse(stateJson) });
|
|
261
|
+
return transitioned;
|
|
262
|
+
})
|
|
263
|
+
).pipe(
|
|
264
|
+
Effect.catchTag("TransitionRefused", ({ outcome }) => Effect.succeed(outcome)),
|
|
265
|
+
Effect.orDie
|
|
266
|
+
);
|
|
267
|
+
const abandon = (runId, claimedAtMs) => store.abandonClaim(runId, dependencies.owner, claimedAtMs).pipe(
|
|
268
|
+
Effect.asVoid,
|
|
269
|
+
Effect.orDie
|
|
270
|
+
);
|
|
271
|
+
const claimAndActivate = Effect.fn(
|
|
272
|
+
"RunDriver.claimAndActivate"
|
|
273
|
+
)(function* (row) {
|
|
274
|
+
return yield* Effect.gen(function* () {
|
|
275
|
+
yield* Effect.annotateCurrentSpan({ runId: row.runId, status: row.status });
|
|
276
|
+
if (dependencies.canExecute !== void 0 && !(yield* dependencies.canExecute(row))) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
if (row.status === "completed" || row.status === "failed" || row.status === "cancelled") {
|
|
280
|
+
yield* Effect.annotateCurrentSpan({ outcome: "terminal" });
|
|
281
|
+
yield* Metric.update(EngineStoreMetrics.claim.Terminal, 1);
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
if (row.cancelRequestedAtMs === null) {
|
|
285
|
+
const waiting = yield* engineState.waiting(row.runId);
|
|
286
|
+
if (Option.isSome(waiting) && waiting.value.reason === "quarantine") {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const expected = snapshot(row);
|
|
291
|
+
const nowMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor));
|
|
292
|
+
let claim;
|
|
293
|
+
let stealEvidenceKind;
|
|
294
|
+
let recoveredClaim;
|
|
295
|
+
if (row.status === "running") {
|
|
296
|
+
if (row.owner === null || row.heartbeatAtMs === null || row.heartbeatAtMs >= nowMs - Duration.toMillis(import_run_store.Ownership.heartbeatStaleAfter)) {
|
|
297
|
+
if (row.owner !== null && !sameOwner(row.owner, dependencies.owner)) {
|
|
298
|
+
deferSteal(row.runId, row.heartbeatAtMs, nowMs);
|
|
299
|
+
yield* emitEvidence(row.runId, {
|
|
300
|
+
decision: "steal-refused-owner-alive",
|
|
301
|
+
evidence: "lease-fresh",
|
|
302
|
+
expectedOwner: row.owner,
|
|
303
|
+
heartbeatAtMs: row.heartbeatAtMs
|
|
304
|
+
}, stealRefusedSourceId(row.owner, row.heartbeatAtMs, "lease-fresh"));
|
|
305
|
+
}
|
|
306
|
+
yield* Effect.annotateCurrentSpan({ outcome: "heartbeat_fresh" });
|
|
307
|
+
yield* Metric.update(EngineStoreMetrics.claim.HeartbeatFresh, 1);
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
if (yield* isAlive(row.owner, { claimant: dependencies.owner, heartbeatAtMs: row.heartbeatAtMs, nowMs })) {
|
|
311
|
+
deferSteal(row.runId, row.heartbeatAtMs, nowMs);
|
|
312
|
+
yield* emitEvidence(row.runId, {
|
|
313
|
+
decision: "steal-refused-owner-alive",
|
|
314
|
+
// Always a supplied probe. The default check is the lease, and
|
|
315
|
+
// the lease already refused above for every row that could still
|
|
316
|
+
// be fresh, so reaching this arm means the deployment answered
|
|
317
|
+
// for the owner with something the store cannot see.
|
|
318
|
+
evidence: "probe",
|
|
319
|
+
expectedOwner: row.owner,
|
|
320
|
+
heartbeatAtMs: row.heartbeatAtMs
|
|
321
|
+
}, stealRefusedSourceId(row.owner, row.heartbeatAtMs, "probe"));
|
|
322
|
+
yield* Effect.annotateCurrentSpan({ outcome: "steal_refused_owner_alive" });
|
|
323
|
+
yield* Metric.update(EngineStoreMetrics.claim.StealRefusedOwnerAlive, 1);
|
|
324
|
+
yield* Effect.logDebug("run steal refused, recorded owner is alive", {
|
|
325
|
+
runId: row.runId,
|
|
326
|
+
heartbeatAtMs: row.heartbeatAtMs
|
|
327
|
+
});
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
stealEvidenceKind = livenessEvidenceKind(row.owner);
|
|
331
|
+
claim = yield* store.steal(
|
|
332
|
+
row.runId,
|
|
333
|
+
expected,
|
|
334
|
+
dependencies.owner,
|
|
335
|
+
nowMs,
|
|
336
|
+
{
|
|
337
|
+
expectedOwner: row.owner,
|
|
338
|
+
checkedAtMs: nowMs,
|
|
339
|
+
kind: stealEvidenceKind
|
|
340
|
+
}
|
|
341
|
+
).pipe(Effect.orDie);
|
|
342
|
+
} else {
|
|
343
|
+
if (row.claim !== null && row.claimedAtMs !== null && !sameOwner(row.claim, dependencies.owner) && row.claimedAtMs < nowMs - Duration.toMillis(import_run_store.Ownership.heartbeatStaleAfter) && !(yield* isAlive(row.claim, { claimant: dependencies.owner, heartbeatAtMs: row.claimedAtMs, nowMs }))) {
|
|
344
|
+
const recovered = yield* store.recoverClaim(
|
|
345
|
+
row.runId,
|
|
346
|
+
row.claim,
|
|
347
|
+
row.claimedAtMs,
|
|
348
|
+
dependencies.owner,
|
|
349
|
+
nowMs,
|
|
350
|
+
{ expectedOwner: row.claim, checkedAtMs: nowMs, kind: livenessEvidenceKind(row.claim) }
|
|
351
|
+
).pipe(Effect.orDie);
|
|
352
|
+
if (recovered._tag === "Recovered") {
|
|
353
|
+
recoveredClaim = { claimant: row.claim, claimedAtMs: row.claimedAtMs };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
claim = yield* store.claim(
|
|
357
|
+
row.runId,
|
|
358
|
+
expected,
|
|
359
|
+
dependencies.owner,
|
|
360
|
+
nowMs
|
|
361
|
+
).pipe(Effect.orDie);
|
|
362
|
+
}
|
|
363
|
+
if (claim._tag !== "Claimed") {
|
|
364
|
+
yield* emitEvidence(row.runId, {
|
|
365
|
+
decision: "claim-lost",
|
|
366
|
+
outcome: claim._tag,
|
|
367
|
+
expected
|
|
368
|
+
});
|
|
369
|
+
yield* Effect.annotateCurrentSpan({ outcome: "claim_lost" });
|
|
370
|
+
yield* Metric.update(EngineStoreMetrics.claim.ClaimLost, 1);
|
|
371
|
+
yield* Effect.logDebug("run claim lost", { runId: row.runId, outcome: claim._tag });
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
const activation = yield* transactState(
|
|
375
|
+
Effect.gen(function* () {
|
|
376
|
+
if (dependencies.canActivate !== void 0 && !(yield* dependencies.canActivate(yield* store.get(row.runId).pipe(Effect.orDie)))) {
|
|
377
|
+
return { _tag: "SnapshotChanged" };
|
|
378
|
+
}
|
|
379
|
+
if (row.cancelRequestedAtMs === null) {
|
|
380
|
+
const waiting = yield* engineState.waiting(row.runId);
|
|
381
|
+
if (Option.isSome(waiting) && waiting.value.reason === "quarantine") {
|
|
382
|
+
return { _tag: "SnapshotChanged" };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const activation2 = yield* store.activate(
|
|
386
|
+
row.runId,
|
|
387
|
+
dependencies.owner,
|
|
388
|
+
claim.claimedAtMs,
|
|
389
|
+
expected
|
|
390
|
+
).pipe(Effect.orDie);
|
|
391
|
+
if (activation2._tag !== "Activated") {
|
|
392
|
+
return activation2;
|
|
393
|
+
}
|
|
394
|
+
yield* emitDecision(row.runId, {
|
|
395
|
+
decision: row.status === "running" ? "stolen-and-activated" : "claimed-and-activated",
|
|
396
|
+
previousStatus: row.status,
|
|
397
|
+
owner: dependencies.owner,
|
|
398
|
+
// What admitted the takeover, so an operator reading the journal
|
|
399
|
+
// can tell a lease that simply expired from a probe that
|
|
400
|
+
// positively reported the owner gone.
|
|
401
|
+
...stealEvidenceKind === void 0 ? {} : { evidence: stealEvidenceKind },
|
|
402
|
+
...recoveredClaim === void 0 ? {} : { recoveredClaim }
|
|
403
|
+
});
|
|
404
|
+
return activation2;
|
|
405
|
+
})
|
|
406
|
+
).pipe(Effect.orDie);
|
|
407
|
+
if (activation._tag !== "Activated") {
|
|
408
|
+
yield* abandon(row.runId, claim.claimedAtMs);
|
|
409
|
+
yield* emitEvidence(row.runId, {
|
|
410
|
+
decision: "activation-lost",
|
|
411
|
+
outcome: activation._tag,
|
|
412
|
+
expected
|
|
413
|
+
});
|
|
414
|
+
yield* Effect.annotateCurrentSpan({ outcome: "activation_lost" });
|
|
415
|
+
yield* Metric.update(EngineStoreMetrics.claim.ActivationLost, 1);
|
|
416
|
+
yield* Effect.logDebug("run activation lost", { runId: row.runId, outcome: activation._tag });
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
yield* Effect.annotateCurrentSpan({ outcome: "activated" });
|
|
420
|
+
yield* Metric.update(EngineStoreMetrics.claim.Activated, 1);
|
|
421
|
+
stealRefusals.delete(row.runId);
|
|
422
|
+
return true;
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
const descendantsOf = lifecycle.descendants;
|
|
426
|
+
const requestCancelDescendants = (runId, nowMs) => Effect.gen(function* () {
|
|
427
|
+
const descendants = yield* descendantsOf(runId);
|
|
428
|
+
yield* Effect.forEach(
|
|
429
|
+
descendants,
|
|
430
|
+
(childId) => requestCancellation(childId, nowMs),
|
|
431
|
+
{ discard: true }
|
|
432
|
+
);
|
|
433
|
+
return descendants;
|
|
434
|
+
});
|
|
435
|
+
const exitPolicyOf = (row) => decodeState(row.stateJson).pipe(
|
|
436
|
+
// A row written before the policy existed, or created outside
|
|
437
|
+
// `ensureRun` (a seeded edge, an external admission), names none. It
|
|
438
|
+
// is a LINKED child either way, and the contract for a linked child is
|
|
439
|
+
// that it ends with the run it belongs to; `detach` is the deliberate
|
|
440
|
+
// exception and has to be recorded to hold.
|
|
441
|
+
Effect.map((state) => state.onParentExit ?? "cancel")
|
|
442
|
+
);
|
|
443
|
+
const endLinkedChildren = (runId, nowMs) => Effect.gen(function* () {
|
|
444
|
+
const roots = (yield* lifecycle.rounds(runId)).map((row) => row.runId);
|
|
445
|
+
const seen = new Set(roots);
|
|
446
|
+
const cancelled = [];
|
|
447
|
+
const detached = [];
|
|
448
|
+
let frontier = roots;
|
|
449
|
+
while (frontier.length > 0) {
|
|
450
|
+
const next = [];
|
|
451
|
+
for (const parentId of frontier) {
|
|
452
|
+
for (const edge of yield* engineState.runChildren(parentId)) {
|
|
453
|
+
if (seen.has(edge.childId)) continue;
|
|
454
|
+
const rounds = yield* lifecycle.rounds(edge.childId);
|
|
455
|
+
for (const round of rounds) seen.add(round.runId);
|
|
456
|
+
seen.add(edge.childId);
|
|
457
|
+
const row = rounds.find(
|
|
458
|
+
(round) => round.status !== "completed" && round.status !== "failed" && round.status !== "cancelled"
|
|
459
|
+
);
|
|
460
|
+
if (row === void 0) continue;
|
|
461
|
+
if ((yield* exitPolicyOf(rounds.find((round) => round.runId === edge.childId))) === "detach") {
|
|
462
|
+
detached.push(edge.childId);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
for (const round of rounds) {
|
|
466
|
+
if (round.status !== "completed" && round.status !== "failed" && round.status !== "cancelled") {
|
|
467
|
+
cancelled.push(round.runId);
|
|
468
|
+
}
|
|
469
|
+
next.push(round.runId);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
frontier = next;
|
|
474
|
+
}
|
|
475
|
+
yield* Effect.forEach(
|
|
476
|
+
cancelled,
|
|
477
|
+
(childId) => requestCancellation(childId, nowMs),
|
|
478
|
+
{ discard: true }
|
|
479
|
+
);
|
|
480
|
+
return { cancelled, detached };
|
|
481
|
+
});
|
|
482
|
+
const applyChildExitPolicy = (runId) => Effect.gen(function* () {
|
|
483
|
+
const nowMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor));
|
|
484
|
+
const outcome = yield* endLinkedChildren(runId, nowMs).pipe(Effect.orDie);
|
|
485
|
+
if (outcome.cancelled.length === 0 && outcome.detached.length === 0) return;
|
|
486
|
+
yield* emitDecision(runId, {
|
|
487
|
+
decision: "child-policy-applied",
|
|
488
|
+
appliedAtMs: nowMs,
|
|
489
|
+
cancelled: outcome.cancelled,
|
|
490
|
+
detached: outcome.detached
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
const inheritParentCancellation = (parentId, childId) => Effect.gen(function* () {
|
|
494
|
+
const rounds = yield* lifecycle.rounds(parentId).pipe(Effect.orDie);
|
|
495
|
+
if (rounds.length === 0) return;
|
|
496
|
+
const cancelled = rounds.find((round) => round.cancelRequestedAtMs !== null || round.status === "cancelled");
|
|
497
|
+
if (cancelled !== void 0) {
|
|
498
|
+
const nowMs = cancelled.cancelRequestedAtMs ?? (yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor)));
|
|
499
|
+
yield* requestCancellation(childId, nowMs).pipe(Effect.orDie);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (rounds.some((round) => round.status !== "completed" && round.status !== "failed")) return;
|
|
503
|
+
if ((yield* exitPolicyOf(yield* store.get(childId).pipe(Effect.orDie))) === "detach") {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
yield* requestCancellation(childId, yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))).pipe(
|
|
507
|
+
Effect.orDie
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
const cancelOwned = (runId, state) => Effect.gen(function* () {
|
|
511
|
+
const interruptedAtMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor));
|
|
512
|
+
const stateJson = yield* encodeState({
|
|
513
|
+
...withoutResult(state),
|
|
514
|
+
cancellation: { interruptedAtMs }
|
|
515
|
+
});
|
|
516
|
+
let cascaded = [];
|
|
517
|
+
let settled = false;
|
|
518
|
+
yield* transactState(
|
|
519
|
+
Effect.gen(function* () {
|
|
520
|
+
yield* store.acknowledgeCancel(runId, dependencies.owner, interruptedAtMs).pipe(Effect.orDie);
|
|
521
|
+
const transitioned = yield* store.transitionOwned(
|
|
522
|
+
runId,
|
|
523
|
+
dependencies.owner,
|
|
524
|
+
"cancelled",
|
|
525
|
+
stateJson
|
|
526
|
+
).pipe(Effect.orDie);
|
|
527
|
+
if (transitioned._tag !== "Transitioned") return;
|
|
528
|
+
settled = true;
|
|
529
|
+
cascaded = yield* requestCancelDescendants(runId, interruptedAtMs);
|
|
530
|
+
yield* completeRunClocks(runId, interruptedAtMs);
|
|
531
|
+
yield* engineState.wake(runId).pipe(Effect.asVoid);
|
|
532
|
+
yield* journal.emitDurableUnfenced(
|
|
533
|
+
JournalRecords.interrupted({
|
|
534
|
+
runId,
|
|
535
|
+
lineageId: import_engine.FlowEngine.Lineage.root(runId),
|
|
536
|
+
sourceId: dependencies.journalSource
|
|
537
|
+
}, {
|
|
538
|
+
outcome: "cancelled",
|
|
539
|
+
interruptedAtMs,
|
|
540
|
+
owner: dependencies.owner,
|
|
541
|
+
executionFact: yield* executionFacts.fact(runId),
|
|
542
|
+
...cascaded.length === 0 ? {} : { cascadedTo: cascaded }
|
|
543
|
+
})
|
|
544
|
+
).pipe(Effect.orDie);
|
|
545
|
+
})
|
|
546
|
+
).pipe(Effect.orDie);
|
|
547
|
+
yield* releaseRetainedScope(runId, Exit.failCause(Cause.interrupt()));
|
|
548
|
+
if (cascaded.length > 0) {
|
|
549
|
+
const activeCoordinator = yield* Deferred.await(coordinatorDeferred);
|
|
550
|
+
yield* Effect.forEach(cascaded, (childId) => activeCoordinator.wake(childId), { discard: true });
|
|
551
|
+
}
|
|
552
|
+
if (settled) {
|
|
553
|
+
yield* announceSettled(runId, state);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
const interruptedWaiting = (runId, instance) => Effect.gen(function* () {
|
|
557
|
+
if (instance === void 0 || !instance.suspended) return { reason: "released" };
|
|
558
|
+
const declared = instance.waiting;
|
|
559
|
+
if (declared !== void 0) return declared;
|
|
560
|
+
const pendingClocks = yield* engineState.pendingClocks({ executionId: runId });
|
|
561
|
+
return pendingClocks.length > 0 ? {
|
|
562
|
+
reason: "timer",
|
|
563
|
+
wakeAt: Math.min(...pendingClocks.map((clock) => clock.dueAtMs))
|
|
564
|
+
} : { reason: "event" };
|
|
565
|
+
});
|
|
566
|
+
const releaseOwned = (runId, state, cause, round) => Effect.gen(function* () {
|
|
567
|
+
const waiting = yield* interruptedWaiting(runId, round?.instance);
|
|
568
|
+
const suspension = round !== void 0 && waiting.reason !== "released" ? (yield* encodeResult(
|
|
569
|
+
round.flow,
|
|
570
|
+
new import_flow.Flow.Suspended({ cause: round.instance.cause })
|
|
571
|
+
)).encoded : void 0;
|
|
572
|
+
yield* transitionAndRecord(
|
|
573
|
+
runId,
|
|
574
|
+
"suspended",
|
|
575
|
+
yield* encodeState(
|
|
576
|
+
suspension === void 0 ? withoutResult(state) : { ...withoutResult(state), result: suspension }
|
|
577
|
+
),
|
|
578
|
+
// Why the run was released (#3328): an operator reading a parked run
|
|
579
|
+
// must be able to tell a lapsed lease on a live host from a shutdown.
|
|
580
|
+
{ decision: "interrupt-released", owner: dependencies.owner, cause },
|
|
581
|
+
void 0,
|
|
582
|
+
void 0,
|
|
583
|
+
waiting
|
|
584
|
+
);
|
|
585
|
+
});
|
|
586
|
+
const settleInterrupted = (runId, state, cause, round) => store.get(runId).pipe(
|
|
587
|
+
Effect.flatMap((row) => {
|
|
588
|
+
if (row.claim !== null && row.claimedAtMs !== null && sameOwner(row.claim, dependencies.owner)) {
|
|
589
|
+
return abandon(runId, row.claimedAtMs);
|
|
590
|
+
}
|
|
591
|
+
if (row.status !== "running" || row.owner === null || !sameOwner(row.owner, dependencies.owner)) {
|
|
592
|
+
return Effect.void;
|
|
593
|
+
}
|
|
594
|
+
return row.cancelRequestedAtMs !== null ? cancelOwned(runId, state) : releaseOwned(runId, state, cause, round);
|
|
595
|
+
}),
|
|
596
|
+
Effect.catch((error) => error.code === "not_found_row" ? Effect.void : Effect.die(error))
|
|
597
|
+
);
|
|
598
|
+
const encodeResult = (flow, result) => ExitEncoding.encode(flow, result);
|
|
599
|
+
const normalizePayload = (flow, payload) => {
|
|
600
|
+
const codec = Schema.toCodecJson(flow.payloadSchema);
|
|
601
|
+
return Schema.decodeUnknownEffect(codec)(payload).pipe(
|
|
602
|
+
Effect.orDie,
|
|
603
|
+
Effect.flatMap((decoded) => Schema.encodeEffect(codec)(decoded)),
|
|
604
|
+
Effect.orDie
|
|
605
|
+
);
|
|
606
|
+
};
|
|
607
|
+
const ensureCreatedRun = (options) => Effect.gen(function* () {
|
|
608
|
+
const created = yield* store.create(options.executionId, options.stateJson, {
|
|
609
|
+
...options.parentRunId === void 0 ? {} : { parentRunId: options.parentRunId },
|
|
610
|
+
lineageId: options.lineageId,
|
|
611
|
+
roundOrdinal: options.roundOrdinal
|
|
612
|
+
}).pipe(Effect.exit);
|
|
613
|
+
if (Exit.isSuccess(created)) {
|
|
614
|
+
return yield* options.onCreated;
|
|
615
|
+
}
|
|
616
|
+
const failure = Cause.findFail(created.cause);
|
|
617
|
+
if (Result.isFailure(failure)) {
|
|
618
|
+
return yield* Effect.failCause(failure.failure);
|
|
619
|
+
}
|
|
620
|
+
const error = failure.success.error;
|
|
621
|
+
if (!(error instanceof import_run_store.RunStore.RunStoreError) || error.code !== "constraint") {
|
|
622
|
+
return yield* Effect.die(error);
|
|
623
|
+
}
|
|
624
|
+
const existing = yield* store.get(options.executionId).pipe(Effect.orDie);
|
|
625
|
+
const persisted = yield* decodeState(existing.stateJson);
|
|
626
|
+
const sameParent = options.parentRunId === void 0 || existing.parentRunId === options.parentRunId;
|
|
627
|
+
const conflict = persisted.flowName !== options.flowName ? {
|
|
628
|
+
field: "flow",
|
|
629
|
+
expected: persisted.flowName,
|
|
630
|
+
actual: options.flowName
|
|
631
|
+
} : !samePayload(persisted.payload, options.payload) ? {
|
|
632
|
+
field: "payload",
|
|
633
|
+
expected: "the encoded payload the execution was admitted with",
|
|
634
|
+
actual: "a different encoded payload"
|
|
635
|
+
} : !import_engine.FlowEngine.joinable(
|
|
636
|
+
// A row with no persisted authority ran unrestricted, and is
|
|
637
|
+
// refused further dispatch; only an unrestricted caller joins it.
|
|
638
|
+
persisted.capabilityCeilings ?? [],
|
|
639
|
+
options.capabilityCeilings
|
|
640
|
+
) ? {
|
|
641
|
+
// A join answers the result the admitted authority produced.
|
|
642
|
+
field: "capabilities",
|
|
643
|
+
expected: "a capability ceiling covering the one the execution was admitted with",
|
|
644
|
+
actual: "a capability ceiling that does not cover it"
|
|
645
|
+
} : existing.lineageId !== options.lineageId ? {
|
|
646
|
+
field: "lineage",
|
|
647
|
+
expected: existing.lineageId ?? "no lineage",
|
|
648
|
+
actual: options.lineageId
|
|
649
|
+
} : existing.roundOrdinal !== options.roundOrdinal ? {
|
|
650
|
+
field: "round",
|
|
651
|
+
expected: String(existing.roundOrdinal),
|
|
652
|
+
actual: String(options.roundOrdinal)
|
|
653
|
+
} : !sameParent ? {
|
|
654
|
+
field: "parent",
|
|
655
|
+
expected: existing.parentRunId ?? "no parent",
|
|
656
|
+
// `!sameParent` implies the requested parent is present: an
|
|
657
|
+
// omitted parent is deliberately compatible with any persisted
|
|
658
|
+
// parent when joining an already-created root.
|
|
659
|
+
actual: options.parentRunId
|
|
660
|
+
} : void 0;
|
|
661
|
+
if (conflict !== void 0) {
|
|
662
|
+
return yield* Effect.die(
|
|
663
|
+
new import_engine.FlowEngine.ExecutionIdentityConflict({
|
|
664
|
+
executionId: options.executionId,
|
|
665
|
+
status: existing.status,
|
|
666
|
+
...conflict,
|
|
667
|
+
message: `execution ${options.executionId} already belongs to ${conflict.field} identity ${conflict.expected}; it cannot be reused for ${conflict.actual}`
|
|
668
|
+
})
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
const coordinatorDeferred = yield* Deferred.make();
|
|
673
|
+
const cancelPollLoop = (executionId) => Effect.gen(function* () {
|
|
674
|
+
while (true) {
|
|
675
|
+
const requested = yield* store.get(executionId).pipe(
|
|
676
|
+
Effect.map((row) => row.cancelRequestedAtMs !== null),
|
|
677
|
+
Effect.catch(() => Effect.succeed(false))
|
|
678
|
+
);
|
|
679
|
+
if (requested) {
|
|
680
|
+
yield* store.acknowledgeCancel(
|
|
681
|
+
executionId,
|
|
682
|
+
dependencies.owner,
|
|
683
|
+
yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor))
|
|
684
|
+
).pipe(Effect.orDie);
|
|
685
|
+
return cancelRequested;
|
|
686
|
+
}
|
|
687
|
+
yield* Effect.sleep(import_run_store.Ownership.heartbeatInterval);
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
const continueLineage = (seam, advanced) => Effect.gen(function* () {
|
|
691
|
+
const target = registrations.get(seam.handoff.flow)?.at(-1);
|
|
692
|
+
const payload = target === void 0 ? seam.handoff.payload : yield* normalizePayload(target.flow, seam.handoff.payload);
|
|
693
|
+
const nextCeilings = [
|
|
694
|
+
// The execution guard rejects missing persisted authority before a handler can hand off.
|
|
695
|
+
...seam.state.capabilityCeilings,
|
|
696
|
+
...seam.handoff.capabilityCeilings ?? [[]],
|
|
697
|
+
// The successor records its own declaration, as admission would, so
|
|
698
|
+
// a later join compares against the authority it actually runs
|
|
699
|
+
// under. A target this process does not run has none here; the root
|
|
700
|
+
// caller's re-entry then carries a declaration the row lacks and is
|
|
701
|
+
// refused rather than trusted.
|
|
702
|
+
...target === void 0 ? [] : import_flow.Flow.parseCapabilityCeilings(import_flow.Flow.capabilityCeilings(target.flow.annotations))
|
|
703
|
+
];
|
|
704
|
+
const nextStateJson = yield* encodeState({
|
|
705
|
+
version: 1,
|
|
706
|
+
flowName: seam.handoff.flow,
|
|
707
|
+
payload,
|
|
708
|
+
capabilityCeilings: nextCeilings,
|
|
709
|
+
...seam.state.parentExecutionId === void 0 ? {} : { parentExecutionId: seam.state.parentExecutionId },
|
|
710
|
+
...seam.state.onParentExit === void 0 ? {} : { onParentExit: seam.state.onParentExit },
|
|
711
|
+
...seam.state.maxRounds === void 0 ? {} : { maxRounds: seam.state.maxRounds },
|
|
712
|
+
...seam.handoff.deadline === void 0 ? {} : { deadline: seam.handoff.deadline }
|
|
713
|
+
});
|
|
714
|
+
const settledStateJson = yield* encodeState({
|
|
715
|
+
...seam.state,
|
|
716
|
+
result: (yield* encodeResult(seam.flow, seam.handoff)).encoded
|
|
717
|
+
});
|
|
718
|
+
const cancelled = { _tag: "HandoffCancelled" };
|
|
719
|
+
const fenceLost = { _tag: "HandoffFenceLost" };
|
|
720
|
+
const committed = yield* Effect.result(
|
|
721
|
+
transactState(Effect.gen(function* () {
|
|
722
|
+
yield* ensureCreatedRun({
|
|
723
|
+
flowName: seam.handoff.flow,
|
|
724
|
+
executionId: advanced.executionId,
|
|
725
|
+
stateJson: nextStateJson,
|
|
726
|
+
payload,
|
|
727
|
+
lineageId: advanced.round.rootExecutionId,
|
|
728
|
+
roundOrdinal: advanced.round.ordinal,
|
|
729
|
+
parentRunId: seam.executionId,
|
|
730
|
+
capabilityCeilings: nextCeilings,
|
|
731
|
+
onCreated: emitDecision(advanced.executionId, {
|
|
732
|
+
decision: "created",
|
|
733
|
+
state: JSON.parse(nextStateJson),
|
|
734
|
+
lineageId: advanced.round.rootExecutionId,
|
|
735
|
+
roundOrdinal: advanced.round.ordinal,
|
|
736
|
+
parentExecutionId: seam.executionId
|
|
737
|
+
})
|
|
738
|
+
});
|
|
739
|
+
const transitioned = yield* transitionAndRecord(
|
|
740
|
+
seam.executionId,
|
|
741
|
+
"completed",
|
|
742
|
+
settledStateJson,
|
|
743
|
+
{
|
|
744
|
+
decision: "handed-off",
|
|
745
|
+
status: "completed",
|
|
746
|
+
flow: seam.handoff.flow,
|
|
747
|
+
lineageId: advanced.round.rootExecutionId,
|
|
748
|
+
roundOrdinal: advanced.round.ordinal,
|
|
749
|
+
nextExecutionId: advanced.executionId,
|
|
750
|
+
owner: dependencies.owner
|
|
751
|
+
},
|
|
752
|
+
{ cancelRequested: "absent" }
|
|
753
|
+
);
|
|
754
|
+
if (transitioned._tag === "Transitioned") return;
|
|
755
|
+
return yield* Effect.fail(
|
|
756
|
+
transitioned._tag === "GuardFailed" ? cancelled : fenceLost
|
|
757
|
+
);
|
|
758
|
+
})).pipe(Effect.catchTag("@smthrs/journal/JournalError", Effect.die))
|
|
759
|
+
);
|
|
760
|
+
if (Result.isFailure(committed)) {
|
|
761
|
+
if (committed.failure._tag === "HandoffCancelled") {
|
|
762
|
+
yield* cancelOwned(seam.executionId, seam.state);
|
|
763
|
+
}
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
const activeCoordinator = yield* Deferred.await(coordinatorDeferred);
|
|
767
|
+
yield* activeCoordinator.wake(advanced.executionId);
|
|
768
|
+
});
|
|
769
|
+
const announceSettled = (executionId, state) => Effect.gen(function* () {
|
|
770
|
+
yield* wakeBus.wake(executionId);
|
|
771
|
+
const parents = /* @__PURE__ */ new Set();
|
|
772
|
+
if (state.parentExecutionId !== void 0) parents.add(state.parentExecutionId);
|
|
773
|
+
const rounds = yield* lifecycle.rounds(executionId).pipe(Effect.orDie);
|
|
774
|
+
for (const roundId of /* @__PURE__ */ new Set([executionId, ...rounds.map((round) => round.runId)])) {
|
|
775
|
+
for (const edge of yield* engineState.runParents(roundId)) parents.add(edge.parentId);
|
|
776
|
+
}
|
|
777
|
+
if (parents.size === 0) return;
|
|
778
|
+
const activeCoordinator = yield* Deferred.await(coordinatorDeferred);
|
|
779
|
+
for (const parentId of parents) {
|
|
780
|
+
yield* recordParentResume(parentId);
|
|
781
|
+
yield* activeCoordinator.wake(parentId);
|
|
782
|
+
yield* wakeBus.wake(parentId);
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
const endLineage = (seam, error) => Effect.gen(function* () {
|
|
786
|
+
const stateJson = yield* encodeState({
|
|
787
|
+
...seam.state,
|
|
788
|
+
result: (yield* encodeResult(seam.flow, new import_flow.Flow.Complete({ exit: Exit.die(error) }))).encoded
|
|
789
|
+
});
|
|
790
|
+
const transitioned = yield* transitionAndRecord(
|
|
791
|
+
seam.executionId,
|
|
792
|
+
"failed",
|
|
793
|
+
stateJson,
|
|
794
|
+
{
|
|
795
|
+
decision: "lineage-exhausted",
|
|
796
|
+
status: "failed",
|
|
797
|
+
lineageId: error.lineageId,
|
|
798
|
+
maxRounds: error.maxRounds,
|
|
799
|
+
owner: dependencies.owner
|
|
800
|
+
},
|
|
801
|
+
{ cancelRequested: "absent" },
|
|
802
|
+
// The lineage is over, not continuing: this round is the last one,
|
|
803
|
+
// so it ends its children like any other terminal exit. A round that
|
|
804
|
+
// HANDS OFF settles through `continueLineage` instead and deliberately
|
|
805
|
+
// does not, because the lineage — and therefore the parent the
|
|
806
|
+
// children are linked to — carries on.
|
|
807
|
+
applyChildExitPolicy(seam.executionId)
|
|
808
|
+
);
|
|
809
|
+
if (transitioned._tag === "GuardFailed") {
|
|
810
|
+
yield* cancelOwned(seam.executionId, seam.state);
|
|
811
|
+
} else if (transitioned._tag === "Transitioned") {
|
|
812
|
+
yield* announceSettled(seam.executionId, seam.state);
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
const endInvalidRound = (seam, error) => Effect.gen(function* () {
|
|
816
|
+
const stateJson = yield* encodeState({
|
|
817
|
+
...seam.state,
|
|
818
|
+
result: (yield* encodeResult(seam.flow, new import_flow.Flow.Complete({ exit: Exit.die(error) }))).encoded
|
|
819
|
+
});
|
|
820
|
+
const transitioned = yield* transitionAndRecord(
|
|
821
|
+
seam.executionId,
|
|
822
|
+
"failed",
|
|
823
|
+
stateJson,
|
|
824
|
+
{
|
|
825
|
+
decision: "round-invalid",
|
|
826
|
+
status: "failed",
|
|
827
|
+
message: error.message,
|
|
828
|
+
owner: dependencies.owner
|
|
829
|
+
},
|
|
830
|
+
{ cancelRequested: "absent" },
|
|
831
|
+
applyChildExitPolicy(seam.executionId)
|
|
832
|
+
);
|
|
833
|
+
if (transitioned._tag === "GuardFailed") {
|
|
834
|
+
yield* cancelOwned(seam.executionId, seam.state);
|
|
835
|
+
} else if (transitioned._tag === "Transitioned") {
|
|
836
|
+
yield* announceSettled(seam.executionId, seam.state);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
const handOff = (seam) => import_engine.FlowEngine.Round.next(
|
|
840
|
+
{
|
|
841
|
+
// The row's lineage_id was written from a round's rootExecutionId,
|
|
842
|
+
// so this rehydrates the brand; `Round.next` still validates it.
|
|
843
|
+
rootExecutionId: seam.row.lineageId,
|
|
844
|
+
ordinal: seam.row.roundOrdinal
|
|
845
|
+
},
|
|
846
|
+
// The origin persisted its budget into every round's state, so a
|
|
847
|
+
// multi-flow handoff cannot reset the lineage by changing targets.
|
|
848
|
+
{ flowName: seam.flow._tag, maxRounds: seam.state.maxRounds }
|
|
849
|
+
).pipe(
|
|
850
|
+
Effect.flatMap((advanced) => continueLineage(seam, advanced)),
|
|
851
|
+
Effect.catch(
|
|
852
|
+
(error) => error instanceof import_engine.FlowEngine.Round.InvalidRound ? endInvalidRound(seam, error) : endLineage(seam, error)
|
|
853
|
+
)
|
|
854
|
+
);
|
|
855
|
+
const drive = (executionId) => Effect.suspend(() => {
|
|
856
|
+
let interruptState;
|
|
857
|
+
let round;
|
|
858
|
+
let durableParkAccepted = false;
|
|
859
|
+
let lapsedMs;
|
|
860
|
+
return Effect.gen(function* () {
|
|
861
|
+
const initial = yield* store.get(executionId).pipe(
|
|
862
|
+
Effect.catch(
|
|
863
|
+
(error) => error.code === "not_found_row" ? Effect.succeed(void 0) : Effect.die(error)
|
|
864
|
+
)
|
|
865
|
+
);
|
|
866
|
+
if (initial === void 0) return;
|
|
867
|
+
const state = yield* decodeState(initial.stateJson);
|
|
868
|
+
interruptState = state;
|
|
869
|
+
const registration = registrations.get(state.flowName)?.at(-1);
|
|
870
|
+
if (registration === void 0) {
|
|
871
|
+
if (initial.status === "cancelled" || initial.status === "completed" || initial.status === "failed") return;
|
|
872
|
+
if ((initial.status === "pending" || initial.status === "suspended" || initial.status === "running") && initial.cancelRequestedAtMs !== null) {
|
|
873
|
+
if (!(yield* claimAndActivate(initial))) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
return yield* cancelOwned(executionId, withoutResult(state));
|
|
877
|
+
}
|
|
878
|
+
if (!warnedUnregistered.has(executionId)) {
|
|
879
|
+
warnedUnregistered.add(executionId);
|
|
880
|
+
yield* Effect.logWarning(
|
|
881
|
+
`engine-store: run ${executionId} woke for flow ${state.flowName}, which is not registered in this process; leaving it parked for a worker that registers the flow`,
|
|
882
|
+
{ runId: executionId, flowName: state.flowName }
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (!(yield* claimAndActivate(initial))) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
yield* Effect.logDebug("run activated", { flowName: state.flowName });
|
|
891
|
+
const activeState = withoutResult(state);
|
|
892
|
+
const cleared = yield* transitionAndRecord(
|
|
893
|
+
executionId,
|
|
894
|
+
"running",
|
|
895
|
+
yield* encodeState(activeState),
|
|
896
|
+
{ decision: "resumed", status: "running" },
|
|
897
|
+
{ cancelRequested: "absent" },
|
|
898
|
+
void 0,
|
|
899
|
+
null
|
|
900
|
+
);
|
|
901
|
+
if (cleared._tag === "GuardFailed") {
|
|
902
|
+
return yield* cancelOwned(executionId, withoutResult(state));
|
|
903
|
+
}
|
|
904
|
+
if (cleared._tag !== "Transitioned") return;
|
|
905
|
+
const payload = yield* Schema.decodeUnknownEffect(
|
|
906
|
+
Schema.toCodecJson(registration.flow.payloadSchema)
|
|
907
|
+
)(activeState.payload).pipe(Effect.orDie);
|
|
908
|
+
yield* releaseRetainedScope(executionId, Exit.void);
|
|
909
|
+
const instance = import_engine.FlowEngine.makeInstance(
|
|
910
|
+
registration.flow,
|
|
911
|
+
executionId,
|
|
912
|
+
activeState.deadline
|
|
913
|
+
);
|
|
914
|
+
round = { flow: registration.flow, instance };
|
|
915
|
+
const flowEngine = yield* dependencies.engine;
|
|
916
|
+
yield* Effect.gen(function* () {
|
|
917
|
+
liveInstances.set(executionId, instance);
|
|
918
|
+
const result = yield* Effect.scoped(
|
|
919
|
+
Effect.raceFirst(
|
|
920
|
+
Effect.raceFirst(
|
|
921
|
+
Effect.suspend(
|
|
922
|
+
() => activeState.capabilityCeilings === void 0 ? Effect.die(new Error("Missing persisted capabilityCeilings; execution cannot safely resume")) : registration.execute(payload, executionId)
|
|
923
|
+
).pipe(
|
|
924
|
+
CapabilitySet.attenuateGroups(activeState.capabilityCeilings ?? [[]]),
|
|
925
|
+
import_flow.Flow.intoResult,
|
|
926
|
+
Effect.provideService(import_flow.FlowRuntime.FlowInstance, instance),
|
|
927
|
+
Effect.provideService(import_flow.FlowRuntime.FlowRuntime, flowEngine)
|
|
928
|
+
),
|
|
929
|
+
import_run_store.Ownership.heartbeatLoop(executionId, dependencies.owner, {
|
|
930
|
+
onReconfirm: (unconfirmedMs) => {
|
|
931
|
+
const record = decisionRecord(executionId, {
|
|
932
|
+
decision: "lease-reconfirmed",
|
|
933
|
+
detail: { unconfirmedMs }
|
|
934
|
+
}, dependencies.journalSource);
|
|
935
|
+
return journal.emitDurable(record, dependencies.owner).pipe(Effect.asVoid, Effect.orDie);
|
|
936
|
+
},
|
|
937
|
+
onLapse: (unconfirmedMs) => Effect.sync(() => {
|
|
938
|
+
lapsedMs = unconfirmedMs;
|
|
939
|
+
})
|
|
940
|
+
}).pipe(
|
|
941
|
+
Effect.provideService(import_run_store.RunStore.RunStore, store)
|
|
942
|
+
)
|
|
943
|
+
),
|
|
944
|
+
cancelPollLoop(executionId)
|
|
945
|
+
)
|
|
946
|
+
).pipe(
|
|
947
|
+
Effect.ensuring(Effect.sync(() => liveInstances.delete(executionId))),
|
|
948
|
+
// A suspension is the ONE settlement `Flow.intoResult` leaves the
|
|
949
|
+
// flow scope open for, so from the instant it surfaces the scope
|
|
950
|
+
// needs an owner. Retention is taken HERE, inside the race's own
|
|
951
|
+
// exit processing — `Effect.onExit` finalizers run uninterruptibly —
|
|
952
|
+
// because taking it any later crosses an interruptible boundary: an
|
|
953
|
+
// interrupt landing there would end the drive with the scope never
|
|
954
|
+
// retained, unreachable, and its finalizers never run at all. The
|
|
955
|
+
// entry is inserted into a cleanup region that was registered before
|
|
956
|
+
// this race started, so it is owned from this instant on: every
|
|
957
|
+
// ending, including an interrupt observed at the boundary between
|
|
958
|
+
// this finalizer and the settlement below, releases it there.
|
|
959
|
+
Effect.onExit(
|
|
960
|
+
(exit) => Effect.suspend(() => {
|
|
961
|
+
if (!(Exit.isSuccess(exit) && exit.value._tag === "Suspended")) return Effect.void;
|
|
962
|
+
retainedScopes.set(executionId, instance.scope);
|
|
963
|
+
return dependencies.unsafeOnScopeRetained?.(executionId) ?? Effect.void;
|
|
964
|
+
})
|
|
965
|
+
)
|
|
966
|
+
);
|
|
967
|
+
const settleRound = Effect.gen(function* () {
|
|
968
|
+
if (result._tag === "CancelRequested") {
|
|
969
|
+
yield* cancelOwned(executionId, activeState);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const quarantine = result._tag === "Complete" && Exit.isFailure(result.exit) ? ActionPersistence.evidenceQuarantined(result.exit.cause) : void 0;
|
|
973
|
+
if (quarantine !== void 0) {
|
|
974
|
+
const parked = yield* transitionAndRecord(
|
|
975
|
+
executionId,
|
|
976
|
+
"suspended",
|
|
977
|
+
yield* encodeState(withoutResult(activeState)),
|
|
978
|
+
{
|
|
979
|
+
decision: "quarantined",
|
|
980
|
+
status: "suspended",
|
|
981
|
+
keyDigest: quarantine.keyDigest,
|
|
982
|
+
owner: dependencies.owner
|
|
983
|
+
},
|
|
984
|
+
{ cancelRequested: "absent" },
|
|
985
|
+
void 0,
|
|
986
|
+
{ reason: "quarantine", token: quarantine.keyDigest }
|
|
987
|
+
);
|
|
988
|
+
if (parked._tag === "GuardFailed") {
|
|
989
|
+
yield* cancelOwned(executionId, activeState);
|
|
990
|
+
}
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (result._tag === "Handoff") {
|
|
994
|
+
yield* handOff({
|
|
995
|
+
executionId,
|
|
996
|
+
row: initial,
|
|
997
|
+
state: activeState,
|
|
998
|
+
flow: registration.flow,
|
|
999
|
+
handoff: result
|
|
1000
|
+
});
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const encodedResult = yield* encodeResult(registration.flow, result);
|
|
1004
|
+
const nextState = { ...activeState, result: encodedResult.encoded };
|
|
1005
|
+
const status = encodedResult.note !== void 0 ? "failed" : result._tag === "Suspended" ? "suspended" : Exit.isSuccess(result.exit) ? "completed" : "failed";
|
|
1006
|
+
let waiting = null;
|
|
1007
|
+
if (status === "suspended") {
|
|
1008
|
+
const declared = instance.waiting;
|
|
1009
|
+
const pendingClocks = yield* engineState.pendingClocks({ executionId });
|
|
1010
|
+
waiting = declared !== void 0 ? declared : pendingClocks.length > 0 ? {
|
|
1011
|
+
reason: "timer",
|
|
1012
|
+
wakeAt: Math.min(...pendingClocks.map((clock) => clock.dueAtMs))
|
|
1013
|
+
} : { reason: "event" };
|
|
1014
|
+
}
|
|
1015
|
+
const transitioned = yield* transitionAndRecord(
|
|
1016
|
+
executionId,
|
|
1017
|
+
status,
|
|
1018
|
+
yield* encodeState(nextState),
|
|
1019
|
+
{ decision: "transitioned", status, owner: dependencies.owner },
|
|
1020
|
+
{ cancelRequested: "absent" },
|
|
1021
|
+
// A suspension is not an exit: the run is parked and will settle
|
|
1022
|
+
// later, so its children keep going and its timers stay armed. A
|
|
1023
|
+
// `completed` or `failed` run is done, so its attached children
|
|
1024
|
+
// end with it. The transition closes its own clock rows.
|
|
1025
|
+
status === "suspended" ? void 0 : applyChildExitPolicy(executionId),
|
|
1026
|
+
waiting
|
|
1027
|
+
).pipe(
|
|
1028
|
+
// The park becomes durable the instant this transition commits, so
|
|
1029
|
+
// the flag that records it is set in the transition's own exit
|
|
1030
|
+
// processing. `Effect.onExit` finalizers run uninterruptibly and
|
|
1031
|
+
// before the fiber can observe an interruption again, so no
|
|
1032
|
+
// interrupt can land between the durable commit and the flag and
|
|
1033
|
+
// make the cleanup region discard a scope the park still needs.
|
|
1034
|
+
Effect.onExit(
|
|
1035
|
+
(exit) => Effect.sync(() => {
|
|
1036
|
+
if (status === "suspended" && Exit.isSuccess(exit) && exit.value._tag === "Transitioned") {
|
|
1037
|
+
durableParkAccepted = true;
|
|
1038
|
+
}
|
|
1039
|
+
})
|
|
1040
|
+
)
|
|
1041
|
+
);
|
|
1042
|
+
if (transitioned._tag === "GuardFailed") {
|
|
1043
|
+
yield* cancelOwned(executionId, activeState);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (transitioned._tag !== "Transitioned") return;
|
|
1047
|
+
if (status !== "suspended") {
|
|
1048
|
+
yield* announceSettled(executionId, activeState);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
yield* settleRound;
|
|
1053
|
+
});
|
|
1054
|
+
}).pipe(
|
|
1055
|
+
// Install ownership cleanup before the claim itself. Limiting it to
|
|
1056
|
+
// the execution race strands an activated row if shutdown lands in
|
|
1057
|
+
// setup or settlement; a replacement host in the same process then
|
|
1058
|
+
// correctly refuses to steal from the still-live PID forever.
|
|
1059
|
+
Effect.onInterrupt(
|
|
1060
|
+
() => interruptState === void 0 ? Effect.void : settleInterrupted(
|
|
1061
|
+
executionId,
|
|
1062
|
+
interruptState,
|
|
1063
|
+
lapsedMs === void 0 ? { kind: "interrupted" } : { kind: "lease-lapsed", unconfirmedMs: Math.max(0, Math.floor(lapsedMs)) },
|
|
1064
|
+
round
|
|
1065
|
+
)
|
|
1066
|
+
),
|
|
1067
|
+
Effect.onExit(
|
|
1068
|
+
() => Effect.suspend(() => {
|
|
1069
|
+
liveInstances.delete(executionId);
|
|
1070
|
+
return round === void 0 || durableParkAccepted ? Effect.void : releaseRetainedScope(executionId, Exit.void);
|
|
1071
|
+
})
|
|
1072
|
+
),
|
|
1073
|
+
Effect.annotateLogs({ runId: executionId })
|
|
1074
|
+
);
|
|
1075
|
+
});
|
|
1076
|
+
const coordinator = yield* RunCoordinator.make({
|
|
1077
|
+
drain: drive
|
|
1078
|
+
});
|
|
1079
|
+
yield* Deferred.succeed(coordinatorDeferred, coordinator);
|
|
1080
|
+
const sweepCancelRequested = Effect.gen(function* () {
|
|
1081
|
+
const released = yield* engineState.waitingRuns({ reason: "released" });
|
|
1082
|
+
const cancelRequestedRows = yield* engineState.waitingRuns({ cancelRequested: true });
|
|
1083
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
1084
|
+
for (const waiting of released) candidates.set(waiting.runId, waiting);
|
|
1085
|
+
for (const waiting of cancelRequestedRows) candidates.set(waiting.runId, waiting);
|
|
1086
|
+
for (const waiting of candidates.values()) {
|
|
1087
|
+
const row = yield* store.get(waiting.runId).pipe(
|
|
1088
|
+
Effect.catch(() => Effect.succeed(void 0))
|
|
1089
|
+
);
|
|
1090
|
+
if (row !== void 0 && row.status === "suspended" && (row.cancelRequestedAtMs !== null || waiting.reason === "released")) {
|
|
1091
|
+
yield* coordinator.wake(row.runId);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
const sweepReadyTimers = Effect.gen(function* () {
|
|
1096
|
+
const nowMs = yield* Clock.currentTimeMillis;
|
|
1097
|
+
const due = yield* engineState.waitingRuns({ reason: "timer", dueBeforeMs: nowMs });
|
|
1098
|
+
const completions = /* @__PURE__ */ new Map();
|
|
1099
|
+
for (const waiting of due) {
|
|
1100
|
+
const row = yield* store.get(waiting.runId).pipe(Effect.catch(() => Effect.succeed(void 0)));
|
|
1101
|
+
if (row === void 0 || row.status !== "suspended") continue;
|
|
1102
|
+
const state = yield* decodeState(row.stateJson);
|
|
1103
|
+
if (!registrations.has(state.flowName)) continue;
|
|
1104
|
+
let ready = completions.get(state.flowName);
|
|
1105
|
+
if (ready === void 0) {
|
|
1106
|
+
ready = new Set((yield* engineState.completedDeferreds(state.flowName)).map((address) => address.executionId));
|
|
1107
|
+
completions.set(state.flowName, ready);
|
|
1108
|
+
}
|
|
1109
|
+
if (!ready.has(row.runId)) continue;
|
|
1110
|
+
yield* coordinator.schedule(row.runId);
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
const pendingCycles = /* @__PURE__ */ new Map();
|
|
1114
|
+
let pendingCancellationCycle;
|
|
1115
|
+
const sweepPendingCancellation = () => Effect.gen(function* () {
|
|
1116
|
+
if (pendingCancellationCycle === void 0) {
|
|
1117
|
+
const tail = yield* engineState.pendingCancellationTail();
|
|
1118
|
+
if (Option.isNone(tail)) return;
|
|
1119
|
+
pendingCancellationCycle = { through: tail.value };
|
|
1120
|
+
}
|
|
1121
|
+
const cycle = pendingCancellationCycle;
|
|
1122
|
+
const candidates = yield* engineState.pendingCancellationRuns(
|
|
1123
|
+
staleRunningSweepBatch,
|
|
1124
|
+
cycle.after,
|
|
1125
|
+
cycle.through
|
|
1126
|
+
);
|
|
1127
|
+
for (const cursor of candidates) {
|
|
1128
|
+
const row = yield* store.get(cursor.runId).pipe(
|
|
1129
|
+
Effect.catch(
|
|
1130
|
+
(error) => error.code === "not_found_row" ? Effect.succeed(void 0) : error.code === "decode_failed" ? Effect.logWarning("engine-store: unreadable pending cancellation skipped", {
|
|
1131
|
+
runId: cursor.runId,
|
|
1132
|
+
error
|
|
1133
|
+
}).pipe(Effect.as(void 0)) : Effect.die(error)
|
|
1134
|
+
)
|
|
1135
|
+
);
|
|
1136
|
+
cycle.after = cursor;
|
|
1137
|
+
if (row === void 0 || row.status !== "pending" || row.cancelRequestedAtMs === null || (yield* coordinator.active).has(cursor.runId)) continue;
|
|
1138
|
+
if (dependencies.canExecute !== void 0 && !(yield* dependencies.canExecute(row))) continue;
|
|
1139
|
+
if (!(yield* coordinator.active).has(cursor.runId)) yield* coordinator.schedule(cursor.runId);
|
|
1140
|
+
}
|
|
1141
|
+
if (candidates.length < staleRunningSweepBatch || candidates.at(-1)?.runId === cycle.through.runId) pendingCancellationCycle = void 0;
|
|
1142
|
+
});
|
|
1143
|
+
let pendingFlowOffset = 0;
|
|
1144
|
+
const sweepPending = () => Effect.gen(function* () {
|
|
1145
|
+
let remaining = staleRunningSweepBatch;
|
|
1146
|
+
const allNames = Array.from(registrations.keys());
|
|
1147
|
+
const offset = allNames.length === 0 ? 0 : pendingFlowOffset % allNames.length;
|
|
1148
|
+
const names = [...allNames.slice(offset), ...allNames.slice(0, offset)];
|
|
1149
|
+
pendingFlowOffset++;
|
|
1150
|
+
for (const name of names) {
|
|
1151
|
+
const registration = registrations.get(name)?.at(-1);
|
|
1152
|
+
if (registration === void 0 || remaining === 0) continue;
|
|
1153
|
+
let cycle = pendingCycles.get(name);
|
|
1154
|
+
if (cycle === void 0) {
|
|
1155
|
+
const eligibleBeforeMs = (yield* Clock.currentTimeMillis) - Duration.toMillis(import_run_store.Ownership.heartbeatStaleAfter);
|
|
1156
|
+
const tail = yield* engineState.pendingRunTail(name, eligibleBeforeMs);
|
|
1157
|
+
if (Option.isNone(tail)) continue;
|
|
1158
|
+
cycle = { through: tail.value, eligibleBeforeMs };
|
|
1159
|
+
pendingCycles.set(name, cycle);
|
|
1160
|
+
}
|
|
1161
|
+
const requested = remaining;
|
|
1162
|
+
const pending = yield* engineState.pendingRuns(
|
|
1163
|
+
name,
|
|
1164
|
+
requested,
|
|
1165
|
+
cycle.after,
|
|
1166
|
+
cycle.through,
|
|
1167
|
+
cycle.eligibleBeforeMs
|
|
1168
|
+
);
|
|
1169
|
+
for (const cursor of pending) {
|
|
1170
|
+
if (remaining === 0 || registrations.get(name)?.at(-1) !== registration) break;
|
|
1171
|
+
remaining--;
|
|
1172
|
+
const row = yield* store.get(cursor.runId).pipe(
|
|
1173
|
+
Effect.catch(
|
|
1174
|
+
(error) => error.code === "not_found_row" ? Effect.succeed(void 0) : error.code === "decode_failed" ? Effect.logWarning("engine-store: unreadable pending admission skipped", {
|
|
1175
|
+
runId: cursor.runId,
|
|
1176
|
+
flowName: name,
|
|
1177
|
+
error
|
|
1178
|
+
}).pipe(Effect.as(void 0)) : Effect.die(error)
|
|
1179
|
+
)
|
|
1180
|
+
);
|
|
1181
|
+
cycle.after = cursor;
|
|
1182
|
+
if (row === void 0 || row.status !== "pending" || (yield* coordinator.active).has(cursor.runId)) {
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
if (dependencies.canExecute !== void 0 && !(yield* dependencies.canExecute(row))) {
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
if (registrations.get(name)?.at(-1) !== registration || (yield* coordinator.active).has(cursor.runId)) {
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
yield* coordinator.schedule(cursor.runId);
|
|
1192
|
+
}
|
|
1193
|
+
const last = pending.at(-1);
|
|
1194
|
+
if (pending.length < requested || last !== void 0 && last.runId === cycle.through.runId) {
|
|
1195
|
+
pendingCycles.delete(name);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
const sweepStaleRunning = Effect.gen(function* () {
|
|
1200
|
+
const nowMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor));
|
|
1201
|
+
const deferred = Array.from(stealRefusals.values()).filter((entry) => entry.nextProbeAtMs > nowMs).length;
|
|
1202
|
+
const stale = yield* engineState.staleRunningRuns(
|
|
1203
|
+
nowMs - Duration.toMillis(import_run_store.Ownership.heartbeatStaleAfter),
|
|
1204
|
+
staleRunningSweepBatch + deferred
|
|
1205
|
+
);
|
|
1206
|
+
const visible = new Set(stale.map((row) => row.runId));
|
|
1207
|
+
for (const runId of stealRefusals.keys()) {
|
|
1208
|
+
if (!visible.has(runId)) stealRefusals.delete(runId);
|
|
1209
|
+
}
|
|
1210
|
+
let woken = 0;
|
|
1211
|
+
for (const { heartbeatAtMs, runId } of stale) {
|
|
1212
|
+
if (woken >= staleRunningSweepBatch) break;
|
|
1213
|
+
const refusal = stealRefusals.get(runId);
|
|
1214
|
+
if (refusal !== void 0 && refusal.heartbeatAtMs === heartbeatAtMs && refusal.nextProbeAtMs > nowMs) continue;
|
|
1215
|
+
woken = woken + 1;
|
|
1216
|
+
yield* coordinator.wake(runId);
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
const resilientSweep = (sweep, name) => sweep.pipe(
|
|
1220
|
+
Effect.sandbox,
|
|
1221
|
+
Effect.catchCause((cause) => Effect.logWarning(`engine-store: ${name} sweep failed; retrying next tick`, cause))
|
|
1222
|
+
);
|
|
1223
|
+
yield* Effect.forkScoped(
|
|
1224
|
+
Effect.forever(
|
|
1225
|
+
Effect.sleep(import_run_store.Ownership.heartbeatInterval).pipe(
|
|
1226
|
+
Effect.andThen(
|
|
1227
|
+
// One transient defect (a `SQLITE_BUSY` escaping `waitingRuns()`'s
|
|
1228
|
+
// `orDie`, a wake failure) must not kill the sweeper for the rest
|
|
1229
|
+
// of the process lifetime (issue #44) — that would silently revert
|
|
1230
|
+
// to pre-#27 behavior where cancel of parked runs is never
|
|
1231
|
+
// delivered. Mirror `armClock`'s hardening: expose the full cause,
|
|
1232
|
+
// log it, and keep ticking.
|
|
1233
|
+
resilientSweep(sweepCancelRequested, "parked-run cancel").pipe(
|
|
1234
|
+
Effect.andThen(resilientSweep(sweepStaleRunning, "stale-running")),
|
|
1235
|
+
Effect.andThen(resilientSweep(sweepReadyTimers, "ready timer")),
|
|
1236
|
+
Effect.andThen(resilientSweep(sweepPendingCancellation(), "pending cancellation")),
|
|
1237
|
+
Effect.andThen(resilientSweep(sweepPending(), "pending admission"))
|
|
1238
|
+
)
|
|
1239
|
+
)
|
|
1240
|
+
)
|
|
1241
|
+
)
|
|
1242
|
+
);
|
|
1243
|
+
const ensureRun = (flow, options) => Effect.gen(function* () {
|
|
1244
|
+
const payload = yield* Schema.encodeEffect(
|
|
1245
|
+
Schema.toCodecJson(flow.payloadSchema)
|
|
1246
|
+
)(
|
|
1247
|
+
options.payload
|
|
1248
|
+
).pipe(Effect.orDie);
|
|
1249
|
+
yield* transactState(Effect.gen(function* () {
|
|
1250
|
+
let parentRecorded = false;
|
|
1251
|
+
if (options.parent !== void 0) {
|
|
1252
|
+
const parentEdge = yield* engineState.recordRunParent(
|
|
1253
|
+
options.executionId,
|
|
1254
|
+
options.parent.executionId
|
|
1255
|
+
).pipe(
|
|
1256
|
+
Effect.catch(
|
|
1257
|
+
(error) => Effect.fail(
|
|
1258
|
+
new FlowCycleDetected({ code: "flow_cycle_detected", path: error.path })
|
|
1259
|
+
)
|
|
1260
|
+
)
|
|
1261
|
+
);
|
|
1262
|
+
parentRecorded = parentEdge._tag === "Recorded";
|
|
1263
|
+
}
|
|
1264
|
+
const round = options.round ?? import_engine.FlowEngine.Round.initial(options.executionId);
|
|
1265
|
+
const previousExecutionId = options.round?.previousExecutionId;
|
|
1266
|
+
const onParentExit = options.discard === true ? "detach" : "cancel";
|
|
1267
|
+
const state = {
|
|
1268
|
+
version: 1,
|
|
1269
|
+
flowName: flow._tag,
|
|
1270
|
+
payload,
|
|
1271
|
+
capabilityCeilings: yield* import_engine.FlowEngine.requestedAuthority(flow),
|
|
1272
|
+
onParentExit,
|
|
1273
|
+
...options.parent === void 0 ? {} : { parentExecutionId: options.parent.executionId },
|
|
1274
|
+
...flow.maxRounds === void 0 ? {} : { maxRounds: flow.maxRounds }
|
|
1275
|
+
};
|
|
1276
|
+
const createdStateJson = yield* encodeState(state);
|
|
1277
|
+
yield* ensureCreatedRun({
|
|
1278
|
+
flowName: flow._tag,
|
|
1279
|
+
capabilityCeilings: state.capabilityCeilings,
|
|
1280
|
+
executionId: options.executionId,
|
|
1281
|
+
stateJson: createdStateJson,
|
|
1282
|
+
payload,
|
|
1283
|
+
lineageId: round.rootExecutionId,
|
|
1284
|
+
roundOrdinal: round.ordinal,
|
|
1285
|
+
...previousExecutionId === void 0 ? {} : { parentRunId: previousExecutionId },
|
|
1286
|
+
onCreated: Effect.gen(function* () {
|
|
1287
|
+
yield* emitDecision(options.executionId, {
|
|
1288
|
+
decision: "created",
|
|
1289
|
+
state: JSON.parse(createdStateJson),
|
|
1290
|
+
...options.parent === void 0 ? {} : { parentExecutionId: options.parent.executionId }
|
|
1291
|
+
});
|
|
1292
|
+
if (options.parent !== void 0) {
|
|
1293
|
+
yield* journal.emitDurableUnfenced(
|
|
1294
|
+
EffectRecords.boundary(
|
|
1295
|
+
{
|
|
1296
|
+
id: `${options.parent.executionId}:spawn:${options.executionId}`,
|
|
1297
|
+
kind: spawnEffectKind,
|
|
1298
|
+
tier: "irreversible",
|
|
1299
|
+
runId: options.parent.executionId,
|
|
1300
|
+
lineageId: import_engine.FlowEngine.Lineage.root(options.parent.executionId),
|
|
1301
|
+
sourceId: dependencies.journalSource,
|
|
1302
|
+
attempt: 1,
|
|
1303
|
+
residue: `Child run ${options.executionId} exists and keeps its own journal; rewinding past its spawn orphans it.`
|
|
1304
|
+
},
|
|
1305
|
+
"succeeded",
|
|
1306
|
+
// `attached` is written even though it is always false: the
|
|
1307
|
+
// lineage-tree bridge in `@smthrs/time-travel` reads it off
|
|
1308
|
+
// this payload, and an absent field there would make "this
|
|
1309
|
+
// spawn is detached" indistinguishable from "this producer
|
|
1310
|
+
// predates the field". A run created with a parent is a
|
|
1311
|
+
// separate run row with its own claim, which is what detached
|
|
1312
|
+
// means; attached nesting
|
|
1313
|
+
// never reaches `create` because it is one journal.
|
|
1314
|
+
{ childRunId: options.executionId, flowName: flow._tag, attached: false }
|
|
1315
|
+
)
|
|
1316
|
+
).pipe(Effect.orDie);
|
|
1317
|
+
}
|
|
1318
|
+
})
|
|
1319
|
+
});
|
|
1320
|
+
if (options.parent !== void 0) {
|
|
1321
|
+
yield* inheritParentCancellation(
|
|
1322
|
+
options.parent.executionId,
|
|
1323
|
+
options.executionId
|
|
1324
|
+
);
|
|
1325
|
+
if (parentRecorded) {
|
|
1326
|
+
yield* emitDecision(options.executionId, {
|
|
1327
|
+
decision: "parent-recorded",
|
|
1328
|
+
parentExecutionId: options.parent.executionId
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
})).pipe(
|
|
1333
|
+
Effect.catchIf((error) => error instanceof import_journal.Journal.JournalError || error instanceof import_DurableWriter.DatabaseError, Effect.die)
|
|
1334
|
+
);
|
|
1335
|
+
});
|
|
1336
|
+
const readResult = (flow, executionId, followLineage = false) => Effect.annotateCurrentSpan({ executionId, flow: flow._tag }).pipe(
|
|
1337
|
+
Effect.andThen(import_engine.FlowEngine.requestedAuthority(flow)),
|
|
1338
|
+
Effect.flatMap(
|
|
1339
|
+
(requested) => Effect.map(followLineage ? store.latestRound(executionId) : store.get(executionId), (row) => ({
|
|
1340
|
+
row,
|
|
1341
|
+
requested
|
|
1342
|
+
}))
|
|
1343
|
+
),
|
|
1344
|
+
Effect.catch(
|
|
1345
|
+
(error) => error.code === "not_found_row" ? Effect.succeed(void 0) : Effect.die(error)
|
|
1346
|
+
),
|
|
1347
|
+
Effect.flatMap((found) => {
|
|
1348
|
+
if (found === void 0) {
|
|
1349
|
+
return Effect.fail(
|
|
1350
|
+
new import_flow.FlowRuntime.FlowExecutionNotFound({
|
|
1351
|
+
code: "execution_not_found",
|
|
1352
|
+
executionId
|
|
1353
|
+
})
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
const { row, requested } = found;
|
|
1357
|
+
return decodeState(row.stateJson).pipe(
|
|
1358
|
+
Effect.flatMap((state) => {
|
|
1359
|
+
if (!followLineage && state.flowName !== flow._tag || state.result === void 0) {
|
|
1360
|
+
return Effect.succeedNone;
|
|
1361
|
+
}
|
|
1362
|
+
if (!import_engine.FlowEngine.joinable(state.capabilityCeilings ?? [], requested)) {
|
|
1363
|
+
return Effect.die(import_engine.FlowEngine.capabilityConflict(executionId));
|
|
1364
|
+
}
|
|
1365
|
+
const resultFlow = state.flowName === flow._tag ? flow : registrations.get(state.flowName)?.at(-1)?.flow;
|
|
1366
|
+
if (resultFlow === void 0) {
|
|
1367
|
+
return Effect.die(new import_engine.FlowEngine.FlowNotRegistered({
|
|
1368
|
+
flowName: state.flowName,
|
|
1369
|
+
message: `Flow ${state.flowName} is not registered`
|
|
1370
|
+
}));
|
|
1371
|
+
}
|
|
1372
|
+
return Schema.decodeUnknownEffect(
|
|
1373
|
+
Schema.toCodecJson(import_flow.Flow.Result({
|
|
1374
|
+
success: resultFlow.successSchema,
|
|
1375
|
+
error: resultFlow.errorSchema
|
|
1376
|
+
}))
|
|
1377
|
+
)(state.result).pipe(
|
|
1378
|
+
Effect.orDie,
|
|
1379
|
+
Effect.map(Option.some)
|
|
1380
|
+
);
|
|
1381
|
+
}),
|
|
1382
|
+
Effect.map((result) => ({ result, status: row.status }))
|
|
1383
|
+
);
|
|
1384
|
+
})
|
|
1385
|
+
);
|
|
1386
|
+
const authorizeResume = (flow, executionId) => Effect.gen(function* () {
|
|
1387
|
+
const row = yield* store.get(executionId).pipe(
|
|
1388
|
+
Effect.catch((error) => error.code === "not_found_row" ? Effect.succeed(void 0) : Effect.die(error))
|
|
1389
|
+
);
|
|
1390
|
+
if (row === void 0) return false;
|
|
1391
|
+
const state = yield* decodeState(row.stateJson);
|
|
1392
|
+
if (state.flowName !== flow._tag) return false;
|
|
1393
|
+
if (!import_engine.FlowEngine.joinable(state.capabilityCeilings ?? [], yield* import_engine.FlowEngine.requestedAuthority(flow))) {
|
|
1394
|
+
return yield* Effect.die(import_engine.FlowEngine.capabilityConflict(executionId));
|
|
1395
|
+
}
|
|
1396
|
+
return true;
|
|
1397
|
+
});
|
|
1398
|
+
const poll = Effect.fn("FlowEngine.poll")(
|
|
1399
|
+
(flow, executionId) => Effect.map(readResult(flow, executionId), (observed) => observed.result)
|
|
1400
|
+
);
|
|
1401
|
+
const execute = Effect.fn("FlowEngine.execute")(
|
|
1402
|
+
function* (flow, options) {
|
|
1403
|
+
yield* Effect.annotateCurrentSpan({ executionId: options.executionId, flow: flow._tag });
|
|
1404
|
+
if (!registrations.has(flow._tag)) {
|
|
1405
|
+
return yield* Effect.die(
|
|
1406
|
+
new import_engine.FlowEngine.FlowNotRegistered({
|
|
1407
|
+
flowName: flow._tag,
|
|
1408
|
+
message: `Flow ${flow._tag} is not registered`
|
|
1409
|
+
})
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
if (options.discard) {
|
|
1413
|
+
yield* Effect.uninterruptible(
|
|
1414
|
+
ensureRun(flow, options).pipe(Effect.andThen(coordinator.schedule(options.executionId)))
|
|
1415
|
+
);
|
|
1416
|
+
return void 0;
|
|
1417
|
+
}
|
|
1418
|
+
yield* ensureRun(flow, options);
|
|
1419
|
+
if (options.follow === true) {
|
|
1420
|
+
yield* coordinator.scheduleIf(
|
|
1421
|
+
options.executionId,
|
|
1422
|
+
store.get(options.executionId).pipe(
|
|
1423
|
+
Effect.orDie,
|
|
1424
|
+
Effect.map((row) => row.status === "pending")
|
|
1425
|
+
)
|
|
1426
|
+
);
|
|
1427
|
+
yield* coordinator.join(options.executionId);
|
|
1428
|
+
} else {
|
|
1429
|
+
yield* coordinator.run(options.executionId);
|
|
1430
|
+
}
|
|
1431
|
+
const observed = yield* Effect.orDie(readResult(
|
|
1432
|
+
flow,
|
|
1433
|
+
options.executionId,
|
|
1434
|
+
options.parent !== void 0 && options.round === void 0
|
|
1435
|
+
));
|
|
1436
|
+
if (Option.isSome(observed.result)) {
|
|
1437
|
+
return observed.result.value;
|
|
1438
|
+
}
|
|
1439
|
+
return observed.status === "cancelled" ? new import_flow.Flow.Complete({ exit: Exit.failCause(Cause.interrupt()) }) : new import_flow.Flow.Suspended({});
|
|
1440
|
+
}
|
|
1441
|
+
);
|
|
1442
|
+
const interrupt = Effect.fn("FlowEngine.interrupt")(
|
|
1443
|
+
(_flow, executionId) => Effect.gen(function* () {
|
|
1444
|
+
yield* Effect.annotateCurrentSpan({ executionId });
|
|
1445
|
+
const nowMs = yield* Clock.currentTimeMillis.pipe(Effect.map(Math.floor));
|
|
1446
|
+
const requested = yield* transactState(
|
|
1447
|
+
Effect.gen(function* () {
|
|
1448
|
+
yield* requestCancellation(executionId, nowMs);
|
|
1449
|
+
return [executionId, ...yield* requestCancelDescendants(executionId, nowMs)];
|
|
1450
|
+
}),
|
|
1451
|
+
// Capture the shared writer directly. SqlJournal.transact catches a
|
|
1452
|
+
// selected failure and can discard the rest of a composite cause.
|
|
1453
|
+
(write) => Effect.flatMap(Effect.exit(writer.write(write)), (exit) => exit)
|
|
1454
|
+
).pipe(
|
|
1455
|
+
Effect.catchCause((cause) => {
|
|
1456
|
+
const failure = (error) => new import_flow.FlowRuntime.CancelRequestFailed({
|
|
1457
|
+
code: "cancel_request_failed",
|
|
1458
|
+
executionId,
|
|
1459
|
+
reason: error.message
|
|
1460
|
+
});
|
|
1461
|
+
if (cause.reasons.length === 1) {
|
|
1462
|
+
const reason = cause.reasons[0];
|
|
1463
|
+
if (Cause.isDieReason(reason) && reason.defect instanceof import_DurableWriter.DatabaseError) {
|
|
1464
|
+
return Effect.fail(failure(reason.defect));
|
|
1465
|
+
}
|
|
1466
|
+
if (Cause.isFailReason(reason)) {
|
|
1467
|
+
return Effect.fail(failure(reason.error));
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return Effect.failCause(asDefects(cause));
|
|
1471
|
+
})
|
|
1472
|
+
);
|
|
1473
|
+
yield* journal.whenCommitted(Effect.gen(function* () {
|
|
1474
|
+
for (const targetId of requested) {
|
|
1475
|
+
const instance = liveInstances.get(targetId);
|
|
1476
|
+
if (instance !== void 0) instance.interrupted = true;
|
|
1477
|
+
yield* coordinator.requestInterrupt(targetId);
|
|
1478
|
+
}
|
|
1479
|
+
}));
|
|
1480
|
+
})
|
|
1481
|
+
);
|
|
1482
|
+
const scheduleResume = Effect.fn("FlowEngine.scheduleResume")(
|
|
1483
|
+
(flowName, executionId, reason, sourceId) => transactState(Effect.gen(function* () {
|
|
1484
|
+
yield* Effect.annotateCurrentSpan({ executionId, flow: flowName, reason });
|
|
1485
|
+
const row = yield* store.get(executionId).pipe(
|
|
1486
|
+
Effect.catch(
|
|
1487
|
+
(error) => error.code === "not_found_row" ? Effect.succeed(void 0) : Effect.die(error)
|
|
1488
|
+
)
|
|
1489
|
+
);
|
|
1490
|
+
if (row === void 0) return;
|
|
1491
|
+
if (row.status === "completed" || row.status === "failed" || row.status === "cancelled") return;
|
|
1492
|
+
const state = yield* decodeState(row.stateJson);
|
|
1493
|
+
if (state.flowName !== flowName) return;
|
|
1494
|
+
const waiting = yield* engineState.waiting(executionId);
|
|
1495
|
+
if (Option.isSome(waiting) && waiting.value.reason === "quarantine") {
|
|
1496
|
+
if (reason !== "operator") return;
|
|
1497
|
+
yield* engineState.wake(executionId);
|
|
1498
|
+
}
|
|
1499
|
+
yield* emitDecision(executionId, {
|
|
1500
|
+
decision: "wake-scheduled",
|
|
1501
|
+
reason
|
|
1502
|
+
}, sourceId);
|
|
1503
|
+
return { parked: row.status === "suspended" };
|
|
1504
|
+
})).pipe(
|
|
1505
|
+
Effect.orDie,
|
|
1506
|
+
// A host can write a separate database. Release the engine transaction
|
|
1507
|
+
// before calling it, but record its request before publishing the wake.
|
|
1508
|
+
Effect.flatMap(
|
|
1509
|
+
(scheduled) => scheduled === void 0 ? Effect.void : (reason === "operator" || reason === "delegated" ? Effect.void : recordResume(executionId, scheduled.parked, reason)).pipe(
|
|
1510
|
+
Effect.andThen(coordinator.wake(executionId)),
|
|
1511
|
+
Effect.andThen(wakeBus.wake(executionId))
|
|
1512
|
+
)
|
|
1513
|
+
)
|
|
1514
|
+
)
|
|
1515
|
+
);
|
|
1516
|
+
return {
|
|
1517
|
+
register: Effect.fn("FlowEngine.register")(
|
|
1518
|
+
(flow, handler) => Effect.acquireRelease(
|
|
1519
|
+
Effect.sync(() => {
|
|
1520
|
+
const registration = { flow, execute: handler };
|
|
1521
|
+
const entries = registrations.get(flow._tag) ?? [];
|
|
1522
|
+
entries.push(registration);
|
|
1523
|
+
registrations.set(flow._tag, entries);
|
|
1524
|
+
pendingCycles.delete(flow._tag);
|
|
1525
|
+
warnedUnregistered.clear();
|
|
1526
|
+
return { registration, entries };
|
|
1527
|
+
}),
|
|
1528
|
+
({ registration, entries }) => Effect.sync(() => {
|
|
1529
|
+
const wasLatest = entries.at(-1) === registration;
|
|
1530
|
+
entries.splice(entries.indexOf(registration), 1);
|
|
1531
|
+
if (entries.length === 0) registrations.delete(flow._tag);
|
|
1532
|
+
if (wasLatest) {
|
|
1533
|
+
pendingCycles.delete(flow._tag);
|
|
1534
|
+
if (entries.length > 0) warnedUnregistered.clear();
|
|
1535
|
+
}
|
|
1536
|
+
})
|
|
1537
|
+
).pipe(Effect.asVoid)
|
|
1538
|
+
),
|
|
1539
|
+
execute,
|
|
1540
|
+
poll,
|
|
1541
|
+
interrupt,
|
|
1542
|
+
// The durable engine has ONE cancellation path (the release policy). The
|
|
1543
|
+
// port promises forced cancellation without cleanup; nothing here can
|
|
1544
|
+
// deliver that — a durable run is closed by a fenced transition whose
|
|
1545
|
+
// finalizers, cascade, and interruption record are the point — and
|
|
1546
|
+
// answering with `interrupt` silently reinterpreted the caller's
|
|
1547
|
+
// request as the safe one. Refusing is what keeps the excluded feature
|
|
1548
|
+
// from appearing to half-work; `interrupt` is the supported request and
|
|
1549
|
+
// is still available to the same caller. The memory engine, which does
|
|
1550
|
+
// have two behaviors, is unchanged.
|
|
1551
|
+
//
|
|
1552
|
+
// The reason is the release policy "Durable interruptUnsafe" verbatim, not a
|
|
1553
|
+
// paraphrase of it: the release note and the failure a caller catches
|
|
1554
|
+
// are the same statement, so an operator who reads one and a developer
|
|
1555
|
+
// who logs the other are reading the same sentence.
|
|
1556
|
+
interruptUnsafe: Effect.fn("FlowEngine.interruptUnsafe")(
|
|
1557
|
+
(_flow, executionId) => Effect.fail(
|
|
1558
|
+
new import_flow.FlowRuntime.CancelRequestFailed({
|
|
1559
|
+
code: "unsafe_interrupt_unsupported",
|
|
1560
|
+
executionId,
|
|
1561
|
+
reason: "The durable engine has one cancellation path, interrupt, which is durable and cascades to linked children. FlowRuntime.interruptUnsafe on the durable engine fails with unsafe_interrupt_unsupported instead of forcing cancellation without cleanup."
|
|
1562
|
+
})
|
|
1563
|
+
)
|
|
1564
|
+
),
|
|
1565
|
+
resume: Effect.fn("FlowEngine.resume")(
|
|
1566
|
+
(flow, executionId, options) => Effect.annotateCurrentSpan({ executionId, flow: flow._tag }).pipe(
|
|
1567
|
+
// A resume drives the run under its admitted authority, so a caller
|
|
1568
|
+
// whose authority does not cover it may not wake or join it, and a
|
|
1569
|
+
// request that names no admitted run of this flow drives nothing.
|
|
1570
|
+
Effect.andThen(authorizeResume(flow, executionId)),
|
|
1571
|
+
Effect.flatMap(
|
|
1572
|
+
(authorized) => authorized ? Effect.andThen(
|
|
1573
|
+
options?.poll === true ? Effect.void : scheduleResume(flow._tag, executionId, options?.delegated === true ? "delegated" : "operator"),
|
|
1574
|
+
coordinator.run(executionId)
|
|
1575
|
+
) : Effect.void
|
|
1576
|
+
)
|
|
1577
|
+
)
|
|
1578
|
+
),
|
|
1579
|
+
scheduleResume,
|
|
1580
|
+
active: Effect.fn("FlowEngine.active")(() => coordinator.active)(),
|
|
1581
|
+
retainedRuns
|
|
1582
|
+
};
|
|
1583
|
+
});
|
|
1584
|
+
//# sourceMappingURL=RunDriver.js.map
|