@smthrs/engine-store 0.0.0-stage → 1.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +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 +201 -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 +1586 -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 +300 -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 +3154 -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,985 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional workspace transactions for action execution.
|
|
3
|
+
*
|
|
4
|
+
* An action body normally mutates the host tree in place, which leaves the
|
|
5
|
+
* engine two bad options: trust the declaration, or diff the whole repository
|
|
6
|
+
* after the fact. This module takes the third one. The body runs against an
|
|
7
|
+
* **isolated workspace** seeded with exactly its declared read set; its writes
|
|
8
|
+
* are a value — a whole-tree diff — that reaches the host only through an
|
|
9
|
+
* explicit {@link Service.materialize} step with preconditions and rollback.
|
|
10
|
+
*
|
|
11
|
+
* Two consequences follow, and they are the reason this exists:
|
|
12
|
+
*
|
|
13
|
+
* - **Whole-tree write observation is structural.** The transaction *is* the
|
|
14
|
+
* tree, so "did this body write outside its declared write set" is a map
|
|
15
|
+
* comparison, not an inference. `StepBoundary`'s filesystem layer can only
|
|
16
|
+
* re-measure paths it was told about, which is why it never claims
|
|
17
|
+
* `wholeTreeWritesVerified` and why nothing it settled could enter the
|
|
18
|
+
* cross-run cache.
|
|
19
|
+
* - **The host is untouched until copy-back.** An execution whose observations
|
|
20
|
+
* contradict its declaration is {@link Invalidated}, and an invalidated
|
|
21
|
+
* result exposes provenance and violations *only* — never the candidate
|
|
22
|
+
* output, files, or queued effects.
|
|
23
|
+
*
|
|
24
|
+
* This is a **deterministic transaction model, not a security boundary** — the
|
|
25
|
+
* same caveat the proof of concept carried. A body that reaches the host
|
|
26
|
+
* through a service this module does not seed (a spawned native process, an
|
|
27
|
+
* undecorated socket) is outside the transaction. Actually denying that
|
|
28
|
+
* ambient access requires a machine boundary such as the providers in
|
|
29
|
+
* `packages/smithers/flows/sandbox/docs/api.md`. The one host
|
|
30
|
+
* write this module *does* perform is confined, however: copy-back resolves
|
|
31
|
+
* symlinks before it lands a byte and refuses any target whose canonical
|
|
32
|
+
* location escapes the workspace root.
|
|
33
|
+
*
|
|
34
|
+
* Settled bundles reach the host without a human review gate. Governing
|
|
35
|
+
* design: `docs/concepts/workspace-transactions.md`.
|
|
36
|
+
*
|
|
37
|
+
* @since 0.1.0
|
|
38
|
+
*/
|
|
39
|
+
import * as ArtifactStore from "@smthrs/artifacts/ArtifactStore";
|
|
40
|
+
import * as FileLease from "@smthrs/artifacts/FileLease";
|
|
41
|
+
import { Sha256 } from "@smthrs/crypto";
|
|
42
|
+
import * as KernelFileSystem from "@smthrs/kernel/FileSystem";
|
|
43
|
+
import { Workspace as KernelWorkspace } from "@smthrs/kernel/Workspace";
|
|
44
|
+
import { DerivedKey } from "@smthrs/keys";
|
|
45
|
+
import * as FileSet from "@smthrs/plan/FileSet";
|
|
46
|
+
import * as Cause from "effect/Cause";
|
|
47
|
+
import * as Context from "effect/Context";
|
|
48
|
+
import * as Effect from "effect/Effect";
|
|
49
|
+
import * as Exit from "effect/Exit";
|
|
50
|
+
import * as FileSystem from "effect/FileSystem";
|
|
51
|
+
import * as Layer from "effect/Layer";
|
|
52
|
+
import * as Metric from "effect/Metric";
|
|
53
|
+
import * as Path from "effect/Path";
|
|
54
|
+
import * as PlatformError from "effect/PlatformError";
|
|
55
|
+
import * as RcMap from "effect/RcMap";
|
|
56
|
+
import * as Ref from "effect/Ref";
|
|
57
|
+
import * as Result from "effect/Result";
|
|
58
|
+
import * as Schema from "effect/Schema";
|
|
59
|
+
import * as Scope from "effect/Scope";
|
|
60
|
+
import * as Semaphore from "effect/Semaphore";
|
|
61
|
+
import * as EngineStoreMetrics from "./EngineStoreMetrics.js";
|
|
62
|
+
import * as FileBoundarySnapshot from "./internal/FileBoundarySnapshot.js";
|
|
63
|
+
import * as FileEnumeration from "./internal/FileEnumeration.js";
|
|
64
|
+
import { compareText } from "./internal/Ordering.js";
|
|
65
|
+
/**
|
|
66
|
+
* Context service made available only inside a workspace transaction.
|
|
67
|
+
*
|
|
68
|
+
* @category services
|
|
69
|
+
* @since 0.1.0
|
|
70
|
+
*/
|
|
71
|
+
export const Workspace = Context.Service("@smthrs/engine-store/WorkspaceSandbox/Workspace");
|
|
72
|
+
/**
|
|
73
|
+
* Stable workspace sandbox failure codes.
|
|
74
|
+
*
|
|
75
|
+
* @category schemas
|
|
76
|
+
* @since 0.1.0
|
|
77
|
+
*/
|
|
78
|
+
export const WorkspaceErrorCode = Schema.Literals([
|
|
79
|
+
"invalid_path",
|
|
80
|
+
"not_found",
|
|
81
|
+
"host_unavailable",
|
|
82
|
+
"path_escapes_workspace",
|
|
83
|
+
"commit_lock_timeout"
|
|
84
|
+
]);
|
|
85
|
+
/**
|
|
86
|
+
* A body could not execute inside — or its result could not be moved through —
|
|
87
|
+
* an isolated workspace.
|
|
88
|
+
*
|
|
89
|
+
* `cause` carries the refusing host or artifact-store failure whole, so a
|
|
90
|
+
* debugger sees the original error instead of a flattened message
|
|
91
|
+
* (`PlatformError`'s own convention).
|
|
92
|
+
*
|
|
93
|
+
* @category errors
|
|
94
|
+
* @since 0.1.0
|
|
95
|
+
*/
|
|
96
|
+
export class WorkspaceError extends Schema.TaggedError()("@smthrs/engine-store/WorkspaceError", {
|
|
97
|
+
code: WorkspaceErrorCode,
|
|
98
|
+
message: Schema.String,
|
|
99
|
+
cause: Schema.optional(Schema.Defect())
|
|
100
|
+
}) {
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Copy-back would overwrite host state that changed since the transaction's
|
|
104
|
+
* base snapshot was taken.
|
|
105
|
+
*
|
|
106
|
+
* The engine answers this by retrying the attempt from a fresh base a bounded
|
|
107
|
+
* number of times. This local retry is rebase-shaped. The worktree-lane
|
|
108
|
+
* surface was removed; this error does not recreate it.
|
|
109
|
+
*
|
|
110
|
+
* @category errors
|
|
111
|
+
* @since 0.1.0
|
|
112
|
+
*/
|
|
113
|
+
export class MaterializationConflict extends Schema.TaggedError()("@smthrs/engine-store/MaterializationConflict", {
|
|
114
|
+
paths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(4_096))).check(Schema.isMaxLength(1_024)),
|
|
115
|
+
message: Schema.NonEmptyString.check(Schema.isMaxLength(16_384))
|
|
116
|
+
}) {
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Whether a failure is a materialization conflict in live or durable form.
|
|
120
|
+
* Persisted failures retain the schema tag but not the class prototype, so
|
|
121
|
+
* replay classification must recognize both representations.
|
|
122
|
+
*
|
|
123
|
+
* @category guards
|
|
124
|
+
* @since 0.1.0
|
|
125
|
+
*/
|
|
126
|
+
export const isMaterializationConflict = (error) => {
|
|
127
|
+
if (typeof error !== "object" || error === null)
|
|
128
|
+
return false;
|
|
129
|
+
try {
|
|
130
|
+
if (error instanceof MaterializationConflict)
|
|
131
|
+
return true;
|
|
132
|
+
const own = (name) => {
|
|
133
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, name);
|
|
134
|
+
return descriptor !== undefined && descriptor.enumerable && "value" in descriptor
|
|
135
|
+
? descriptor.value
|
|
136
|
+
: undefined;
|
|
137
|
+
};
|
|
138
|
+
const paths = own("paths");
|
|
139
|
+
const message = own("message");
|
|
140
|
+
if (own("_tag") !== MaterializationConflict.identifier ||
|
|
141
|
+
typeof message !== "string" ||
|
|
142
|
+
message.length === 0 ||
|
|
143
|
+
message.length > 16_384 ||
|
|
144
|
+
!Array.isArray(paths) ||
|
|
145
|
+
Object.getPrototypeOf(paths) !== Array.prototype ||
|
|
146
|
+
paths.length > 1_024)
|
|
147
|
+
return false;
|
|
148
|
+
for (let index = 0; index < paths.length; index++) {
|
|
149
|
+
const descriptor = Object.getOwnPropertyDescriptor(paths, String(index));
|
|
150
|
+
if (descriptor === undefined ||
|
|
151
|
+
!("value" in descriptor) ||
|
|
152
|
+
!descriptor.enumerable ||
|
|
153
|
+
typeof descriptor.value !== "string" ||
|
|
154
|
+
descriptor.value.length === 0 ||
|
|
155
|
+
descriptor.value.length > 4_096)
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (Reflect.ownKeys(paths).some((key) => key !== "length" &&
|
|
159
|
+
(typeof key !== "string" || !/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= paths.length)))
|
|
160
|
+
return false;
|
|
161
|
+
return Reflect.ownKeys(error).every((key) => key === "_tag" || key === "paths" || key === "message");
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Context service for the selected workspace sandbox implementation.
|
|
169
|
+
*
|
|
170
|
+
* @category services
|
|
171
|
+
* @since 0.1.0
|
|
172
|
+
*/
|
|
173
|
+
export const WorkspaceSandbox = Context.Service("@smthrs/engine-store/WorkspaceSandbox");
|
|
174
|
+
/**
|
|
175
|
+
* Constructs a workspace sandbox from an implementation.
|
|
176
|
+
*
|
|
177
|
+
* @category constructors
|
|
178
|
+
* @since 0.1.0
|
|
179
|
+
*/
|
|
180
|
+
export const make = (service) => WorkspaceSandbox.of(service);
|
|
181
|
+
/**
|
|
182
|
+
* Provides a workspace sandbox implementation.
|
|
183
|
+
*
|
|
184
|
+
* @category layers
|
|
185
|
+
* @since 0.1.0
|
|
186
|
+
*/
|
|
187
|
+
export const layer = (service) => Layer.succeed(WorkspaceSandbox, make(service));
|
|
188
|
+
/**
|
|
189
|
+
* Context service for the queued-effect dispatch stage. Optional: with no
|
|
190
|
+
* dispatcher provided the engine journals what a transaction queued and sends
|
|
191
|
+
* nothing.
|
|
192
|
+
*
|
|
193
|
+
* @category services
|
|
194
|
+
* @since 0.1.0
|
|
195
|
+
*/
|
|
196
|
+
export const EffectDispatcher = Context.Service("@smthrs/engine-store/WorkspaceSandbox/EffectDispatcher");
|
|
197
|
+
/**
|
|
198
|
+
* Provides a queued-effect dispatch stage.
|
|
199
|
+
*
|
|
200
|
+
* @category layers
|
|
201
|
+
* @since 0.1.0
|
|
202
|
+
*/
|
|
203
|
+
export const layerDispatcher = (dispatcher) => Layer.succeed(EffectDispatcher, EffectDispatcher.of(dispatcher));
|
|
204
|
+
// -----------------------------------------------------------------------------
|
|
205
|
+
// path vocabulary
|
|
206
|
+
// -----------------------------------------------------------------------------
|
|
207
|
+
const decoder = new TextDecoder();
|
|
208
|
+
const encoder = new TextEncoder();
|
|
209
|
+
const invalidPath = (path) => new WorkspaceError({
|
|
210
|
+
code: "invalid_path",
|
|
211
|
+
message: `Workspace paths must be relative paths inside the workspace without '..': ${path}`
|
|
212
|
+
});
|
|
213
|
+
/**
|
|
214
|
+
* Normalizes a caller path to a workspace-relative key.
|
|
215
|
+
*
|
|
216
|
+
* Absolute paths are accepted only under `root`, which is how a body that
|
|
217
|
+
* resolved `workspace.root` itself still lands inside the transaction; every
|
|
218
|
+
* other absolute path, and anything traversing upward, is refused rather than
|
|
219
|
+
* silently confined.
|
|
220
|
+
*/
|
|
221
|
+
const normalizePath = (root, path, allowRoot = false) => {
|
|
222
|
+
const slashed = path.replaceAll("\\", "/");
|
|
223
|
+
const slashedRoot = root.replaceAll("\\", "/");
|
|
224
|
+
const rooted = slashed === slashedRoot
|
|
225
|
+
? ""
|
|
226
|
+
: slashedRoot !== "" && slashed.startsWith(`${slashedRoot}/`)
|
|
227
|
+
? slashed.slice(slashedRoot.length + 1)
|
|
228
|
+
: slashed;
|
|
229
|
+
const parts = rooted.split("/");
|
|
230
|
+
if (rooted.startsWith("/") || parts.some((part) => part === ".."))
|
|
231
|
+
return Result.fail(invalidPath(path));
|
|
232
|
+
const compact = parts.filter((part) => part !== "" && part !== ".").join("/");
|
|
233
|
+
return compact === "" && !allowRoot ? Result.fail(invalidPath(path)) : Result.succeed(compact);
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Whether a declared write-set entry covers an observed path. A plain string
|
|
237
|
+
* is a literal path, even when it contains `*` or `**`, exactly as
|
|
238
|
+
* `StepBoundary` captures and `FileSet.overlaps` compares it; wildcard
|
|
239
|
+
* declarations are written as a Glob.
|
|
240
|
+
*/
|
|
241
|
+
const covers = (entry, path) => typeof entry === "string"
|
|
242
|
+
? entry === path
|
|
243
|
+
: entry._tag === "TreeArtifact"
|
|
244
|
+
? path === entry.path || path.startsWith(`${entry.path}/`)
|
|
245
|
+
: FileSet.matchesGlob(entry, path);
|
|
246
|
+
const resource = (path) => ({ kind: "file", id: path });
|
|
247
|
+
/**
|
|
248
|
+
* Restates a declaration in the transaction's own path vocabulary.
|
|
249
|
+
*
|
|
250
|
+
* A caller may declare `out/result.txt` or the absolute path the same file has
|
|
251
|
+
* on the host — the kernel-decorated `FileSystem` accepts both, so the
|
|
252
|
+
* declaration does too. Observed paths are always workspace-relative, so the
|
|
253
|
+
* declaration has to be brought to the same footing before anything is
|
|
254
|
+
* compared against it, or an absolute declaration would cover nothing and
|
|
255
|
+
* every write would read as undeclared. An entry that cannot be named inside
|
|
256
|
+
* the workspace is kept verbatim: it covers nothing, which is the honest
|
|
257
|
+
* answer for a declaration pointing outside the tree.
|
|
258
|
+
*/
|
|
259
|
+
const workspaceRelative = (root, descriptor) => {
|
|
260
|
+
const relative = (path) => {
|
|
261
|
+
const normalized = normalizePath(root, path);
|
|
262
|
+
return Result.isFailure(normalized) ? path : normalized.success;
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
readSet: descriptor.readSet.map((entry) => FileSet.isGlob(entry)
|
|
266
|
+
? {
|
|
267
|
+
...entry,
|
|
268
|
+
include: [relative(entry.include[0]), ...entry.include.slice(1).map(relative)],
|
|
269
|
+
...(entry.exclude === undefined ? {} : { exclude: entry.exclude.map(relative) })
|
|
270
|
+
}
|
|
271
|
+
: { ...entry, path: relative(entry.path) }),
|
|
272
|
+
writeSet: descriptor.writeSet.map((entry) => typeof entry === "string"
|
|
273
|
+
? relative(entry)
|
|
274
|
+
: entry._tag === "TreeArtifact"
|
|
275
|
+
? { ...entry, path: relative(entry.path) }
|
|
276
|
+
: {
|
|
277
|
+
...entry,
|
|
278
|
+
include: [relative(entry.include[0]), ...entry.include.slice(1).map(relative)],
|
|
279
|
+
...(entry.exclude === undefined ? {} : { exclude: entry.exclude.map(relative) })
|
|
280
|
+
}),
|
|
281
|
+
...(descriptor.removes === undefined ? {} : { removes: descriptor.removes.map(relative) }),
|
|
282
|
+
boundaryMode: descriptor.boundaryMode
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
const digestOf = (bytes) => Schema.decodeUnknownEffect(Sha256)(bytes).pipe(Effect.orDie);
|
|
286
|
+
const parentDirectory = (path) => {
|
|
287
|
+
const index = path.lastIndexOf("/");
|
|
288
|
+
return index <= 0 ? undefined : path.slice(0, index);
|
|
289
|
+
};
|
|
290
|
+
const notFound = (path) => new WorkspaceError({ code: "not_found", message: `Workspace file does not exist: ${path}` });
|
|
291
|
+
/**
|
|
292
|
+
* The transaction surfaces: the {@link Workspace} tag and a re-rooted Effect
|
|
293
|
+
* `FileSystem`, both over one path-keyed map.
|
|
294
|
+
*
|
|
295
|
+
* The `FileSystem` is built from `makeNoop` on purpose. Only the operations
|
|
296
|
+
* listed here are meaningful over a functional map, and a body reaching for
|
|
297
|
+
* one that is not — a temp file, a watch, a stream — gets the platform's own
|
|
298
|
+
* refusal rather than a plausible lie. Widening the surface is a deliberate
|
|
299
|
+
* change to this list, never a silent default.
|
|
300
|
+
*/
|
|
301
|
+
const transaction = (base, trace, root) => {
|
|
302
|
+
const files = new Map(base);
|
|
303
|
+
const produced = new Set();
|
|
304
|
+
const resolvePath = (path, allowRoot = false) => {
|
|
305
|
+
const normalized = normalizePath(root, path, allowRoot);
|
|
306
|
+
return Result.isFailure(normalized) ? Effect.fail(normalized.failure) : Effect.succeed(normalized.success);
|
|
307
|
+
};
|
|
308
|
+
const readBytes = Effect.fn("WorkspaceSandbox.read")(function* (path) {
|
|
309
|
+
const key = yield* resolvePath(path);
|
|
310
|
+
trace.attemptedReads.push({ path: key, produced: produced.has(key) });
|
|
311
|
+
const content = files.get(key);
|
|
312
|
+
if (content === undefined)
|
|
313
|
+
return yield* Effect.fail(notFound(key));
|
|
314
|
+
trace.inputs.push({ path: key, content });
|
|
315
|
+
return content.slice();
|
|
316
|
+
});
|
|
317
|
+
const writeBytes = Effect.fn("WorkspaceSandbox.write")(function* (path, content) {
|
|
318
|
+
const key = yield* resolvePath(path);
|
|
319
|
+
produced.add(key);
|
|
320
|
+
files.set(key, content.slice());
|
|
321
|
+
});
|
|
322
|
+
const removePath = Effect.fn("WorkspaceSandbox.remove")(function* (path) {
|
|
323
|
+
const key = yield* resolvePath(path);
|
|
324
|
+
produced.add(key);
|
|
325
|
+
files.delete(key);
|
|
326
|
+
});
|
|
327
|
+
const workspace = Workspace.of({
|
|
328
|
+
readFile: (path) => readBytes(path),
|
|
329
|
+
writeFile: (path, content) => writeBytes(path, content),
|
|
330
|
+
removeFile: (path) => removePath(path),
|
|
331
|
+
queueEffect: (effect) => Effect.sync(() => trace.effects.push(effect))
|
|
332
|
+
});
|
|
333
|
+
const refuse = (method, path) => (error) => error.code === "not_found"
|
|
334
|
+
? PlatformError.systemError({
|
|
335
|
+
_tag: "NotFound",
|
|
336
|
+
module: "FileSystem",
|
|
337
|
+
method,
|
|
338
|
+
description: error.message,
|
|
339
|
+
pathOrDescriptor: path
|
|
340
|
+
})
|
|
341
|
+
: PlatformError.badArgument({ module: "FileSystem", method, description: error.message });
|
|
342
|
+
const fileSystem = FileSystem.makeNoop({
|
|
343
|
+
// `exists` and `readDirectory` trace every PRESENCE they reveal: under a
|
|
344
|
+
// whole-tree-seeding host the map holds undeclared world state, and a
|
|
345
|
+
// probe that observes it is a read the key never folded. Absence stays
|
|
346
|
+
// untraced — under the declared-set host an undeclared path is absent by
|
|
347
|
+
// construction, the Bazel forest's own deterministic answer.
|
|
348
|
+
exists: (path) => resolvePath(path, true).pipe(Effect.map((key) => {
|
|
349
|
+
if (key === "")
|
|
350
|
+
return true;
|
|
351
|
+
if (files.has(key)) {
|
|
352
|
+
trace.attemptedReads.push({ path: key, produced: produced.has(key) });
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
const prefix = `${key}/`;
|
|
356
|
+
let present = false;
|
|
357
|
+
for (const candidate of files.keys()) {
|
|
358
|
+
if (!candidate.startsWith(prefix))
|
|
359
|
+
continue;
|
|
360
|
+
trace.attemptedReads.push({ path: candidate, produced: produced.has(candidate) });
|
|
361
|
+
present = true;
|
|
362
|
+
}
|
|
363
|
+
return present;
|
|
364
|
+
}),
|
|
365
|
+
// A path the transaction cannot even name does not exist in it; that
|
|
366
|
+
// is an answer, not a host failure.
|
|
367
|
+
Effect.catch(() => Effect.succeed(false))),
|
|
368
|
+
readFile: (path) => readBytes(path).pipe(Effect.mapError(refuse("readFile", path))),
|
|
369
|
+
readFileString: (path) => readBytes(path).pipe(Effect.map((bytes) => decoder.decode(bytes)), Effect.mapError(refuse("readFileString", path))),
|
|
370
|
+
writeFile: (path, data) => writeBytes(path, data).pipe(Effect.mapError(refuse("writeFile", path))),
|
|
371
|
+
writeFileString: (path, data) => writeBytes(path, encoder.encode(data)).pipe(Effect.mapError(refuse("writeFileString", path))),
|
|
372
|
+
remove: (path) => removePath(path).pipe(Effect.mapError(refuse("remove", path))),
|
|
373
|
+
// Directories are implicit in a path-keyed map: creating one is a
|
|
374
|
+
// successful no-op so a body that mirrors ordinary filesystem discipline
|
|
375
|
+
// works unchanged, and listing one is a prefix scan.
|
|
376
|
+
makeDirectory: () => Effect.void,
|
|
377
|
+
readDirectory: (path) => resolvePath(path, true).pipe(Effect.map((key) => {
|
|
378
|
+
const prefix = key === "" ? "" : `${key}/`;
|
|
379
|
+
const entries = new Set();
|
|
380
|
+
for (const candidate of files.keys()) {
|
|
381
|
+
if (!candidate.startsWith(prefix))
|
|
382
|
+
continue;
|
|
383
|
+
// A listing reveals each file it names, so each is a traced read.
|
|
384
|
+
trace.attemptedReads.push({ path: candidate, produced: produced.has(candidate) });
|
|
385
|
+
entries.add(candidate.slice(prefix.length).split("/")[0]);
|
|
386
|
+
}
|
|
387
|
+
return [...entries].sort(compareText);
|
|
388
|
+
}), Effect.mapError(refuse("readDirectory", path)))
|
|
389
|
+
});
|
|
390
|
+
return { files, workspace, fileSystem };
|
|
391
|
+
};
|
|
392
|
+
const changes = Effect.fn("WorkspaceSandbox.changes")(function* (base, current, host) {
|
|
393
|
+
const paths = [...new Set([...base.keys(), ...current.keys()])].sort(compareText);
|
|
394
|
+
const result = [];
|
|
395
|
+
for (const path of paths) {
|
|
396
|
+
const before = base.get(path);
|
|
397
|
+
const after = current.get(path);
|
|
398
|
+
// A path the transaction never observed is not a path the host lacks: ask
|
|
399
|
+
// the host what is there before claiming the change creates it.
|
|
400
|
+
const beforeDigest = before === undefined ? yield* host.baseline(path) : yield* digestOf(before);
|
|
401
|
+
const afterDigest = after === undefined ? undefined : yield* digestOf(after);
|
|
402
|
+
if (beforeDigest === afterDigest)
|
|
403
|
+
continue;
|
|
404
|
+
result.push({
|
|
405
|
+
path,
|
|
406
|
+
beforeDigest,
|
|
407
|
+
afterDigest,
|
|
408
|
+
...(after === undefined ? {} : { after: yield* host.retain(after) })
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
return result;
|
|
412
|
+
});
|
|
413
|
+
/**
|
|
414
|
+
* The compare-and-set every copy-back runs before it writes a single byte,
|
|
415
|
+
* capturing each target's pre-image while it looks.
|
|
416
|
+
*
|
|
417
|
+
* Both hosts share it, so "all-or-nothing" cannot drift between the in-memory
|
|
418
|
+
* conformance implementation and the filesystem one: a bundle whose base moved
|
|
419
|
+
* is refused whole, and the tree is left exactly as it was found. The captured
|
|
420
|
+
* pre-images are the rollback journal the filesystem host restores from when a
|
|
421
|
+
* mid-apply refusal would otherwise strand a half-written tree.
|
|
422
|
+
*/
|
|
423
|
+
const preflight = Effect.fn("WorkspaceSandbox.preflight")(function* (changes, current) {
|
|
424
|
+
const conflicting = [];
|
|
425
|
+
const before = new Map();
|
|
426
|
+
for (const change of changes) {
|
|
427
|
+
const content = yield* current(change.path);
|
|
428
|
+
before.set(change.path, content);
|
|
429
|
+
const digest = content === undefined ? undefined : yield* digestOf(content);
|
|
430
|
+
conflicting.push(...(digest === change.beforeDigest ? [] : [change.path]));
|
|
431
|
+
}
|
|
432
|
+
const conflict = conflicting.length === 0 ? undefined : new MaterializationConflict({
|
|
433
|
+
paths: conflicting,
|
|
434
|
+
message: "Workspace state changed after the transaction's base snapshot was taken"
|
|
435
|
+
});
|
|
436
|
+
return { conflict, before };
|
|
437
|
+
});
|
|
438
|
+
const revisionOf = Effect.fn("WorkspaceSandbox.revision")(function* (base) {
|
|
439
|
+
const entries = [];
|
|
440
|
+
for (const [path, content] of [...base].sort((left, right) => compareText(left[0], right[0]))) {
|
|
441
|
+
entries.push({ path, digest: yield* digestOf(content) });
|
|
442
|
+
}
|
|
443
|
+
return yield* Schema.decodeUnknownEffect(DerivedKey)({ kind: "workspace-revision", entries }).pipe(Effect.orDie);
|
|
444
|
+
});
|
|
445
|
+
/**
|
|
446
|
+
* Everything the declaration failed to predict, deduplicated.
|
|
447
|
+
*
|
|
448
|
+
* Reads and writes are checked against the declaration's exact paths and patterns; writes
|
|
449
|
+
* against the declared write set's patterns. A read of a file the body itself
|
|
450
|
+
* produced is not undeclared — the transaction, not the host, supplied it.
|
|
451
|
+
*
|
|
452
|
+
* @category accessors
|
|
453
|
+
* @since 0.1.0
|
|
454
|
+
*/
|
|
455
|
+
export const violations = (descriptor, base, provenance) => {
|
|
456
|
+
const declaredReads = descriptor.readSet;
|
|
457
|
+
const observed = [
|
|
458
|
+
...provenance.inputs
|
|
459
|
+
.filter((input) => base.has(input.resource.id) &&
|
|
460
|
+
!declaredReads.some((entry) => FileSet.isGlob(entry) ? FileSet.matchesGlob(entry, input.resource.id) : entry.path === input.resource.id))
|
|
461
|
+
.map((input) => ({ kind: "undeclared-read", resource: input.resource })),
|
|
462
|
+
...provenance.outputs
|
|
463
|
+
.filter((output) => ![...descriptor.writeSet, ...descriptor.removes ?? []].some((entry) => covers(entry, output.resource.id)))
|
|
464
|
+
.map((output) => ({ kind: "undeclared-write", resource: output.resource }))
|
|
465
|
+
];
|
|
466
|
+
return [
|
|
467
|
+
...new Map(observed.map((violation) => [`${violation.kind}:${violation.resource.id}`, violation])).values()
|
|
468
|
+
];
|
|
469
|
+
};
|
|
470
|
+
/**
|
|
471
|
+
* Builds a workspace sandbox over a {@link Host}.
|
|
472
|
+
*
|
|
473
|
+
* @category constructors
|
|
474
|
+
* @since 0.1.0
|
|
475
|
+
*/
|
|
476
|
+
export const makeHosted = (host) => {
|
|
477
|
+
const memo = new Map();
|
|
478
|
+
/**
|
|
479
|
+
* The speculative half, untraced: the `execute` span below wraps it, so
|
|
480
|
+
* the transaction body reports under one span with its metrics.
|
|
481
|
+
*/
|
|
482
|
+
const executeBody = Effect.fnUntraced(function* (execution) {
|
|
483
|
+
const boundary = FileBoundarySnapshot.make(execution.descriptor);
|
|
484
|
+
const key = execution.cacheKey;
|
|
485
|
+
if (key !== undefined) {
|
|
486
|
+
const hit = memo.get(key);
|
|
487
|
+
if (hit !== undefined) {
|
|
488
|
+
return {
|
|
489
|
+
_tag: "Accepted",
|
|
490
|
+
result: hit.result,
|
|
491
|
+
violations: hit.violations,
|
|
492
|
+
cache: { status: "hit", key }
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const captured = yield* host.snapshot(boundary);
|
|
497
|
+
// The host owns its returned buffers. Preserve the instant represented by
|
|
498
|
+
// snapshot() even if that host or another fiber mutates them while the
|
|
499
|
+
// workflow is suspended.
|
|
500
|
+
const base = new Map([...captured].map(([path, content]) => [path, content.slice()]));
|
|
501
|
+
const trace = { inputs: [], attemptedReads: [], effects: [] };
|
|
502
|
+
const isolated = transaction(base, trace, host.root);
|
|
503
|
+
const outcome = yield* execution.workflow.pipe(Effect.provideService(Workspace, isolated.workspace), Effect.provideService(FileSystem.FileSystem, isolated.fileSystem), Effect.exit);
|
|
504
|
+
const inputs = [];
|
|
505
|
+
for (const input of trace.inputs) {
|
|
506
|
+
inputs.push({ resource: resource(input.path), digest: yield* digestOf(input.content) });
|
|
507
|
+
}
|
|
508
|
+
const descriptor = workspaceRelative(host.root, boundary);
|
|
509
|
+
const undeclaredReads = trace.attemptedReads.filter((attempt) => !attempt.produced &&
|
|
510
|
+
!descriptor.readSet.some((entry) => FileSet.isGlob(entry) ? FileSet.matchesGlob(entry, attempt.path) : entry.path === attempt.path));
|
|
511
|
+
if (Exit.isFailure(outcome)) {
|
|
512
|
+
// Hard mode only, mirroring the success path below: in expected mode
|
|
513
|
+
// an undeclared read is a deviation the engine journals, and a body's
|
|
514
|
+
// own typed failure must surface as itself — converting it into an
|
|
515
|
+
// isolation verdict would both harden a soft boundary and mask the
|
|
516
|
+
// error the retry policy classifies on.
|
|
517
|
+
if (undeclaredReads.length > 0 && boundary.boundaryMode === "hard") {
|
|
518
|
+
return {
|
|
519
|
+
_tag: "Invalidated",
|
|
520
|
+
provenance: {
|
|
521
|
+
baseRevision: yield* revisionOf(base),
|
|
522
|
+
inputs,
|
|
523
|
+
outputs: []
|
|
524
|
+
},
|
|
525
|
+
violations: [
|
|
526
|
+
...new Map(undeclaredReads.map((attempt) => [attempt.path, {
|
|
527
|
+
kind: "undeclared-read",
|
|
528
|
+
resource: resource(attempt.path)
|
|
529
|
+
}])).values()
|
|
530
|
+
]
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
return yield* Effect.failCause(outcome.cause);
|
|
534
|
+
}
|
|
535
|
+
const output = outcome.value;
|
|
536
|
+
const files = yield* changes(base, isolated.files, host);
|
|
537
|
+
const provenance = {
|
|
538
|
+
baseRevision: yield* revisionOf(base),
|
|
539
|
+
inputs,
|
|
540
|
+
outputs: files.map((change) => ({
|
|
541
|
+
resource: resource(change.path),
|
|
542
|
+
operation: change.afterDigest === undefined ? "remove" : "write",
|
|
543
|
+
digest: change.afterDigest
|
|
544
|
+
}))
|
|
545
|
+
};
|
|
546
|
+
const invalid = [
|
|
547
|
+
...new Map([
|
|
548
|
+
...violations(descriptor, base, provenance),
|
|
549
|
+
...undeclaredReads.map((attempt) => ({
|
|
550
|
+
kind: "undeclared-read",
|
|
551
|
+
resource: resource(attempt.path)
|
|
552
|
+
}))
|
|
553
|
+
].map((violation) => [`${violation.kind}:${violation.resource.id}`, violation])).values()
|
|
554
|
+
];
|
|
555
|
+
// Hard mode discards; expected mode admits the result and leaves the
|
|
556
|
+
// deviation for the engine to journal and reconcile
|
|
557
|
+
// (`Effect Taxonomy.md`, "Expected sets — the soft mode"). Either way
|
|
558
|
+
// the host has not been touched.
|
|
559
|
+
if (invalid.length > 0 && boundary.boundaryMode === "hard") {
|
|
560
|
+
return { _tag: "Invalidated", provenance, violations: invalid };
|
|
561
|
+
}
|
|
562
|
+
const result = { output, files, provenance, effects: trace.effects };
|
|
563
|
+
if (key !== undefined) {
|
|
564
|
+
memo.set(key, { result: result, violations: invalid });
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
_tag: "Accepted",
|
|
568
|
+
result,
|
|
569
|
+
violations: invalid,
|
|
570
|
+
cache: key === undefined ? { status: "disabled" } : { status: "miss", key }
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
return make({
|
|
574
|
+
execute: Effect.fn("WorkspaceSandbox.execute")(function* (execution) {
|
|
575
|
+
yield* Effect.annotateCurrentSpan({ boundaryMode: execution.descriptor.boundaryMode });
|
|
576
|
+
return yield* executeBody(execution);
|
|
577
|
+
}, (effect) => EngineStoreMetrics.observe({
|
|
578
|
+
timer: EngineStoreMetrics.sandboxExecutionDuration,
|
|
579
|
+
counter: EngineStoreMetrics.sandboxExecution
|
|
580
|
+
})(effect)),
|
|
581
|
+
materialize: Effect.fn("WorkspaceSandbox.materialize")(function* (accepted) {
|
|
582
|
+
yield* Effect.annotateCurrentSpan({ changes: accepted.result.files.length });
|
|
583
|
+
// Nothing to copy back needs no commit: a body that changed nothing
|
|
584
|
+
// (a sealed read) must not need write authority over the root.
|
|
585
|
+
if (accepted.result.files.length === 0)
|
|
586
|
+
return;
|
|
587
|
+
yield* host.commit(accepted.result.files);
|
|
588
|
+
}, (effect) => EngineStoreMetrics.observe({
|
|
589
|
+
timer: EngineStoreMetrics.materializationDuration,
|
|
590
|
+
counter: EngineStoreMetrics.materialization
|
|
591
|
+
})(effect))
|
|
592
|
+
});
|
|
593
|
+
};
|
|
594
|
+
/**
|
|
595
|
+
* Creates a deterministic in-memory workspace sandbox.
|
|
596
|
+
*
|
|
597
|
+
* The returned `files` effect observes *host* state, not a running
|
|
598
|
+
* transaction: it changes only through `service.materialize`. This host hands
|
|
599
|
+
* the transaction its whole tree rather than seeding the declared read set, so
|
|
600
|
+
* an undeclared read is observable — which is exactly what makes it the
|
|
601
|
+
* conformance implementation for the bare tier of `Effect Taxonomy.md`.
|
|
602
|
+
*
|
|
603
|
+
* @category constructors
|
|
604
|
+
* @since 0.1.0
|
|
605
|
+
*/
|
|
606
|
+
export const makeMemory = (initialFiles = {}) => Effect.gen(function* () {
|
|
607
|
+
const entries = [];
|
|
608
|
+
for (const [path, content] of Object.entries(initialFiles)) {
|
|
609
|
+
const normalized = normalizePath("", path);
|
|
610
|
+
if (Result.isFailure(normalized))
|
|
611
|
+
return yield* Effect.fail(normalized.failure);
|
|
612
|
+
entries.push([normalized.success, typeof content === "string" ? encoder.encode(content) : content.slice()]);
|
|
613
|
+
}
|
|
614
|
+
const host = yield* Ref.make(new Map(entries));
|
|
615
|
+
const service = makeHosted({
|
|
616
|
+
root: "",
|
|
617
|
+
snapshot: () => Ref.get(host),
|
|
618
|
+
// This host seeds the transaction with the WHOLE tree, so a path the
|
|
619
|
+
// transaction did not observe is one the host genuinely did not hold:
|
|
620
|
+
// `undefined` is the honest snapshot-time answer, and a file that
|
|
621
|
+
// appeared underneath the transaction is caught by copy-back's
|
|
622
|
+
// compare-and-set rather than clobbered.
|
|
623
|
+
baseline: () => Effect.succeed(undefined),
|
|
624
|
+
retain: (bytes) => Effect.succeed(bytes.slice()),
|
|
625
|
+
commit: Effect.fn("WorkspaceSandbox.commit")(function* (changes) {
|
|
626
|
+
yield* Effect.annotateCurrentSpan({ changes: changes.length });
|
|
627
|
+
const current = yield* Ref.get(host);
|
|
628
|
+
const { conflict } = yield* preflight(changes, (path) => Effect.succeed(current.get(path)));
|
|
629
|
+
if (conflict !== undefined) {
|
|
630
|
+
yield* Metric.update(EngineStoreMetrics.materializationConflicts, 1);
|
|
631
|
+
return yield* Effect.fail(conflict);
|
|
632
|
+
}
|
|
633
|
+
const next = new Map(current);
|
|
634
|
+
for (const change of changes) {
|
|
635
|
+
if (change.afterDigest === undefined)
|
|
636
|
+
next.delete(change.path);
|
|
637
|
+
else
|
|
638
|
+
next.set(change.path, change.after.slice());
|
|
639
|
+
}
|
|
640
|
+
yield* Ref.set(host, next);
|
|
641
|
+
})
|
|
642
|
+
});
|
|
643
|
+
return {
|
|
644
|
+
service,
|
|
645
|
+
files: Ref.get(host).pipe(Effect.map((current) => [...current]
|
|
646
|
+
.map(([path, content]) => ({ path, content }))
|
|
647
|
+
.sort((left, right) => compareText(left.path, right.path))))
|
|
648
|
+
};
|
|
649
|
+
});
|
|
650
|
+
// Shared across separately constructed sandboxes. Entries live only while a
|
|
651
|
+
// commit is running or waiting, so short-lived workspace roots do not leak.
|
|
652
|
+
const CommitLocks = Context.Service("@smthrs/engine-store/WorkspaceSandbox/CommitLocks");
|
|
653
|
+
const locksLayer = Layer.effect(CommitLocks, RcMap.make({ lookup: (_root) => Semaphore.make(1) }));
|
|
654
|
+
// The memo map shares the layer across active operations, including operations
|
|
655
|
+
// from different store instances. Their scopes release the final owner.
|
|
656
|
+
const lockOwners = Layer.makeMemoMapUnsafe();
|
|
657
|
+
const coordinateCommit = (root, write) => Effect.scoped(Effect.gen(function* () {
|
|
658
|
+
const services = yield* Layer.buildWithMemoMap(locksLayer, lockOwners, yield* Scope.Scope);
|
|
659
|
+
const lock = yield* RcMap.get(Context.get(services, CommitLocks), root);
|
|
660
|
+
return yield* lock.withPermit(write);
|
|
661
|
+
}));
|
|
662
|
+
const commitLockName = ".smithers-workspace-lock";
|
|
663
|
+
// The conventional engine state directory: the SQLite store, its WAL/SHM
|
|
664
|
+
// siblings, and the artifact objects. A step body must never replace them.
|
|
665
|
+
const engineStateName = ".flows";
|
|
666
|
+
const defaultMaxInlineBytes = 1024 * 1024;
|
|
667
|
+
const hostFailure = (cause) => new WorkspaceError({
|
|
668
|
+
code: "host_unavailable",
|
|
669
|
+
message: "the host filesystem could not serve the workspace transaction",
|
|
670
|
+
cause
|
|
671
|
+
});
|
|
672
|
+
const artifactFailure = (cause) => new WorkspaceError({
|
|
673
|
+
code: "host_unavailable",
|
|
674
|
+
message: `the artifact store could not serve the workspace transaction: ${cause.message}`,
|
|
675
|
+
cause
|
|
676
|
+
});
|
|
677
|
+
/**
|
|
678
|
+
* The errno names the descriptor-relative host reports when it refuses to
|
|
679
|
+
* resolve a path inside its pinned root: a symlink at the final component
|
|
680
|
+
* (`ELOOP`); a symlink at an intermediate component (`ELOOP` on Linux,
|
|
681
|
+
* `ENOTDIR` on Darwin, which also covers a file standing where a directory
|
|
682
|
+
* must be); or `..` traversal, a path outside the root, a hard-linked file, or
|
|
683
|
+
* a root whose identity changed (`EPERM`). Each means the change does not
|
|
684
|
+
* name a workspace entry reachable without following a link.
|
|
685
|
+
*/
|
|
686
|
+
const confinementCodes = new Set(["ELOOP", "ENOTDIR", "EPERM"]);
|
|
687
|
+
const refusedByConfinement = (error) => error.reason._tag !== "BadArgument" &&
|
|
688
|
+
typeof error.reason.cause === "object" && error.reason.cause !== null &&
|
|
689
|
+
confinementCodes.has(error.reason.cause.code);
|
|
690
|
+
/** Maps one copy-back host failure onto the sandbox's typed error codes. */
|
|
691
|
+
const copyBackFailure = (path) => (error) => refusedByConfinement(error)
|
|
692
|
+
? new WorkspaceError({
|
|
693
|
+
code: "path_escapes_workspace",
|
|
694
|
+
message: `materializing ${path} was refused: it does not name a workspace entry reachable without following a link (${error.message})`,
|
|
695
|
+
cause: error
|
|
696
|
+
})
|
|
697
|
+
: hostFailure(error);
|
|
698
|
+
/**
|
|
699
|
+
* Maps a commit-lock failure: a missed acquisition deadline is
|
|
700
|
+
* `commit_lock_timeout`, so a caller can tell "another writer holds the root"
|
|
701
|
+
* from "the host refused".
|
|
702
|
+
*/
|
|
703
|
+
const lockFailure = (cause) => Cause.isTimeoutError(cause)
|
|
704
|
+
? new WorkspaceError({
|
|
705
|
+
code: "commit_lock_timeout",
|
|
706
|
+
message: `another writer held ${commitLockName} for the whole acquisition deadline; a lock whose owner stopped is reclaimed once its heartbeat is ${FileLease.defaultStaleAfterMs} ms old`,
|
|
707
|
+
cause
|
|
708
|
+
})
|
|
709
|
+
// `FileLease` hands every other refusal over as the host's `PlatformError`.
|
|
710
|
+
: copyBackFailure(commitLockName)(cause);
|
|
711
|
+
// Over-approximates the spellings a case-insensitive, normalization-insensitive
|
|
712
|
+
// volume equates: `.FLOWS`, `.flow\u017f`, and `.\ufb02ows` all open `.flows` on
|
|
713
|
+
// default APFS. Folding more than a volume does only refuses more names.
|
|
714
|
+
const foldPath = (path) => path.normalize("NFKC").toUpperCase().toLowerCase().normalize("NFKC");
|
|
715
|
+
// NTFS with 8.3 name creation on (the system volume default) also opens a
|
|
716
|
+
// long name through its generated short name: `.flows` through `FLOWS~1`, or
|
|
717
|
+
// `FL3A2B~1` once the numbered tails collide. Linux reaches such a volume
|
|
718
|
+
// through WSL's `/mnt/c` and macOS through SMB, so the shape is refused on
|
|
719
|
+
// every host. The pattern is broader than the generator: any `~` and digit
|
|
720
|
+
// tail on a base of at most eight characters, with an extension of at most
|
|
721
|
+
// three.
|
|
722
|
+
const shortNameAlias = /^[^.~]{0,7}~\d{1,7}(\.[^.]{0,3})?$/u;
|
|
723
|
+
const isShortNameAlias = (segment) => {
|
|
724
|
+
// `split` always yields a first element, possibly empty.
|
|
725
|
+
const base = segment.split(".")[0];
|
|
726
|
+
return base.length <= 8 && shortNameAlias.test(segment);
|
|
727
|
+
};
|
|
728
|
+
/**
|
|
729
|
+
* Builds the filesystem-backed workspace sandbox.
|
|
730
|
+
*
|
|
731
|
+
* **Copy-in, not overlay.** The transaction is seeded with exactly the
|
|
732
|
+
* declared read set and nothing else
|
|
733
|
+
* (`docs/concepts/workspace-transactions.md`). Bazel uses the same strategy, which
|
|
734
|
+
* is why whole-tree write observation is structural here. Re-rooting a
|
|
735
|
+
* body onto a bare host tree instead would leave every undeclared file
|
|
736
|
+
* readable and reduce enforcement to the taxonomy's weakest, post-hoc
|
|
737
|
+
* detection-by-diff tier; and the OS-level overlay that would fix that is not
|
|
738
|
+
* reachable through Effect's `FileSystem` tag, so it could not run in a
|
|
739
|
+
* browser. Seeding costs a copy of the declared reads and buys both.
|
|
740
|
+
*
|
|
741
|
+
* **Copy-back is confined and journaled.** Every copy-back host call (the
|
|
742
|
+
* lock, preflight, apply, and rollback) goes through
|
|
743
|
+
* `@smthrs/kernel/FileSystem`'s `confined` view: one descriptor-relative,
|
|
744
|
+
* no-follow request against the workspace root pinned when the commit starts.
|
|
745
|
+
* A symlink anywhere on a change's path, a hard-linked file, or a root
|
|
746
|
+
* replaced mid-commit is refused with `path_escapes_workspace`, never
|
|
747
|
+
* followed, and no check-then-write window exists for a concurrent process to
|
|
748
|
+
* swap a link into. A copy-back through a symlink that stays inside the root
|
|
749
|
+
* is refused too: the body writes the referent path itself. `fs` must
|
|
750
|
+
* therefore carry the kernel's descriptor-relative executor (Node and Bun
|
|
751
|
+
* hosts use `@smthrs/platform-node`'s `AtomicFileSystem`) or a
|
|
752
|
+
* whole-filesystem isolation attestation (`withIsolatedFileSystem`, for an
|
|
753
|
+
* in-memory volume); a plain path-based host is refused at copy-back with
|
|
754
|
+
* `host_unavailable`. Snapshot reads use `fs` as given. A root-keyed semaphore and
|
|
755
|
+
* the `.smithers-workspace-lock` lease file (`@smthrs/artifacts/FileLease`)
|
|
756
|
+
* serialize cooperating callers, including separate processes. Preconditions run before file
|
|
757
|
+
* changes, and the apply loop keeps each target's pre-image for in-process
|
|
758
|
+
* rollback. A crash or rollback failure can leave partial changes; see
|
|
759
|
+
* {@link Service.materialize} for the caller's reconciliation obligations.
|
|
760
|
+
* Hosts must implement exclusive `wx` file creation, `stat` with an mtime,
|
|
761
|
+
* `utimes`, `rename`, and `remove` for the reserved lock. Copy-back
|
|
762
|
+
* also refuses the `.flows` engine state directory and any
|
|
763
|
+
* {@link FileSystemOptions.reservedPaths}, so no write set or boundary mode
|
|
764
|
+
* lets a step body replace the engine database or its artifact blobs.
|
|
765
|
+
*
|
|
766
|
+
* @category constructors
|
|
767
|
+
* @since 0.1.0
|
|
768
|
+
*/
|
|
769
|
+
export const makeFileSystem = (fs, platformPath, artifacts, workspaceRoot, options = {}) => {
|
|
770
|
+
const maxInlineBytes = options.maxInlineBytes ?? defaultMaxInlineBytes;
|
|
771
|
+
const root = workspaceRoot.replaceAll(/\/+$/g, "");
|
|
772
|
+
const reserved = [
|
|
773
|
+
commitLockName,
|
|
774
|
+
engineStateName,
|
|
775
|
+
...(options.reservedPaths ?? []).map((path) => path.replace(/^(\.\/)+/, "").replaceAll(/\/+$/g, ""))
|
|
776
|
+
].filter((path) => path !== "" && path !== ".").map((name) => ({ name, segments: foldPath(name).split("/") }));
|
|
777
|
+
// Why the root-relative `path` is refused, if it targets or lies beneath a
|
|
778
|
+
// reserved entry. The confined host refuses every symlink on a path, so the
|
|
779
|
+
// remaining aliases are spellings the volume itself equates: case,
|
|
780
|
+
// compatibility, and normalization variants on APFS and NTFS, and NTFS 8.3
|
|
781
|
+
// short names. Comparing folded segments, and refusing a short-name shape
|
|
782
|
+
// wherever a reserved segment stands, refuses them on every volume,
|
|
783
|
+
// case-sensitive ones included.
|
|
784
|
+
const reservedAt = (path) => {
|
|
785
|
+
const segments = foldPath(path).split("/");
|
|
786
|
+
const within = reserved.filter((entry) => entry.segments.length <= segments.length);
|
|
787
|
+
const exact = within.find((entry) => entry.segments.every((segment, index) => segments[index] === segment));
|
|
788
|
+
if (exact !== undefined)
|
|
789
|
+
return `the workspace path ${exact.name} is reserved`;
|
|
790
|
+
const alias = within.find((entry) =>
|
|
791
|
+
// `within` holds only entries no longer than the path, so every index is present.
|
|
792
|
+
entry.segments.every((segment, index) => segments[index] === segment || isShortNameAlias(segments[index])));
|
|
793
|
+
if (alias === undefined)
|
|
794
|
+
return undefined;
|
|
795
|
+
const spelling = path.split("/").slice(0, alias.segments.length).join("/");
|
|
796
|
+
return `the workspace path ${spelling} may be a short-name alias of a reserved path`;
|
|
797
|
+
};
|
|
798
|
+
const hostPath = (path) => root === "" ? path : `${root}/${path}`;
|
|
799
|
+
// One host call, not two: the read reports an absent path itself. On the
|
|
800
|
+
// confined host
|
|
801
|
+
// the `exists` probe that used to precede every read was a second CPython
|
|
802
|
+
// fork (`@smthrs/platform-node/AtomicFileSystem`) for an answer the read
|
|
803
|
+
// was about to give.
|
|
804
|
+
const readFrom = (host, path, onError) => host.readFile(path).pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(undefined)), Effect.mapError(onError));
|
|
805
|
+
const readIfPresent = Effect.fn("WorkspaceSandbox.readIfPresent")(function* (path) {
|
|
806
|
+
return yield* readFrom(fs, path, hostFailure);
|
|
807
|
+
});
|
|
808
|
+
const canonicalRoot = fs.realPath(root === "" ? "." : root).pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(undefined)), Effect.mapError(hostFailure));
|
|
809
|
+
const withCommitLock = (changes, effect) => Effect.gen(function* () {
|
|
810
|
+
for (const change of changes) {
|
|
811
|
+
const hit = reservedAt(change.path);
|
|
812
|
+
if (hit !== undefined)
|
|
813
|
+
return yield* Effect.fail(hostFailure(hit));
|
|
814
|
+
}
|
|
815
|
+
yield* fs.makeDirectory(root === "" ? "." : root, { recursive: true }).pipe(Effect.mapError(hostFailure));
|
|
816
|
+
// Pinned once per commit, after the root exists: every lock, preflight,
|
|
817
|
+
// apply, and rollback call below resolves against this root identity.
|
|
818
|
+
const host = yield* KernelFileSystem.confined(fs, root).pipe(Effect.provideService(Path.Path, platformPath), Effect.mapError(hostFailure));
|
|
819
|
+
const canonical = yield* canonicalRoot;
|
|
820
|
+
// The confined view refuses `utimes`, which the lease heartbeat needs.
|
|
821
|
+
// The heartbeat touches the lock only after a confined, no-follow read
|
|
822
|
+
// found this call's own token there, so the one path-based call it
|
|
823
|
+
// makes can at worst refresh an mtime.
|
|
824
|
+
const leaseHost = { ...host, utimes: fs.utimes };
|
|
825
|
+
return yield* coordinateCommit(canonical ?? root, FileLease.withLease(leaseHost, hostPath(commitLockName), effect(host), lockFailure, {
|
|
826
|
+
label: "Workspace commit lock",
|
|
827
|
+
annotations: { root: canonical ?? root }
|
|
828
|
+
}));
|
|
829
|
+
});
|
|
830
|
+
const commitWith = Effect.fn("WorkspaceSandbox.commit")(function* (host, changes) {
|
|
831
|
+
yield* Effect.annotateCurrentSpan({ changes: changes.length });
|
|
832
|
+
// The advisory lock and process semaphore cover preflight, apply, and
|
|
833
|
+
// rollback. Preconditions run only after both are acquired.
|
|
834
|
+
const { before, conflict } = yield* preflight(changes, (path) => readFrom(host, hostPath(path), copyBackFailure(path)));
|
|
835
|
+
if (conflict !== undefined) {
|
|
836
|
+
yield* Metric.update(EngineStoreMetrics.materializationConflicts, 1);
|
|
837
|
+
return yield* Effect.fail(conflict);
|
|
838
|
+
}
|
|
839
|
+
const resolved = new Map();
|
|
840
|
+
for (const change of changes) {
|
|
841
|
+
if (change.afterDigest === undefined)
|
|
842
|
+
continue;
|
|
843
|
+
const bytes = change.after ?? (yield* artifacts.get(`${change.afterDigest}`).pipe(Effect.mapError((error) => error._tag === "@smthrs/artifacts/ArtifactStoreError"
|
|
844
|
+
? artifactFailure(error)
|
|
845
|
+
: new WorkspaceError({
|
|
846
|
+
code: "not_found",
|
|
847
|
+
message: `the retained bytes for ${change.path} are unavailable: ${error.message}`,
|
|
848
|
+
cause: error
|
|
849
|
+
}))));
|
|
850
|
+
resolved.set(change.path, bytes);
|
|
851
|
+
}
|
|
852
|
+
const applied = [];
|
|
853
|
+
const apply = Effect.gen(function* () {
|
|
854
|
+
for (const change of changes) {
|
|
855
|
+
// Journal the change before touching its target: a write that
|
|
856
|
+
// fails halfway may still have mutated the file it was writing.
|
|
857
|
+
applied.push(change);
|
|
858
|
+
const target = hostPath(change.path);
|
|
859
|
+
if (change.afterDigest === undefined) {
|
|
860
|
+
yield* host.remove(target).pipe(Effect.mapError(copyBackFailure(change.path)));
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
const parent = parentDirectory(target);
|
|
864
|
+
if (parent !== undefined) {
|
|
865
|
+
yield* host.makeDirectory(parent, { recursive: true }).pipe(Effect.mapError(copyBackFailure(change.path)));
|
|
866
|
+
}
|
|
867
|
+
yield* host.writeFile(target, resolved.get(change.path)).pipe(Effect.mapError(copyBackFailure(change.path)));
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
const rollback = Effect.gen(function* () {
|
|
871
|
+
for (const change of [...applied].reverse()) {
|
|
872
|
+
const target = hostPath(change.path);
|
|
873
|
+
const previous = before.get(change.path);
|
|
874
|
+
if (previous === undefined) {
|
|
875
|
+
yield* host.remove(target, { force: true }).pipe(Effect.mapError(copyBackFailure(change.path)));
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
yield* host.writeFile(target, previous).pipe(Effect.mapError(copyBackFailure(change.path)));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
// Keep empty parent directories. Effect's portable FileSystem has no
|
|
882
|
+
// atomic, non-recursive rmdir; recursively deleting a directory after
|
|
883
|
+
// a separate emptiness check could erase a concurrent writer's file.
|
|
884
|
+
});
|
|
885
|
+
yield* apply.pipe(Effect.catchCause((cause) => Effect.gen(function* () {
|
|
886
|
+
const restored = yield* Effect.exit(rollback);
|
|
887
|
+
if (Exit.isSuccess(restored))
|
|
888
|
+
return yield* Effect.failCause(cause);
|
|
889
|
+
const reasons = restored.cause.reasons
|
|
890
|
+
.filter(Cause.isFailReason)
|
|
891
|
+
.map((reason) => reason.error.message);
|
|
892
|
+
// Both failures travel: the apply cause that opened the window and
|
|
893
|
+
// the rollback cause that could not close it, composed the way
|
|
894
|
+
// `acquireUseRelease` composes a failed use with a failed release.
|
|
895
|
+
return yield* Effect.failCause(Cause.combine(cause, Cause.fail(new WorkspaceError({
|
|
896
|
+
code: "host_unavailable",
|
|
897
|
+
message: `copy-back failed mid-apply and rollback could not restore the workspace: ${reasons.join("; ")}`,
|
|
898
|
+
cause: restored.cause
|
|
899
|
+
}))));
|
|
900
|
+
})),
|
|
901
|
+
// Interruption inside the apply window is a mid-sequence abort the
|
|
902
|
+
// journal exists to prevent; the window is bounded local work, so it
|
|
903
|
+
// closes before the fiber answers the interrupt.
|
|
904
|
+
Effect.uninterruptible);
|
|
905
|
+
});
|
|
906
|
+
return makeHosted({
|
|
907
|
+
root,
|
|
908
|
+
snapshot: Effect.fn("WorkspaceSandbox.snapshot")(function* (descriptor) {
|
|
909
|
+
yield* Effect.annotateCurrentSpan({
|
|
910
|
+
reads: descriptor.readSet.length,
|
|
911
|
+
boundaryMode: descriptor.boundaryMode
|
|
912
|
+
});
|
|
913
|
+
const base = new Map();
|
|
914
|
+
for (const entry of descriptor.readSet) {
|
|
915
|
+
// Through `FileEnumeration`, never the host `glob`: host results skip
|
|
916
|
+
// dotfiles, so a declared read glob covering one would seed a sandbox
|
|
917
|
+
// that silently hides it from the body.
|
|
918
|
+
const paths = FileSet.isGlob(entry)
|
|
919
|
+
? yield* FileEnumeration.expandGlob(fs, entry, {
|
|
920
|
+
resolve: (path) => path === "" ? (root === "" ? "." : root) : hostPath(path)
|
|
921
|
+
}).pipe(Effect.mapError(hostFailure))
|
|
922
|
+
: [entry.path];
|
|
923
|
+
for (const path of paths) {
|
|
924
|
+
const normalized = normalizePath(root, path);
|
|
925
|
+
if (Result.isFailure(normalized))
|
|
926
|
+
return yield* Effect.fail(normalized.failure);
|
|
927
|
+
const content = yield* readIfPresent(hostPath(normalized.success));
|
|
928
|
+
// A declared read that does not exist is seeded as absent, not as an
|
|
929
|
+
// error: `StepBoundary.prepare` already records the mismatch as the
|
|
930
|
+
// evidence that refuses the cache hit.
|
|
931
|
+
if (content !== undefined)
|
|
932
|
+
base.set(normalized.success, content);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
for (const path of descriptor.removes ?? []) {
|
|
936
|
+
const normalized = normalizePath(root, path);
|
|
937
|
+
/* v8 ignore next -- `FileBoundary.removes` is `FileSet.Pattern`-checked, so a decoded boundary cannot carry an upward or absolute removal; the branch guards hand-built descriptors */
|
|
938
|
+
if (Result.isFailure(normalized))
|
|
939
|
+
return yield* Effect.fail(normalized.failure);
|
|
940
|
+
const content = yield* readIfPresent(hostPath(normalized.success));
|
|
941
|
+
if (content !== undefined)
|
|
942
|
+
base.set(normalized.success, content);
|
|
943
|
+
}
|
|
944
|
+
return base;
|
|
945
|
+
}),
|
|
946
|
+
baseline: Effect.fn("WorkspaceSandbox.baseline")(function* (path) {
|
|
947
|
+
const content = yield* readIfPresent(hostPath(path));
|
|
948
|
+
return content === undefined ? undefined : yield* digestOf(content);
|
|
949
|
+
}),
|
|
950
|
+
// The bytes now live at the change's `afterDigest`; the change itself
|
|
951
|
+
// carries only the reference, exactly as oversized `StepBoundary`
|
|
952
|
+
// evidence does.
|
|
953
|
+
retain: (bytes) => bytes.length <= maxInlineBytes
|
|
954
|
+
? Effect.succeed(bytes)
|
|
955
|
+
: artifacts.put(bytes).pipe(Effect.mapError(artifactFailure), Effect.as(undefined)),
|
|
956
|
+
commit: (changes) => withCommitLock(changes, (host) => commitWith(host, changes))
|
|
957
|
+
});
|
|
958
|
+
};
|
|
959
|
+
/**
|
|
960
|
+
* Provides the filesystem-backed workspace sandbox.
|
|
961
|
+
*
|
|
962
|
+
* Host access arrives through Effect's `FileSystem` tag — the same tag the
|
|
963
|
+
* capability kernel decorates in place — and blob retention through
|
|
964
|
+
* `@smthrs/artifacts`, so the same sandbox runs over a purely local store or a
|
|
965
|
+
* local-plus-shared composition without knowing which it got. The workspace
|
|
966
|
+
* root arrives through the kernel's `Workspace` service, so absolute paths a
|
|
967
|
+
* body resolved for itself still land inside the transaction.
|
|
968
|
+
*
|
|
969
|
+
* The layer refuses to build over a host that {@link makeFileSystem} could
|
|
970
|
+
* not confine (a plain path-based `FileSystem`), failing with
|
|
971
|
+
* `host_unavailable`, so a composition that could never copy back safely is
|
|
972
|
+
* rejected at startup rather than at its first commit.
|
|
973
|
+
*
|
|
974
|
+
* @category layers
|
|
975
|
+
* @since 0.1.0
|
|
976
|
+
*/
|
|
977
|
+
export const layerFileSystem = (options = {}) => Layer.effect(WorkspaceSandbox, Effect.gen(function* () {
|
|
978
|
+
const fs = yield* FileSystem.FileSystem;
|
|
979
|
+
const platformPath = yield* Path.Path;
|
|
980
|
+
const artifacts = yield* ArtifactStore.ArtifactStore;
|
|
981
|
+
const workspace = yield* KernelWorkspace;
|
|
982
|
+
yield* KernelFileSystem.requireConfinable(fs, workspace.root).pipe(Effect.mapError(hostFailure));
|
|
983
|
+
return makeFileSystem(fs, platformPath, artifacts, workspace.root, options);
|
|
984
|
+
}));
|
|
985
|
+
//# sourceMappingURL=WorkspaceSandbox.js.map
|