/** * The Deno lifecycle provider's read-only half. * * Inspection is a separate physical path from execution, not a politer way of * using the same one. It opens each database read-only, on a connection of its * own, outside the execution pool: the pool's connections carry DOFS caches, an * active-transaction marker and a teardown that rolls back, and none of that * belongs to a command whose whole contract is that it changes nothing. SQLite * itself refuses a write on these handles, so "read-only" is enforced by the * database rather than promised by this module. * * ## Discovery is arithmetic * * A run's file is the SHA-256 of its id with a `.sqlite` suffix directly beneath * the authorized root, so `status` and `history` derive one path and `list` * enumerates exactly that namespace. Lifecycle sidecars occupy names this * pattern does not match and are never candidates. There is no registry, so * there is nothing that could disagree with the files. * * ## One bad candidate fails the list * * A foreign, incompatible, damaged or unparseable candidate is reported as * itself and ends the request. Returning the healthy rows would answer "these * are your runs" with a list that is missing one nobody was told about. * * ## One snapshot, one moment * * Every field of a snapshot is read inside one transaction, so the record, the * executions, the journal frontier and the current Workspace root cannot come * from different commits of the same run. */ import { DatabaseSync } from "node:sqlite"; import { type WorkflowRunConnections } from "./connections.js"; import { type Operation } from "effection"; import { type WorkflowExecutionTransitions } from "../lifecycle/execution.js"; import type { LegacyWorkflowSourceReader } from "../lifecycle/source.js"; /** A point recovered inspection passes, named so a test can stop it there. */ export type RecoveryPhase = "scratch-created" | "source-pair-copied" | "scratch-recovered" | "before-cleanup"; /** * What recovered inspection reports about where it has reached. * * The directory is host arrangement this installation just made, which is what * lets a test plant a real filesystem fault or start a real competing process * at the exact moment that matters. It is not retained state, and nothing else * about the run travels with it: no lock, no transition, no database handle and * no value the run kept. */ export interface RecoveryObservation { readonly phase: RecoveryPhase; readonly directory: string; } /** * A test's view of recovered inspection, and its chance to pause one. * * Internal to this module and the installation that takes it. Production hands * over `unobserved`, so nothing in a shipped run consults an environment * variable, a file or a registry to decide how inspection behaves. */ export type RecoveryObserver = (observation: RecoveryObservation) => Operation; export interface WorkflowLifecycleOptions { /** The directory this host keeps runs in. Absolute, as storage requires. */ readonly root: string; /** * How this host turns a retained version-1 definition back into Markdown. * * Captured in the provider's closure at installation and reachable through no * Context, contextual Api, component, Plugin installation result or authored * value. A host that installs none can inspect and control runs and cannot * begin, fork or export a Git one — which is the honest answer, because it * has no way to obtain what such a run executes. * * Version 2 never consults it: a source bundle's content is in the run's own * store, and reaching a repository for it would be a second source of truth. */ readonly legacySource?: LegacyWorkflowSourceReader; } /** * Install lifecycle for the current scope and its descendants. * * `{ at: "min" }` for the same reason storage uses it: middleware at the * default position runs outermost, so an outer scope's provider would answer * ahead of one installed nearer the work. * * The executor registry belongs to this installation's closure rather than to * module scope, so the locks it issued last exactly as long as the scope that * installed the provider and nothing accumulates between runs. * */ export declare function useWorkflowLifecycle(options: WorkflowLifecycleOptions): Operation; /** * The same installation, over a registry the host already owns. * * Storage writes to the same databases, so a host running both hands each the * one registry rather than letting either open a second authoritative * connection. Inspection still stays off it entirely: a read-only snapshot has * its own connection and never enters the pool execution serializes on. */ export declare function installWorkflowLifecycle(options: WorkflowLifecycleOptions, connections: WorkflowRunConnections, observe?: RecoveryObserver): Operation; export declare function retainedRoots(database: DatabaseSync): ReadonlySet;