@agentxm/workspace-state 0.28.11 → 0.28.13
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/dist/src/index.d.ts +22 -13
- package/dist/src/index.js +25 -16
- package/dist/src/live.d.ts +46 -0
- package/dist/src/live.js +60 -0
- package/dist/src/lockfile/lockfile.js +1 -2
- package/dist/src/settings/index.d.ts +1 -1
- package/dist/src/settings/index.js +1 -1
- package/dist/src/settings/schema.d.ts +12 -0
- package/dist/src/settings/schema.js +13 -1
- package/dist/src/settings/settings.d.ts +1 -1
- package/dist/src/settings/settings.js +1 -3
- package/dist/src/testing.d.ts +24 -0
- package/dist/src/testing.js +35 -0
- package/dist/src/utils/path-safety.d.ts +0 -5
- package/dist/src/utils/path-safety.js +1 -10
- package/dist/src/workspace/accepted-canonical-ref.d.ts +1 -1
- package/dist/src/workspace/accepted-canonical-ref.js +1 -1
- package/dist/src/workspace/accepted-resolution-writer.d.ts +42 -0
- package/dist/src/workspace/accepted-resolution-writer.js +67 -0
- package/dist/src/workspace/canonical-observation.js +1 -1
- package/dist/src/workspace/configured-entry.d.ts +11 -0
- package/dist/src/workspace/configured-entry.js +8 -0
- package/dist/src/workspace/create-symlink.d.ts +1 -1
- package/dist/src/workspace/create-symlink.js +1 -2
- package/dist/src/workspace/desired-identity.d.ts +19 -0
- package/dist/src/workspace/desired-identity.js +26 -0
- package/dist/src/workspace/desired-state-reader.d.ts +32 -0
- package/dist/src/workspace/desired-state-reader.js +56 -0
- package/dist/src/workspace/desired-state-writer.d.ts +46 -0
- package/dist/src/workspace/desired-state-writer.js +214 -0
- package/dist/src/workspace/entry-accessors.d.ts +50 -0
- package/dist/src/workspace/entry-accessors.js +41 -0
- package/dist/src/workspace/extension-paths-service.d.ts +33 -0
- package/dist/src/workspace/extension-paths-service.js +133 -0
- package/dist/src/workspace/location.d.ts +49 -0
- package/dist/src/workspace/location.js +85 -0
- package/dist/src/workspace/lockfile-reader.d.ts +41 -0
- package/dist/src/workspace/lockfile-reader.js +54 -0
- package/dist/src/workspace/materialized-file-target.d.ts +2 -2
- package/dist/src/workspace/materialized-file-target.js +2 -2
- package/dist/src/workspace/other-scope-reader.d.ts +34 -0
- package/dist/src/workspace/other-scope-reader.js +35 -0
- package/dist/src/workspace/paths.d.ts +2 -2
- package/dist/src/workspace/read-model/__fixtures__/test-layer.d.ts +1 -1
- package/dist/src/workspace/read-model/__fixtures__/test-layer.js +1 -1
- package/dist/src/workspace/read-model/discovery/skills.d.ts +1 -1
- package/dist/src/workspace/read-model/discovery/skills.js +1 -1
- package/dist/src/workspace/read-model/scanners/canonical-extensions.js +2 -2
- package/dist/src/workspace/read-model/scanners/mcp-config.js +1 -1
- package/dist/src/workspace/read-model/service.js +1 -1
- package/dist/src/workspace/read-model-record-readers.js +1 -1
- package/dist/src/workspace/remove-if-exists.d.ts +1 -1
- package/dist/src/workspace/remove-if-exists.js +1 -2
- package/dist/src/workspace/service-interface.d.ts +8 -60
- package/dist/src/workspace/service.d.ts +26 -1886
- package/dist/src/workspace/service.js +144 -1285
- package/dist/src/workspace/settings-reader.d.ts +59 -0
- package/dist/src/workspace/settings-reader.js +109 -0
- package/dist/src/workspace/settings-writer.d.ts +53 -0
- package/dist/src/workspace/settings-writer.js +90 -0
- package/dist/src/workspace/setup-scope-support.js +1 -1
- package/dist/src/workspace/shared.d.ts +24 -0
- package/dist/src/workspace/shared.js +20 -0
- package/dist/src/workspace/state-cells.d.ts +35 -0
- package/dist/src/workspace/state-cells.js +42 -0
- package/dist/src/workspace/subagent-paths.d.ts +49 -0
- package/dist/src/workspace/subagent-paths.js +26 -0
- package/dist/src/workspace/test-stubs.js +0 -11
- package/dist/src/workspace/workspace-records.d.ts +41 -0
- package/dist/src/workspace/workspace-records.js +43 -0
- package/package.json +14 -5
- package/dist/src/utils/atomic-write.d.ts +0 -67
- package/dist/src/utils/atomic-write.js +0 -71
- package/dist/src/workspace/configured-entry-resolution/types.d.ts +0 -32
- package/dist/src/workspace/configured-entry-resolution/types.js +0 -2
- package/dist/src/workspace/footprint-recorder.d.ts +0 -30
- package/dist/src/workspace/footprint-recorder.js +0 -32
- package/dist/src/workspace/mcp-entry-semantics.d.ts +0 -28
- package/dist/src/workspace/mcp-entry-semantics.js +0 -58
- package/dist/src/workspace/read-model/agent-presence.d.ts +0 -32
- package/dist/src/workspace/read-model/agent-presence.js +0 -21
- package/dist/src/workspace/scope-refusal.d.ts +0 -26
- package/dist/src/workspace/scope-refusal.js +0 -29
- package/dist/src/workspace/transaction.d.ts +0 -218
- package/dist/src/workspace/transaction.js +0 -177
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace transaction state: the ambient authority context, the write
|
|
3
|
-
* registration primitives every workspace writer calls, and the typed
|
|
4
|
-
* failure vocabulary of the transaction and transition-lock machinery.
|
|
5
|
-
*
|
|
6
|
-
* This module owns the ambient transaction/closure references and the
|
|
7
|
-
* protection primitives (`protectWorkspacePath`, `protectCreatedAncestors`)
|
|
8
|
-
* that snapshot a path before its first mutation. The transaction runner and
|
|
9
|
-
* closure settlement mechanics that consume this context live in
|
|
10
|
-
* `./operations/transaction.ts`; the application error boundary owns the
|
|
11
|
-
* rendering, codes, and suggestions for every failure declared here.
|
|
12
|
-
*
|
|
13
|
-
* @experimental This API is unstable and may change without notice.
|
|
14
|
-
*/
|
|
15
|
-
import type * as Cause from "effect/Cause";
|
|
16
|
-
import * as ServiceMap from "effect/Context";
|
|
17
|
-
import * as Effect from "effect/Effect";
|
|
18
|
-
import * as FileSystem from "effect/FileSystem";
|
|
19
|
-
import * as Option from "effect/Option";
|
|
20
|
-
import * as Path from "effect/Path";
|
|
21
|
-
import * as Semaphore from "effect/Semaphore";
|
|
22
|
-
/** One pre-mutation preimage of a protected path, keyed to its owning closure. */
|
|
23
|
-
export type Snapshot = {
|
|
24
|
-
readonly closure: string | undefined;
|
|
25
|
-
} & ({
|
|
26
|
-
readonly target: string;
|
|
27
|
-
readonly state: "absent";
|
|
28
|
-
} | {
|
|
29
|
-
readonly target: string;
|
|
30
|
-
readonly state: "copied";
|
|
31
|
-
readonly backup: string;
|
|
32
|
-
} | {
|
|
33
|
-
readonly target: string;
|
|
34
|
-
readonly state: "symlink";
|
|
35
|
-
readonly linkTarget: string;
|
|
36
|
-
});
|
|
37
|
-
/** A closure rollback that could not complete, recorded for the transaction end. */
|
|
38
|
-
export interface PendingClosureRestorationFailure {
|
|
39
|
-
readonly closureId: string;
|
|
40
|
-
readonly restorationCause: unknown;
|
|
41
|
-
readonly retained: ReadonlyArray<string>;
|
|
42
|
-
}
|
|
43
|
-
export interface WorkspaceTransactionContext {
|
|
44
|
-
readonly fs: FileSystem.FileSystem;
|
|
45
|
-
readonly path: Path.Path;
|
|
46
|
-
readonly workspaceDir: string;
|
|
47
|
-
/** Uniquely prefixed OS-temporary directory holding rollback snapshots. */
|
|
48
|
-
readonly snapshotStore: {
|
|
49
|
-
dir: string | undefined;
|
|
50
|
-
};
|
|
51
|
-
/** Per-closure first-touch dedupe; the key "" is the operation closure. */
|
|
52
|
-
readonly protectedTargets: Map<string, Set<string>>;
|
|
53
|
-
readonly snapshots: Array<Snapshot>;
|
|
54
|
-
readonly snapshotSemaphore: Semaphore.Semaphore;
|
|
55
|
-
/** Closure rollbacks that failed; the transaction fails typed at its end. */
|
|
56
|
-
readonly pendingRestorationFailures: Array<PendingClosureRestorationFailure>;
|
|
57
|
-
/** Monotonic backup-name counter: settlement drops entries, names never recur. */
|
|
58
|
-
readonly snapshotSequence: {
|
|
59
|
-
value: number;
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
export declare const CurrentWorkspaceTransaction: ServiceMap.Reference<Option.Option<WorkspaceTransactionContext>>;
|
|
63
|
-
/**
|
|
64
|
-
* The semantic closure whose mutations are currently executing. Snapshots
|
|
65
|
-
* taken while a closure is active belong to it: they are dropped when the
|
|
66
|
-
* closure settles and restored when it — and only it — rolls back. Snapshots
|
|
67
|
-
* taken outside any closure belong to the operation closure and are restored
|
|
68
|
-
* by the transaction's own failure handling.
|
|
69
|
-
*/
|
|
70
|
-
export declare const CurrentWorkspaceClosure: ServiceMap.Reference<string | undefined>;
|
|
71
|
-
declare const WorkspaceRestorationIncomplete_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
72
|
-
readonly _tag: "WorkspaceRestorationIncomplete";
|
|
73
|
-
} & Readonly<A>;
|
|
74
|
-
/**
|
|
75
|
-
* Restoration did not complete: the typed fact the terminal resolution
|
|
76
|
-
* derives outcome, disposition, and exit status from. The pre-change
|
|
77
|
-
* snapshots survive in the OS-temporary snapshot directory; nothing about
|
|
78
|
-
* this failure persists in the workspace, and the next mutation converges
|
|
79
|
-
* from the current workspace state.
|
|
80
|
-
*/
|
|
81
|
-
export declare class WorkspaceRestorationIncomplete extends WorkspaceRestorationIncomplete_base<{
|
|
82
|
-
readonly terminationCause: "failure" | "interruption";
|
|
83
|
-
readonly transitionCause: Cause.Cause<unknown>;
|
|
84
|
-
readonly restorationCause: unknown;
|
|
85
|
-
/** OS-temporary directory preserving the pre-change snapshots, when any were taken. */
|
|
86
|
-
readonly snapshotDir: string | undefined;
|
|
87
|
-
/** Protected paths, workspace-root-relative where possible, left as the failure left them. */
|
|
88
|
-
readonly retained: ReadonlyArray<string>;
|
|
89
|
-
/** Closures whose rollback did not complete, when closure-scoped. */
|
|
90
|
-
readonly closureIds?: ReadonlyArray<string>;
|
|
91
|
-
}> {
|
|
92
|
-
}
|
|
93
|
-
/** Identity an invocation records while it holds the workspace transition. */
|
|
94
|
-
export interface TransitionLockHolder {
|
|
95
|
-
readonly command: string;
|
|
96
|
-
readonly pid: number;
|
|
97
|
-
readonly candidateId?: string;
|
|
98
|
-
}
|
|
99
|
-
export interface TransitionContention {
|
|
100
|
-
/** The holder recorded by the invocation that owns the lock, when readable. */
|
|
101
|
-
readonly holder: Option.Option<TransitionLockHolder>;
|
|
102
|
-
readonly waitedMillis: number;
|
|
103
|
-
}
|
|
104
|
-
declare const WorkspaceSnapshotError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
105
|
-
readonly _tag: "WorkspaceSnapshotError";
|
|
106
|
-
} & Readonly<A>;
|
|
107
|
-
/**
|
|
108
|
-
* Registering a path with the active transaction failed: the pre-mutation
|
|
109
|
-
* preimage could not be taken, so the path was never mutated. `target` is the
|
|
110
|
-
* path being protected; the `create-store` step's message interpolates
|
|
111
|
-
* nothing.
|
|
112
|
-
*/
|
|
113
|
-
export declare class WorkspaceSnapshotError extends WorkspaceSnapshotError_base<{
|
|
114
|
-
readonly target: string;
|
|
115
|
-
readonly step: "inspect-target" | "create-store" | "copy" | "inspect-ancestor";
|
|
116
|
-
readonly cause: unknown;
|
|
117
|
-
}> {
|
|
118
|
-
}
|
|
119
|
-
declare const WorkspaceDirectoryError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
120
|
-
readonly _tag: "WorkspaceDirectoryError";
|
|
121
|
-
} & Readonly<A>;
|
|
122
|
-
/** Preparing the workspace state directory for a transition failed. */
|
|
123
|
-
export declare class WorkspaceDirectoryError extends WorkspaceDirectoryError_base<{
|
|
124
|
-
readonly path: string;
|
|
125
|
-
readonly step: "inspect" | "create";
|
|
126
|
-
readonly cause: unknown;
|
|
127
|
-
}> {
|
|
128
|
-
}
|
|
129
|
-
declare const TransitionLockError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
130
|
-
readonly _tag: "TransitionLockError";
|
|
131
|
-
} & Readonly<A>;
|
|
132
|
-
/**
|
|
133
|
-
* Workspace transition-lock mechanics failed. `path` carries the fact each
|
|
134
|
-
* step's message interpolates: the scratch directory for `create-scratch`,
|
|
135
|
-
* the lock path otherwise. `missing-timestamp` has no underlying cause.
|
|
136
|
-
*/
|
|
137
|
-
export declare class TransitionLockError extends TransitionLockError_base<{
|
|
138
|
-
readonly path: string;
|
|
139
|
-
readonly step: "create-scratch" | "acquire" | "record-holder" | "inspect-timestamp" | "missing-timestamp" | "preserve-timestamp" | "release";
|
|
140
|
-
readonly cause?: unknown;
|
|
141
|
-
}> {
|
|
142
|
-
}
|
|
143
|
-
declare const TransitionLockUnavailable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
144
|
-
readonly _tag: "TransitionLockUnavailable";
|
|
145
|
-
} & Readonly<A>;
|
|
146
|
-
/**
|
|
147
|
-
* The bounded contention wait elapsed while another invocation held the
|
|
148
|
-
* workspace transition.
|
|
149
|
-
*/
|
|
150
|
-
export declare class TransitionLockUnavailable extends TransitionLockUnavailable_base<{
|
|
151
|
-
readonly holder: TransitionLockHolder | undefined;
|
|
152
|
-
readonly waitedMillis: number;
|
|
153
|
-
}> {
|
|
154
|
-
}
|
|
155
|
-
declare const WorkspaceTransitionCompromised_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
156
|
-
readonly _tag: "WorkspaceTransitionCompromised";
|
|
157
|
-
} & Readonly<A>;
|
|
158
|
-
/**
|
|
159
|
-
* The hold is no longer provably owned: ownership could not be confirmed
|
|
160
|
-
* within the staleness window, so a contender may already have reclaimed the
|
|
161
|
-
* lock. Any further durable write by the original owner — mutation and
|
|
162
|
-
* restoration alike — could overwrite a successor's work.
|
|
163
|
-
*/
|
|
164
|
-
export declare class WorkspaceTransitionCompromised extends WorkspaceTransitionCompromised_base<{
|
|
165
|
-
readonly workspaceDir: string;
|
|
166
|
-
readonly lockPath: string;
|
|
167
|
-
readonly cause: unknown;
|
|
168
|
-
}> {
|
|
169
|
-
}
|
|
170
|
-
declare const WorkspaceRestorationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
171
|
-
readonly _tag: "WorkspaceRestorationError";
|
|
172
|
-
} & Readonly<A>;
|
|
173
|
-
/**
|
|
174
|
-
* One restoration step did not complete or verify. Never a channel failure:
|
|
175
|
-
* it travels as the `restorationCause` inside
|
|
176
|
-
* {@link WorkspaceRestorationIncomplete} and the pending closure records.
|
|
177
|
-
*/
|
|
178
|
-
export declare class WorkspaceRestorationError extends WorkspaceRestorationError_base<{
|
|
179
|
-
readonly target: string;
|
|
180
|
-
readonly step: "stage" | "stopped" | "verify";
|
|
181
|
-
readonly cause: unknown;
|
|
182
|
-
}> {
|
|
183
|
-
}
|
|
184
|
-
/** Failures acquiring the workspace transition lock. */
|
|
185
|
-
export type WorkspaceTransitionAcquireFailure = WorkspaceDirectoryError | TransitionLockError;
|
|
186
|
-
/** Failures the transaction machinery itself produces, beside the transition's own. */
|
|
187
|
-
export type WorkspaceTransactionFailure = WorkspaceSnapshotError | WorkspaceDirectoryError | TransitionLockError | TransitionLockUnavailable | WorkspaceTransitionCompromised;
|
|
188
|
-
/**
|
|
189
|
-
* Snapshot one target into the active transaction context, deduplicating on
|
|
190
|
-
* first touch per closure. Shared by the registration primitives below and by
|
|
191
|
-
* the transaction runner claiming its declared targets.
|
|
192
|
-
*/
|
|
193
|
-
export declare const protectInContext: (context: WorkspaceTransactionContext, target: string, closure: string | undefined) => Effect.Effect<void, WorkspaceSnapshotError>;
|
|
194
|
-
/** Snapshot a path before its first mutation when a workspace transaction is active. */
|
|
195
|
-
export declare const protectWorkspacePath: (target: string) => Effect.Effect<void, WorkspaceSnapshotError>;
|
|
196
|
-
/**
|
|
197
|
-
* Closure rollbacks that could not complete and verify, with the snapshot
|
|
198
|
-
* store that still preserves their pre-change bytes. Read at the end of a
|
|
199
|
-
* plan apply so the terminal resolution derives retained state from the
|
|
200
|
-
* in-memory facts alone. `None` outside a transaction.
|
|
201
|
-
*/
|
|
202
|
-
export declare const readPendingClosureRestorationFailures: Effect.Effect<Option.Option<{
|
|
203
|
-
readonly failures: ReadonlyArray<{
|
|
204
|
-
readonly closureId: string;
|
|
205
|
-
readonly restorationCause: unknown;
|
|
206
|
-
readonly retained: ReadonlyArray<string>;
|
|
207
|
-
}>;
|
|
208
|
-
readonly snapshotDir: string | undefined;
|
|
209
|
-
}>>;
|
|
210
|
-
/**
|
|
211
|
-
* Protect the first ancestor a recursive directory creation is about to
|
|
212
|
-
* create, so restoration removes the created directory chain instead of
|
|
213
|
-
* leaving empty parents behind. No-op outside a transaction or when the
|
|
214
|
-
* directory already exists.
|
|
215
|
-
*/
|
|
216
|
-
export declare const protectCreatedAncestors: (fs: FileSystem.FileSystem, path: Path.Path, directory: string) => Effect.Effect<void, WorkspaceSnapshotError>;
|
|
217
|
-
export {};
|
|
218
|
-
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace transaction state: the ambient authority context, the write
|
|
3
|
-
* registration primitives every workspace writer calls, and the typed
|
|
4
|
-
* failure vocabulary of the transaction and transition-lock machinery.
|
|
5
|
-
*
|
|
6
|
-
* This module owns the ambient transaction/closure references and the
|
|
7
|
-
* protection primitives (`protectWorkspacePath`, `protectCreatedAncestors`)
|
|
8
|
-
* that snapshot a path before its first mutation. The transaction runner and
|
|
9
|
-
* closure settlement mechanics that consume this context live in
|
|
10
|
-
* `./operations/transaction.ts`; the application error boundary owns the
|
|
11
|
-
* rendering, codes, and suggestions for every failure declared here.
|
|
12
|
-
*
|
|
13
|
-
* @experimental This API is unstable and may change without notice.
|
|
14
|
-
*/
|
|
15
|
-
import * as ServiceMap from "effect/Context";
|
|
16
|
-
import * as Data from "effect/Data";
|
|
17
|
-
import * as Effect from "effect/Effect";
|
|
18
|
-
import * as FileSystem from "effect/FileSystem";
|
|
19
|
-
import * as Option from "effect/Option";
|
|
20
|
-
import * as Path from "effect/Path";
|
|
21
|
-
import * as Semaphore from "effect/Semaphore";
|
|
22
|
-
export const CurrentWorkspaceTransaction = ServiceMap.Reference("@agentxm/workspace-state/workspace/transaction/CurrentWorkspaceTransaction", {
|
|
23
|
-
defaultValue: () => Option.none(),
|
|
24
|
-
});
|
|
25
|
-
/**
|
|
26
|
-
* The semantic closure whose mutations are currently executing. Snapshots
|
|
27
|
-
* taken while a closure is active belong to it: they are dropped when the
|
|
28
|
-
* closure settles and restored when it — and only it — rolls back. Snapshots
|
|
29
|
-
* taken outside any closure belong to the operation closure and are restored
|
|
30
|
-
* by the transaction's own failure handling.
|
|
31
|
-
*/
|
|
32
|
-
export const CurrentWorkspaceClosure = ServiceMap.Reference("@agentxm/workspace-state/workspace/transaction/CurrentWorkspaceClosure", { defaultValue: () => undefined });
|
|
33
|
-
/**
|
|
34
|
-
* Restoration did not complete: the typed fact the terminal resolution
|
|
35
|
-
* derives outcome, disposition, and exit status from. The pre-change
|
|
36
|
-
* snapshots survive in the OS-temporary snapshot directory; nothing about
|
|
37
|
-
* this failure persists in the workspace, and the next mutation converges
|
|
38
|
-
* from the current workspace state.
|
|
39
|
-
*/
|
|
40
|
-
export class WorkspaceRestorationIncomplete extends Data.TaggedError("WorkspaceRestorationIncomplete") {
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Registering a path with the active transaction failed: the pre-mutation
|
|
44
|
-
* preimage could not be taken, so the path was never mutated. `target` is the
|
|
45
|
-
* path being protected; the `create-store` step's message interpolates
|
|
46
|
-
* nothing.
|
|
47
|
-
*/
|
|
48
|
-
export class WorkspaceSnapshotError extends Data.TaggedError("WorkspaceSnapshotError") {
|
|
49
|
-
}
|
|
50
|
-
/** Preparing the workspace state directory for a transition failed. */
|
|
51
|
-
export class WorkspaceDirectoryError extends Data.TaggedError("WorkspaceDirectoryError") {
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Workspace transition-lock mechanics failed. `path` carries the fact each
|
|
55
|
-
* step's message interpolates: the scratch directory for `create-scratch`,
|
|
56
|
-
* the lock path otherwise. `missing-timestamp` has no underlying cause.
|
|
57
|
-
*/
|
|
58
|
-
export class TransitionLockError extends Data.TaggedError("TransitionLockError") {
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* The bounded contention wait elapsed while another invocation held the
|
|
62
|
-
* workspace transition.
|
|
63
|
-
*/
|
|
64
|
-
export class TransitionLockUnavailable extends Data.TaggedError("TransitionLockUnavailable") {
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* The hold is no longer provably owned: ownership could not be confirmed
|
|
68
|
-
* within the staleness window, so a contender may already have reclaimed the
|
|
69
|
-
* lock. Any further durable write by the original owner — mutation and
|
|
70
|
-
* restoration alike — could overwrite a successor's work.
|
|
71
|
-
*/
|
|
72
|
-
export class WorkspaceTransitionCompromised extends Data.TaggedError("WorkspaceTransitionCompromised") {
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* One restoration step did not complete or verify. Never a channel failure:
|
|
76
|
-
* it travels as the `restorationCause` inside
|
|
77
|
-
* {@link WorkspaceRestorationIncomplete} and the pending closure records.
|
|
78
|
-
*/
|
|
79
|
-
export class WorkspaceRestorationError extends Data.TaggedError("WorkspaceRestorationError") {
|
|
80
|
-
}
|
|
81
|
-
const closureKey = (closure) => closure ?? "";
|
|
82
|
-
/**
|
|
83
|
-
* Snapshot one target into the active transaction context, deduplicating on
|
|
84
|
-
* first touch per closure. Shared by the registration primitives below and by
|
|
85
|
-
* the transaction runner claiming its declared targets.
|
|
86
|
-
*/
|
|
87
|
-
export const protectInContext = (context, target, closure) => context.snapshotSemaphore.withPermits(1)(Effect.gen(function* () {
|
|
88
|
-
const { fs, path } = context;
|
|
89
|
-
const normalized = path.resolve(target);
|
|
90
|
-
// First-touch dedupe is per closure: a later closure touching a target
|
|
91
|
-
// an earlier closure already committed needs its own — post-commit —
|
|
92
|
-
// preimage, so restoring it undoes only that closure's work.
|
|
93
|
-
const key = closureKey(closure);
|
|
94
|
-
const protectedForClosure = context.protectedTargets.get(key) ?? new Set();
|
|
95
|
-
if (protectedForClosure.has(normalized))
|
|
96
|
-
return;
|
|
97
|
-
const link = yield* fs.readLink(normalized).pipe(Effect.option);
|
|
98
|
-
let snapshot;
|
|
99
|
-
if (Option.isSome(link)) {
|
|
100
|
-
snapshot = { closure, target: normalized, state: "symlink", linkTarget: link.value };
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
const exists = yield* fs
|
|
104
|
-
.exists(normalized)
|
|
105
|
-
.pipe(Effect.mapError((cause) => new WorkspaceSnapshotError({ target: normalized, step: "inspect-target", cause })));
|
|
106
|
-
if (!exists) {
|
|
107
|
-
snapshot = { closure, target: normalized, state: "absent" };
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
if (context.snapshotStore.dir === undefined) {
|
|
111
|
-
context.snapshotStore.dir = yield* fs
|
|
112
|
-
.makeTempDirectory({ prefix: "axm-rollback-" })
|
|
113
|
-
.pipe(Effect.mapError((cause) => new WorkspaceSnapshotError({ target: normalized, step: "create-store", cause })));
|
|
114
|
-
}
|
|
115
|
-
const backup = path.join(context.snapshotStore.dir, `${context.snapshotSequence.value++}.snap`);
|
|
116
|
-
// The pre-change bytes are preserved before the path is first
|
|
117
|
-
// mutated; a path that cannot be snapshotted is never mutated.
|
|
118
|
-
yield* fs
|
|
119
|
-
.copy(normalized, backup, { preserveTimestamps: true })
|
|
120
|
-
.pipe(Effect.mapError((cause) => new WorkspaceSnapshotError({ target: normalized, step: "copy", cause })));
|
|
121
|
-
snapshot = { closure, target: normalized, state: "copied", backup };
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
protectedForClosure.add(normalized);
|
|
125
|
-
context.protectedTargets.set(key, protectedForClosure);
|
|
126
|
-
context.snapshots.push(snapshot);
|
|
127
|
-
}));
|
|
128
|
-
/** Snapshot a path before its first mutation when a workspace transaction is active. */
|
|
129
|
-
export const protectWorkspacePath = (target) => Effect.gen(function* () {
|
|
130
|
-
const current = yield* CurrentWorkspaceTransaction;
|
|
131
|
-
if (Option.isNone(current))
|
|
132
|
-
return;
|
|
133
|
-
const closure = yield* CurrentWorkspaceClosure;
|
|
134
|
-
yield* protectInContext(current.value, target, closure);
|
|
135
|
-
});
|
|
136
|
-
/**
|
|
137
|
-
* Closure rollbacks that could not complete and verify, with the snapshot
|
|
138
|
-
* store that still preserves their pre-change bytes. Read at the end of a
|
|
139
|
-
* plan apply so the terminal resolution derives retained state from the
|
|
140
|
-
* in-memory facts alone. `None` outside a transaction.
|
|
141
|
-
*/
|
|
142
|
-
export const readPendingClosureRestorationFailures = CurrentWorkspaceTransaction.pipe(Effect.map(Option.map((context) => ({
|
|
143
|
-
failures: [...context.pendingRestorationFailures],
|
|
144
|
-
snapshotDir: context.snapshotStore.dir,
|
|
145
|
-
}))));
|
|
146
|
-
/**
|
|
147
|
-
* Protect the first ancestor a recursive directory creation is about to
|
|
148
|
-
* create, so restoration removes the created directory chain instead of
|
|
149
|
-
* leaving empty parents behind. No-op outside a transaction or when the
|
|
150
|
-
* directory already exists.
|
|
151
|
-
*/
|
|
152
|
-
export const protectCreatedAncestors = (fs, path, directory) => CurrentWorkspaceTransaction.pipe(Effect.flatMap(Option.match({
|
|
153
|
-
onNone: () => Effect.void,
|
|
154
|
-
onSome: (context) => Effect.gen(function* () {
|
|
155
|
-
let firstMissing;
|
|
156
|
-
let current = path.resolve(directory);
|
|
157
|
-
while (true) {
|
|
158
|
-
const exists = yield* fs.exists(current).pipe(Effect.mapError((cause) => new WorkspaceSnapshotError({
|
|
159
|
-
target: current,
|
|
160
|
-
step: "inspect-ancestor",
|
|
161
|
-
cause,
|
|
162
|
-
})));
|
|
163
|
-
if (exists)
|
|
164
|
-
break;
|
|
165
|
-
firstMissing = current;
|
|
166
|
-
const parent = path.dirname(current);
|
|
167
|
-
if (parent === current)
|
|
168
|
-
break;
|
|
169
|
-
current = parent;
|
|
170
|
-
}
|
|
171
|
-
if (firstMissing !== undefined) {
|
|
172
|
-
const closure = yield* CurrentWorkspaceClosure;
|
|
173
|
-
yield* protectInContext(context, firstMissing, closure);
|
|
174
|
-
}
|
|
175
|
-
}),
|
|
176
|
-
})));
|
|
177
|
-
//# sourceMappingURL=transaction.js.map
|