@openclaw/fs-safe 0.14.0 → 0.15.0
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 +50 -0
- package/README.md +10 -4
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -1
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +113 -46
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +4 -4
- package/dist/archive-zip-directory.d.ts +4 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +2 -0
- package/dist/archive-zip-entry.d.ts +6 -2
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-entry.js +23 -8
- package/dist/archive-zip-integrity.d.ts.map +1 -1
- package/dist/archive-zip-integrity.js +3 -4
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +107 -31
- package/dist/archive-zip-names.d.ts +1 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +6 -0
- package/dist/archive.js +6 -5
- package/dist/bounded-read-stream.d.ts +0 -1
- package/dist/bounded-read-stream.d.ts.map +1 -1
- package/dist/bounded-read-stream.js +0 -6
- package/dist/copy-publication.d.ts +6 -0
- package/dist/copy-publication.d.ts.map +1 -1
- package/dist/copy-publication.js +3 -0
- package/dist/copy-tree-portable.d.ts.map +1 -1
- package/dist/copy-tree-portable.js +44 -24
- package/dist/copy.d.ts.map +1 -1
- package/dist/copy.js +29 -11
- package/dist/directory-mode-owner.js +5 -5
- package/dist/file-handle-transfer.d.ts +2 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +57 -2
- package/dist/file-identity.d.ts.map +1 -1
- package/dist/file-identity.js +18 -4
- package/dist/file-lock-sync-admission.d.ts +19 -0
- package/dist/file-lock-sync-admission.d.ts.map +1 -0
- package/dist/file-lock-sync-admission.js +93 -0
- package/dist/file-lock-sync-root-acquire.d.ts +4 -0
- package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
- package/dist/file-lock-sync-root-acquire.js +370 -0
- package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
- package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
- package/dist/file-lock-sync-root-arbitration.js +66 -0
- package/dist/file-lock-sync-root-held.d.ts +34 -0
- package/dist/file-lock-sync-root-held.d.ts.map +1 -0
- package/dist/file-lock-sync-root-held.js +393 -0
- package/dist/file-lock-sync-root-io.d.ts +44 -0
- package/dist/file-lock-sync-root-io.d.ts.map +1 -0
- package/dist/file-lock-sync-root-io.js +209 -0
- package/dist/file-lock-sync-root-mutation.d.ts +17 -0
- package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
- package/dist/file-lock-sync-root-mutation.js +277 -0
- package/dist/file-lock-sync-root-options.d.ts +20 -0
- package/dist/file-lock-sync-root-options.d.ts.map +1 -0
- package/dist/file-lock-sync-root-options.js +58 -0
- package/dist/file-lock-sync-root-registration.d.ts +2 -0
- package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
- package/dist/file-lock-sync-root-registration.js +90 -0
- package/dist/file-lock-sync-root.d.ts +36 -0
- package/dist/file-lock-sync-root.d.ts.map +1 -0
- package/dist/file-lock-sync-root.js +361 -0
- package/dist/file-lock-sync-stale-admission.d.ts +24 -0
- package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
- package/dist/file-lock-sync-stale-admission.js +205 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +245 -205
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +5 -1
- package/dist/file-store-sync-write.js +3 -3
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +47 -12
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +22 -15
- package/dist/json-durable-queue-ownership.d.ts +0 -1
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +0 -6
- package/dist/native-binding.d.ts +2 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-parent-admission.d.ts +3 -2
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +24 -5
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +7 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +7 -11
- package/dist/native-policy-parent-windows.d.ts +14 -0
- package/dist/native-policy-parent-windows.d.ts.map +1 -0
- package/dist/native-policy-parent-windows.js +200 -0
- package/dist/native-rename-outcome.d.ts +4 -0
- package/dist/native-rename-outcome.d.ts.map +1 -0
- package/dist/native-rename-outcome.js +8 -0
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +42 -40
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +12 -8
- package/dist/path-prefix.d.ts.map +1 -1
- package/dist/path-prefix.js +30 -8
- package/dist/path-suffix-aliases.d.ts +2 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -1
- package/dist/path-suffix-aliases.js +25 -17
- package/dist/permission-exec.d.ts +2 -0
- package/dist/permission-exec.d.ts.map +1 -1
- package/dist/permission-exec.js +150 -21
- package/dist/permissions-windows.js +1 -1
- package/dist/pinned-mutation-admission.d.ts.map +1 -1
- package/dist/pinned-mutation-admission.js +10 -5
- package/dist/pinned-mutation-observation.d.ts +0 -1
- package/dist/pinned-mutation-observation.d.ts.map +1 -1
- package/dist/pinned-mutation-observation.js +0 -19
- package/dist/pinned-mutation-shared-route.d.ts +1 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
- package/dist/pinned-mutation-shared-route.js +1 -1
- package/dist/pinned-write-types.d.ts +2 -0
- package/dist/pinned-write-types.d.ts.map +1 -1
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +75 -121
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +1 -15
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +256 -18
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +62 -70
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -12
- package/dist/replace-file-temp-owner.d.ts +5 -2
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +65 -30
- package/dist/replace-file.js +6 -6
- package/dist/retained-directory-replacement.d.ts +26 -0
- package/dist/retained-directory-replacement.d.ts.map +1 -0
- package/dist/retained-directory-replacement.js +193 -0
- package/dist/root-boundary.d.ts +1 -0
- package/dist/root-boundary.d.ts.map +1 -1
- package/dist/root-boundary.js +4 -0
- package/dist/root-context.d.ts +0 -8
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +0 -3
- package/dist/root-create-input.d.ts +6 -0
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +5 -1
- package/dist/root-directory-list.d.ts +1 -0
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +18 -10
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +2 -2
- package/dist/root-path-errors.d.ts +1 -0
- package/dist/root-path-errors.d.ts.map +1 -1
- package/dist/root-path-errors.js +11 -2
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +11 -35
- package/dist/root-path.js +1 -13
- package/dist/root-remove.d.ts +1 -0
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +4 -0
- package/dist/root-walk.d.ts +1 -1
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +17 -2
- package/dist/root-write-admission.d.ts +0 -2
- package/dist/root-write-admission.d.ts.map +1 -1
- package/dist/root-write-admission.js +1 -15
- package/dist/root-write-complete-parent.d.ts.map +1 -1
- package/dist/root-write-complete-parent.js +7 -23
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +29 -42
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +2 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +3 -24
- package/dist/secret-read-policy.d.ts +6 -2
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +26 -2
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +23 -12
- package/dist/sidecar-lock-acquire.d.ts +2 -28
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +288 -199
- package/dist/sidecar-lock-admission-context.d.ts +19 -0
- package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
- package/dist/sidecar-lock-admission-context.js +60 -0
- package/dist/sidecar-lock-admission-parser.d.ts +43 -0
- package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
- package/dist/sidecar-lock-admission-parser.js +113 -0
- package/dist/sidecar-lock-admission.d.ts +35 -0
- package/dist/sidecar-lock-admission.d.ts.map +1 -0
- package/dist/sidecar-lock-admission.js +7 -0
- package/dist/sidecar-lock-reclaim.d.ts +9 -4
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +80 -25
- package/dist/sidecar-lock-stale-admission.d.ts +39 -0
- package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
- package/dist/sidecar-lock-stale-admission.js +232 -0
- package/dist/sidecar-lock-target.d.ts +8 -0
- package/dist/sidecar-lock-target.d.ts.map +1 -0
- package/dist/sidecar-lock-target.js +55 -0
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +100 -16
- package/dist/temp-workspace-descriptor.d.ts.map +1 -1
- package/dist/temp-workspace-descriptor.js +9 -27
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +8 -8
- package/dist/walk.d.ts +5 -1
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +19 -6
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +2 -2
- package/docs/advanced.md +1 -1
- package/docs/archive.md +36 -9
- package/docs/atomic.md +85 -8
- package/docs/copy.md +35 -0
- package/docs/file-store.md +19 -0
- package/docs/json-store.md +5 -0
- package/docs/native-helper.md +10 -3
- package/docs/output.md +6 -0
- package/docs/path-prefix.md +10 -0
- package/docs/path-suffix-aliases.md +51 -6
- package/docs/permissions.md +13 -0
- package/docs/public-api.md +3 -2
- package/docs/root.md +22 -3
- package/docs/sidecar-lock.md +109 -4
- package/docs/staged-file.md +7 -3
- package/docs/temp.md +20 -3
- package/docs/walk.md +67 -1
- package/docs/writing.md +5 -1
- package/package.json +10 -10
- package/dist/darwin-acl.d.ts +0 -4
- package/dist/darwin-acl.d.ts.map +0 -1
- package/dist/darwin-acl.js +0 -24
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { defaultSidecarLockShouldReclaim, isTransientLockFileDenial } from "./sidecar-lock-policy.js";
|
|
2
|
+
import { readSidecarLockRawSnapshot, removeStaleSidecarLockIfAllowed, sidecarLockSnapshotStillPresent, tryAcquireSidecarReclaimGuard, } from "./sidecar-lock-reclaim.js";
|
|
3
|
+
import { observeSidecarLockParser, parseSidecarLockSnapshotUnderAdmission, scopedSidecarLockParser, } from "./sidecar-lock-admission-parser.js";
|
|
4
|
+
/** Handle one historical EEXIST attempt while its canonical admission is held. */
|
|
5
|
+
export async function handleStaleSidecarAdmission(params) {
|
|
6
|
+
const { admission, lockPath, normalizedTargetPath, options } = params;
|
|
7
|
+
const holderChanged = {};
|
|
8
|
+
const assertUnheld = () => {
|
|
9
|
+
params.assertToken();
|
|
10
|
+
if (params.currentHeld())
|
|
11
|
+
throw holderChanged;
|
|
12
|
+
};
|
|
13
|
+
const retryHolderChange = async (error) => {
|
|
14
|
+
if (error !== holderChanged)
|
|
15
|
+
return false;
|
|
16
|
+
await params.waitForRetry();
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
const retryChangedBoundary = async () => {
|
|
20
|
+
try {
|
|
21
|
+
assertUnheld();
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (await retryHolderChange(error))
|
|
26
|
+
return true;
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
let rawSnapshot;
|
|
31
|
+
let lockFileOpenDenied = false;
|
|
32
|
+
try {
|
|
33
|
+
rawSnapshot = await admission.run(async () => await readSidecarLockRawSnapshot(lockPath, {
|
|
34
|
+
lockRoot: params.lockRoot,
|
|
35
|
+
rejectNonFile: true,
|
|
36
|
+
discardObservation: "changed",
|
|
37
|
+
onOpenFailure: (error) => {
|
|
38
|
+
lockFileOpenDenied = isTransientLockFileDenial(error, lockPath);
|
|
39
|
+
},
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (await retryChangedBoundary())
|
|
44
|
+
return;
|
|
45
|
+
if (!lockFileOpenDenied || !params.withinDenialBudget())
|
|
46
|
+
throw error;
|
|
47
|
+
await params.retryOrRethrowDenial(error);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
params.assertToken();
|
|
51
|
+
if (params.currentHeld()) {
|
|
52
|
+
await params.waitForRetry();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let snapshot;
|
|
56
|
+
try {
|
|
57
|
+
observeSidecarLockParser(params.parserState, admission, () => options.parsePayload, assertUnheld);
|
|
58
|
+
snapshot = parseSidecarLockSnapshotUnderAdmission(rawSnapshot, params.parserState, admission, assertUnheld);
|
|
59
|
+
assertUnheld();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (await retryHolderChange(error))
|
|
63
|
+
return;
|
|
64
|
+
if (await retryChangedBoundary())
|
|
65
|
+
return;
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
if (!snapshot) {
|
|
69
|
+
await params.waitForRetry();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const staleOptions = params.staleOptionsState;
|
|
73
|
+
try {
|
|
74
|
+
if (!staleOptions.shouldReclaimObserved) {
|
|
75
|
+
assertUnheld();
|
|
76
|
+
staleOptions.shouldReclaim = admission.run(() => options.shouldReclaim);
|
|
77
|
+
staleOptions.shouldReclaimObserved = true;
|
|
78
|
+
assertUnheld();
|
|
79
|
+
}
|
|
80
|
+
if (!staleOptions.shouldRemoveObserved) {
|
|
81
|
+
assertUnheld();
|
|
82
|
+
staleOptions.shouldRemove = admission.run(() => options.shouldRemoveStaleLock);
|
|
83
|
+
staleOptions.shouldRemoveObserved = true;
|
|
84
|
+
assertUnheld();
|
|
85
|
+
}
|
|
86
|
+
if (!staleOptions.staleRecoveryObserved) {
|
|
87
|
+
assertUnheld();
|
|
88
|
+
staleOptions.staleRecovery = admission.run(() => options.staleRecovery);
|
|
89
|
+
staleOptions.staleRecoveryObserved = true;
|
|
90
|
+
assertUnheld();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (await retryHolderChange(error))
|
|
95
|
+
return;
|
|
96
|
+
if (await retryChangedBoundary())
|
|
97
|
+
return;
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
const shouldReclaim = staleOptions.shouldReclaim ?? defaultSidecarLockShouldReclaim;
|
|
101
|
+
let reclaim;
|
|
102
|
+
try {
|
|
103
|
+
reclaim = await admission.run(async () => {
|
|
104
|
+
assertUnheld();
|
|
105
|
+
try {
|
|
106
|
+
return await Reflect.apply(shouldReclaim, undefined, [{
|
|
107
|
+
lockPath,
|
|
108
|
+
normalizedTargetPath,
|
|
109
|
+
payload: snapshot.payload ?? null,
|
|
110
|
+
staleMs: params.staleMs,
|
|
111
|
+
nowMs: Date.now(),
|
|
112
|
+
heldByThisProcess: false,
|
|
113
|
+
}]);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
assertUnheld();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
assertUnheld();
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (await retryHolderChange(error))
|
|
123
|
+
return;
|
|
124
|
+
if (await retryChangedBoundary())
|
|
125
|
+
return;
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
if (!reclaim) {
|
|
129
|
+
await params.waitForRetry();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
let stillPresent;
|
|
133
|
+
try {
|
|
134
|
+
stillPresent = await sidecarLockSnapshotStillPresent(lockPath, snapshot, {
|
|
135
|
+
lockRoot: params.lockRoot,
|
|
136
|
+
parsePayload: scopedSidecarLockParser(params.parserState, admission, assertUnheld),
|
|
137
|
+
});
|
|
138
|
+
assertUnheld();
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (await retryHolderChange(error))
|
|
142
|
+
return;
|
|
143
|
+
if (await retryChangedBoundary())
|
|
144
|
+
return;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
if (!stillPresent) {
|
|
148
|
+
await params.waitForRetry();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if ((staleOptions.staleRecovery ?? "fail-closed") === "remove-if-unchanged") {
|
|
152
|
+
const acquiredGuard = await admission.run(() => tryAcquireSidecarReclaimGuard(params.reclaimGuards, params.reclaimGuardPath, params.lockRoot));
|
|
153
|
+
if (acquiredGuard)
|
|
154
|
+
params.setReclaimGuard(acquiredGuard);
|
|
155
|
+
params.assertToken();
|
|
156
|
+
if (params.currentHeld()) {
|
|
157
|
+
if (acquiredGuard) {
|
|
158
|
+
await params.releaseReclaimGuard();
|
|
159
|
+
}
|
|
160
|
+
await params.waitForRetry();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (!acquiredGuard) {
|
|
164
|
+
await params.waitForRetry();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const shouldRemove = staleOptions.shouldRemove;
|
|
168
|
+
const assertRemovalAuthorized = assertUnheld;
|
|
169
|
+
const guardedShouldRemove = shouldRemove && function (staleSnapshot) {
|
|
170
|
+
return admission.run(async () => {
|
|
171
|
+
assertRemovalAuthorized();
|
|
172
|
+
try {
|
|
173
|
+
return await Reflect.apply(shouldRemove, this, [staleSnapshot]);
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
assertRemovalAuthorized();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
let removal;
|
|
181
|
+
try {
|
|
182
|
+
removal = await removeStaleSidecarLockIfAllowed({
|
|
183
|
+
lockPath,
|
|
184
|
+
normalizedTargetPath,
|
|
185
|
+
snapshot,
|
|
186
|
+
assertGuardHeld: acquiredGuard.assertHeld && (() => admission.run(async () => {
|
|
187
|
+
assertUnheld();
|
|
188
|
+
await acquiredGuard.assertHeld();
|
|
189
|
+
assertUnheld();
|
|
190
|
+
})),
|
|
191
|
+
shouldRemoveStaleLock: guardedShouldRemove,
|
|
192
|
+
lockRoot: params.lockRoot,
|
|
193
|
+
parsePayload: scopedSidecarLockParser(params.parserState, admission, assertRemovalAuthorized),
|
|
194
|
+
assertAuthorized: assertRemovalAuthorized,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (error !== holderChanged) {
|
|
199
|
+
try {
|
|
200
|
+
assertRemovalAuthorized();
|
|
201
|
+
}
|
|
202
|
+
catch (boundaryError) {
|
|
203
|
+
if (boundaryError !== holderChanged)
|
|
204
|
+
throw boundaryError;
|
|
205
|
+
}
|
|
206
|
+
if (!params.currentHeld())
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
await params.releaseReclaimGuard();
|
|
210
|
+
await params.waitForRetry();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
params.assertToken();
|
|
214
|
+
if (params.currentHeld()) {
|
|
215
|
+
await params.releaseReclaimGuard();
|
|
216
|
+
await params.waitForRetry();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (removal === "removed")
|
|
220
|
+
return;
|
|
221
|
+
if (removal === "changed") {
|
|
222
|
+
await params.waitForRetry();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
await params.releaseReclaimGuard();
|
|
226
|
+
}
|
|
227
|
+
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
228
|
+
code: "file_lock_stale",
|
|
229
|
+
lockPath,
|
|
230
|
+
normalizedTargetPath,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Root } from "./root-impl.js";
|
|
2
|
+
export declare function isCwdIndependentAbsolutePath(filePath: string): boolean;
|
|
3
|
+
export declare function resolveSidecarTargetPath(resolved: string, lockRoot?: Root): Promise<string>;
|
|
4
|
+
export declare function resolveSidecarLockPaths(targetPath: string, explicitLockPath: string | undefined, lockRoot: Root | undefined): Promise<{
|
|
5
|
+
lockPath: string;
|
|
6
|
+
normalizedTargetPath: string;
|
|
7
|
+
}>;
|
|
8
|
+
//# sourceMappingURL=sidecar-lock-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar-lock-target.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-target.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAG3C,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAItE;AAED,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBjG;AAED,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,QAAQ,EAAE,IAAI,GAAG,SAAS,GACzB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB7D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { canonicalPathFromExistingAncestor } from "./absolute-path.js";
|
|
4
|
+
import { realpathSync } from "./realpath.js";
|
|
5
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
6
|
+
import { anchorWindowsDriveRelativePath, assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
7
|
+
export function isCwdIndependentAbsolutePath(filePath) {
|
|
8
|
+
if (!path.isAbsolute(filePath))
|
|
9
|
+
return false;
|
|
10
|
+
// A leading separator on Windows is rooted on the process's current drive.
|
|
11
|
+
return process.platform !== "win32" || path.parse(filePath).root.length > 1;
|
|
12
|
+
}
|
|
13
|
+
export async function resolveSidecarTargetPath(resolved, lockRoot) {
|
|
14
|
+
assertNoWindowsPathAlias(resolved);
|
|
15
|
+
const dir = path.dirname(resolved);
|
|
16
|
+
if (lockRoot) {
|
|
17
|
+
// The target is an arbitration key, not necessarily inside the lock Root.
|
|
18
|
+
await lockRoot.resolve(".");
|
|
19
|
+
const parent = await canonicalPathFromExistingAncestor(dir);
|
|
20
|
+
await lockRoot.resolve(".");
|
|
21
|
+
assertNoWindowsPathAlias(parent);
|
|
22
|
+
const normalized = path.join(parent, path.basename(resolved));
|
|
23
|
+
assertNoWindowsPathAlias(normalized);
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
await fs.mkdir(recursiveMkdirPath(dir), { recursive: true });
|
|
27
|
+
let parent;
|
|
28
|
+
try {
|
|
29
|
+
parent = realpathSync.native(dir);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
assertNoWindowsPathAlias(parent);
|
|
35
|
+
const normalized = path.join(parent, path.basename(resolved));
|
|
36
|
+
assertNoWindowsPathAlias(normalized);
|
|
37
|
+
return normalized;
|
|
38
|
+
}
|
|
39
|
+
export async function resolveSidecarLockPaths(targetPath, explicitLockPath, lockRoot) {
|
|
40
|
+
const anchoredTargetPath = anchorWindowsDriveRelativePath(targetPath);
|
|
41
|
+
assertNoWindowsPathAlias(anchoredTargetPath);
|
|
42
|
+
const resolvedTargetPath = path.resolve(anchoredTargetPath);
|
|
43
|
+
let resolvedLockPath;
|
|
44
|
+
if (explicitLockPath !== undefined) {
|
|
45
|
+
const anchoredLockPath = anchorWindowsDriveRelativePath(explicitLockPath);
|
|
46
|
+
assertNoWindowsPathAlias(anchoredLockPath);
|
|
47
|
+
resolvedLockPath = isCwdIndependentAbsolutePath(explicitLockPath)
|
|
48
|
+
? explicitLockPath
|
|
49
|
+
: path.resolve(anchoredLockPath);
|
|
50
|
+
}
|
|
51
|
+
const normalizedTargetPath = await resolveSidecarTargetPath(resolvedTargetPath, lockRoot);
|
|
52
|
+
const lockPath = resolvedLockPath ?? `${normalizedTargetPath}.lock`;
|
|
53
|
+
assertNoWindowsPathAlias(lockPath);
|
|
54
|
+
return { lockPath, normalizedTargetPath };
|
|
55
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AA2KjC,0FAA0F;AAC1F,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AA8HD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM;cAuB3B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACpD,yBAAyB,CAAC,QAAQ,CAAC,KAC3C,OAAO,CAAC,iBAAiB,CAAC;eAaL,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACxD,yBAAyB,CAAC,QAAQ,CAAC,MACxC,MAAM,OAAO,CAAC,CAAC,CAAC,KACnB,OAAO,CAAC,CAAC,CAAC;iBAqBW,OAAO,CAAC,IAAI,CAAC;iBAQnB,IAAI;uBAIE,oBAAoB,EAAE;EAW/C;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAMZ"}
|
package/dist/sidecar-lock.js
CHANGED
|
@@ -8,7 +8,10 @@ import { createSuppressedError } from "./suppressed-error.js";
|
|
|
8
8
|
const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
|
|
9
9
|
const GLOBAL_CLEANUP_KEY = Symbol.for("fsSafe.sidecarLockCleanupRegistered");
|
|
10
10
|
const GLOBAL_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.sidecarLockCleanupHandler");
|
|
11
|
+
const GLOBAL_CLEANUP_REGISTRATION_KEY = Symbol.for("fsSafe.sidecarLockCleanupRegistration");
|
|
11
12
|
const GLOBAL_BEFORE_EXIT_KEY = Symbol.for("fsSafe.sidecarLockBeforeExitCleanup");
|
|
13
|
+
const GLOBAL_BEFORE_EXIT_HANDLER_KEY = Symbol.for("fsSafe.sidecarLockBeforeExitCleanupHandler");
|
|
14
|
+
const GLOBAL_BEFORE_EXIT_REGISTRATION_KEY = Symbol.for("fsSafe.sidecarLockBeforeExitCleanupRegistration");
|
|
12
15
|
// Set by copies whose exit handlers honor retainOnExit. When an older package
|
|
13
16
|
// copy registered the handlers first, this marker stays absent and retained
|
|
14
17
|
// acquisitions must fail closed rather than silently lose the guarantee.
|
|
@@ -27,6 +30,7 @@ function resolveManagerState(key) {
|
|
|
27
30
|
state = {
|
|
28
31
|
cleanupRegistered: false,
|
|
29
32
|
held: new Map(),
|
|
33
|
+
admissions: new Map(),
|
|
30
34
|
reclaimCleanupRegistered: false,
|
|
31
35
|
reclaimGuards: new Set(),
|
|
32
36
|
};
|
|
@@ -34,7 +38,9 @@ function resolveManagerState(key) {
|
|
|
34
38
|
}
|
|
35
39
|
else {
|
|
36
40
|
// The global manager symbol is shared across package copies and hot reloads.
|
|
37
|
-
// Backfill state created by fs-safe versions that predate
|
|
41
|
+
// Backfill state created by fs-safe versions that predate admission tokens
|
|
42
|
+
// or reclaim guards without placing an incomplete value in the held map.
|
|
43
|
+
state.admissions ??= new Map();
|
|
38
44
|
state.reclaimCleanupRegistered ??= false;
|
|
39
45
|
state.reclaimGuards ??= new Set();
|
|
40
46
|
}
|
|
@@ -107,21 +113,55 @@ function releaseAllLocksSync(state, options) {
|
|
|
107
113
|
}
|
|
108
114
|
state.held.delete(normalizedTargetPath);
|
|
109
115
|
}
|
|
116
|
+
state.admissions?.clear();
|
|
110
117
|
releaseAllReclaimGuardsSync(state);
|
|
111
118
|
}
|
|
112
119
|
function ensureGlobalExitCleanupRegistered() {
|
|
113
120
|
const globalWithCleanup = globalThis;
|
|
114
121
|
if (globalWithCleanup[GLOBAL_CLEANUP_KEY])
|
|
115
122
|
return;
|
|
116
|
-
globalWithCleanup[
|
|
117
|
-
|
|
123
|
+
if (globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY]) {
|
|
124
|
+
throw new FsSafeError("helper-unavailable", "sidecar lock exit cleanup registration is already in progress");
|
|
125
|
+
}
|
|
126
|
+
const registration = {};
|
|
118
127
|
const cleanup = () => {
|
|
119
128
|
for (const state of getGlobalManagers().values()) {
|
|
120
129
|
releaseAllLocksSync(state, { preserveRetained: true });
|
|
121
130
|
}
|
|
122
131
|
};
|
|
123
|
-
globalWithCleanup[
|
|
124
|
-
|
|
132
|
+
globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY] = registration;
|
|
133
|
+
try {
|
|
134
|
+
process.on("exit", cleanup);
|
|
135
|
+
globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] = cleanup;
|
|
136
|
+
globalWithCleanup[GLOBAL_RETAIN_AWARE_KEY] = true;
|
|
137
|
+
globalWithCleanup[GLOBAL_CLEANUP_KEY] = true;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (process.listeners("exit").includes(cleanup)) {
|
|
141
|
+
try {
|
|
142
|
+
process.off("exit", cleanup);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Reconcile the marker with the actual listener below.
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (process.listeners("exit").includes(cleanup)) {
|
|
149
|
+
globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] = cleanup;
|
|
150
|
+
globalWithCleanup[GLOBAL_RETAIN_AWARE_KEY] = true;
|
|
151
|
+
globalWithCleanup[GLOBAL_CLEANUP_KEY] = true;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
if (globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] === cleanup) {
|
|
155
|
+
delete globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
if (globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY] === registration) {
|
|
162
|
+
delete globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
125
165
|
}
|
|
126
166
|
/** True when a retain-unaware package copy registered the process-exit handlers first. */
|
|
127
167
|
export function exitCleanupCannotRetain() {
|
|
@@ -133,10 +173,13 @@ function ensureGlobalBeforeExitCleanupRegistered() {
|
|
|
133
173
|
if (globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY]) {
|
|
134
174
|
return globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY];
|
|
135
175
|
}
|
|
176
|
+
if (globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY]) {
|
|
177
|
+
throw new FsSafeError("helper-unavailable", "sidecar lock before-exit cleanup registration is already in progress");
|
|
178
|
+
}
|
|
136
179
|
// Independent of the exit registration, which an older package copy may own.
|
|
137
180
|
const lifecycle = { armed: false };
|
|
138
|
-
|
|
139
|
-
|
|
181
|
+
const registration = {};
|
|
182
|
+
const cleanup = () => {
|
|
140
183
|
if (!lifecycle.armed)
|
|
141
184
|
return;
|
|
142
185
|
// Cleanup itself schedules I/O; retry only after another acquisition.
|
|
@@ -148,8 +191,42 @@ function ensureGlobalBeforeExitCleanupRegistered() {
|
|
|
148
191
|
}
|
|
149
192
|
}
|
|
150
193
|
}
|
|
151
|
-
}
|
|
152
|
-
|
|
194
|
+
};
|
|
195
|
+
globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY] = registration;
|
|
196
|
+
try {
|
|
197
|
+
process.on("beforeExit", cleanup);
|
|
198
|
+
globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY] = cleanup;
|
|
199
|
+
globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] = lifecycle;
|
|
200
|
+
return lifecycle;
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (process.listeners("beforeExit").includes(cleanup)) {
|
|
204
|
+
try {
|
|
205
|
+
process.off("beforeExit", cleanup);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Reconcile the marker with the actual listener below.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (process.listeners("beforeExit").includes(cleanup)) {
|
|
212
|
+
globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY] = cleanup;
|
|
213
|
+
globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] = lifecycle;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
if (globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY] === cleanup) {
|
|
217
|
+
delete globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY];
|
|
218
|
+
}
|
|
219
|
+
if (globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] === lifecycle) {
|
|
220
|
+
delete globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
if (globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY] === registration) {
|
|
227
|
+
delete globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
153
230
|
}
|
|
154
231
|
async function releaseHeldLock(state, normalizedTargetPath, held, options = {}) {
|
|
155
232
|
const current = state.held.get(normalizedTargetPath);
|
|
@@ -194,7 +271,6 @@ async function releaseHeldLock(state, normalizedTargetPath, held, options = {})
|
|
|
194
271
|
}
|
|
195
272
|
}
|
|
196
273
|
function handleForHeldLock(state, normalizedTargetPath, held) {
|
|
197
|
-
ensureGlobalBeforeExitCleanupRegistered().armed = true;
|
|
198
274
|
return createHeldSidecarLockHandle({
|
|
199
275
|
normalizedTargetPath,
|
|
200
276
|
held,
|
|
@@ -204,21 +280,29 @@ function handleForHeldLock(state, normalizedTargetPath, held) {
|
|
|
204
280
|
export function createSidecarLockManager(key) {
|
|
205
281
|
const state = resolveManagerState(key);
|
|
206
282
|
function ensureExitCleanupRegistered() {
|
|
283
|
+
ensureGlobalExitCleanupRegistered();
|
|
284
|
+
const lifecycle = ensureGlobalBeforeExitCleanupRegistered();
|
|
207
285
|
state.cleanupRegistered = true;
|
|
208
286
|
state.reclaimCleanupRegistered = true;
|
|
209
|
-
|
|
210
|
-
|
|
287
|
+
return lifecycle;
|
|
288
|
+
}
|
|
289
|
+
function armExitCleanup(lifecycle) {
|
|
290
|
+
lifecycle.armed = true;
|
|
291
|
+
}
|
|
292
|
+
function assertRetainOnExitSupported(retainOnExit) {
|
|
293
|
+
if (retainOnExit !== true || !exitCleanupCannotRetain())
|
|
294
|
+
return;
|
|
295
|
+
throw new FsSafeError("helper-unavailable", "retainOnExit requires this process's exit handlers to be retain-aware; an older package copy registered them first");
|
|
211
296
|
}
|
|
212
297
|
async function acquire(options) {
|
|
213
|
-
if (options.retainOnExit === true && exitCleanupCannotRetain()) {
|
|
214
|
-
throw new FsSafeError("helper-unavailable", "retainOnExit requires this process's exit handlers to be retain-aware; an older package copy registered them first");
|
|
215
|
-
}
|
|
216
298
|
return await acquireSidecarLock(options, {
|
|
217
299
|
held: state.held,
|
|
300
|
+
admissions: state.admissions,
|
|
218
301
|
reclaimGuards: state.reclaimGuards,
|
|
219
302
|
ensureExitCleanupRegistered,
|
|
303
|
+
armExitCleanup,
|
|
304
|
+
assertRetainOnExitSupported,
|
|
220
305
|
handleForHeldLock: (normalizedTargetPath, held) => handleForHeldLock(state, normalizedTargetPath, held),
|
|
221
|
-
releaseHeldLock: async (normalizedTargetPath, held, releaseOptions) => await releaseHeldLock(state, normalizedTargetPath, held, releaseOptions),
|
|
222
306
|
});
|
|
223
307
|
}
|
|
224
308
|
async function withLock(options, fn) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-workspace-descriptor.d.ts","sourceRoot":"","sources":["../src/temp-workspace-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAU7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAMhF,OAAO,EAML,KAAK,yBAAyB,EAC/B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,yBAAyB,GAAG,MAAM,GAAG,QAAQ,CAAC;AASnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,EAAE,QAAQ,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAClD,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAmDpD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,0BAA0B,GACpC,iBAAiB,CA8BnB;AAED,+EAA+E;AAC/E,qBAAa,0BAA0B;;IAWrC,OAAO,eAqBN;IAED,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,0BAA0B,CAcjF;IAED,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;QACjC,QAAQ,EAAE,0BAA0B,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC;KAC1B,CAoBA;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAUD,iBAAiB,CACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,GACX,yBAAyB,CAsB3B;
|
|
1
|
+
{"version":3,"file":"temp-workspace-descriptor.d.ts","sourceRoot":"","sources":["../src/temp-workspace-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAU7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAMhF,OAAO,EAML,KAAK,yBAAyB,EAC/B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,yBAAyB,GAAG,MAAM,GAAG,QAAQ,CAAC;AASnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,EAAE,QAAQ,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAClD,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAmDpD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,0BAA0B,GACpC,iBAAiB,CA8BnB;AAED,+EAA+E;AAC/E,qBAAa,0BAA0B;;IAWrC,OAAO,eAqBN;IAED,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,0BAA0B,CAcjF;IAED,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;QACjC,QAAQ,EAAE,0BAA0B,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC;KAC1B,CAoBA;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAUD,iBAAiB,CACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,GACX,yBAAyB,CAsB3B;IAgEK,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,IAAI,GACvB,OAAO,CAAC,IAAI,CAAC,CAgCf;IAED,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,IAAI,GACvB,IAAI,CAsBN;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,cAAc,IAAI,OAAO,CA2CxB;IAED,QAAQ,CAAC,gBAAgB,EAAE,OAAO,GAAG;QACnC,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjD,SAAS,EAAE,sBAAsB,GAAG,SAAS,CAAC;KAC/C,CA0BA;IAED,KAAK,IAAI,IAAI,CAOZ;CACF"}
|
|
@@ -185,18 +185,15 @@ export class TempWorkspaceRetainedChild {
|
|
|
185
185
|
return descriptor;
|
|
186
186
|
}
|
|
187
187
|
async #assertProcAuthority(fd, ownerUid) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
const procPath = `/proc/self/fd/${fd}`;
|
|
192
|
-
const opened = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), this.#identity);
|
|
193
|
-
const followed = inspectFileIdentitySync(() => fsSync.statSync(procPath, { bigint: true }), this.#identity);
|
|
194
|
-
assertOwnedDirectory(opened, followed);
|
|
195
|
-
assertTempWorkspaceChildState(opened, ownerUid);
|
|
196
|
-
assertTempWorkspaceChildState(followed, ownerUid);
|
|
188
|
+
const filesystem = await fs.statfs("/proc/self/fd", { bigint: true });
|
|
189
|
+
this.#assertProcDescriptorAuthority(fd, ownerUid, filesystem.type);
|
|
197
190
|
}
|
|
198
191
|
#assertProcAuthoritySync(fd, ownerUid) {
|
|
199
|
-
|
|
192
|
+
const filesystem = fsSync.statfsSync("/proc/self/fd", { bigint: true });
|
|
193
|
+
this.#assertProcDescriptorAuthority(fd, ownerUid, filesystem.type);
|
|
194
|
+
}
|
|
195
|
+
#assertProcDescriptorAuthority(fd, ownerUid, filesystemType) {
|
|
196
|
+
if (filesystemType !== 0x9fa0n) {
|
|
200
197
|
throw new FsSafeError("path-mismatch", "directory mode requires a trusted procfs fd namespace");
|
|
201
198
|
}
|
|
202
199
|
const procPath = `/proc/self/fd/${fd}`;
|
|
@@ -309,13 +306,7 @@ export class TempWorkspaceRetainedChild {
|
|
|
309
306
|
catch (error) {
|
|
310
307
|
// The readable replacement has not been installed. Give it exactly one
|
|
311
308
|
// close attempt and leave neither indeterminate descriptor owned here.
|
|
312
|
-
|
|
313
|
-
fsSync.closeSync(fd);
|
|
314
|
-
}
|
|
315
|
-
catch (closeError) {
|
|
316
|
-
throw new AggregateError([error, closeError], "temp workspace child descriptor replacement close failed");
|
|
317
|
-
}
|
|
318
|
-
throw error;
|
|
309
|
+
closeAfterAdmissionFailure(fd, error, "temp workspace child descriptor replacement close failed");
|
|
319
310
|
}
|
|
320
311
|
this.#fd = fd;
|
|
321
312
|
this.#access = "read";
|
|
@@ -334,16 +325,7 @@ export class TempWorkspaceRetainedChild {
|
|
|
334
325
|
const fd = this.#fd;
|
|
335
326
|
this.#fd = undefined;
|
|
336
327
|
if (retainDescriptor && !this.canEnumerate) {
|
|
337
|
-
|
|
338
|
-
fsSync.closeSync(fd);
|
|
339
|
-
}
|
|
340
|
-
catch (closeError) {
|
|
341
|
-
throw new AggregateError([
|
|
342
|
-
new FsSafeError("helper-unavailable", "temp workspace cleanup requires a readable child descriptor"),
|
|
343
|
-
closeError,
|
|
344
|
-
], "temp workspace child descriptor rejection and close failed");
|
|
345
|
-
}
|
|
346
|
-
throw new FsSafeError("helper-unavailable", "temp workspace cleanup requires a readable child descriptor");
|
|
328
|
+
closeAfterAdmissionFailure(fd, new FsSafeError("helper-unavailable", "temp workspace cleanup requires a readable child descriptor"), "temp workspace child descriptor rejection and close failed");
|
|
347
329
|
}
|
|
348
330
|
if (!retainDescriptor) {
|
|
349
331
|
fsSync.closeSync(fd);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-workspace-owner.d.ts","sourceRoot":"","sources":["../src/temp-workspace-owner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAEL,0BAA0B,EAE1B,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,GAAG,eAAe,CAAC;AACvG,MAAM,MAAM,0BAA0B,GAAG,YAAY,GAAG,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"temp-workspace-owner.d.ts","sourceRoot":"","sources":["../src/temp-workspace-owner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAEL,0BAA0B,EAE1B,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,GAAG,eAAe,CAAC;AACvG,MAAM,MAAM,0BAA0B,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAuB1E,qBAAa,8BAA8B;;IACzC,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAM/C,YACE,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,0BAA0B,EAClC,SAAS,EAAE,0BAA0B,EACrC,OAAO,EAAE,MAAM,EAuEhB;IAED,IAAI,kBAAkB,IAAI,OAAO,CAGhC;IAED,oBAAoB,IAAI,IAAI,CAiB3B;IAUD,oBAAoB,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAenD;IAED,aAAa,IAAI,IAAI,CAEpB;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAED,KAAK,IAAI,IAAI,CAIZ;CACF;AAED,qBAAa,yBAAyB;;IAWpC,YACE,QAAQ,EAAE,0BAA0B,EACpC,UAAU,EAAE,8BAA8B,EAC1C,gBAAgB,EAAE,OAAO,EAO1B;IAkLD,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAM7C;IAED,WAAW,IAAI,0BAA0B,CAexC;CACF"}
|
|
@@ -294,22 +294,22 @@ export class TempWorkspaceCleanupOwner {
|
|
|
294
294
|
await beforeNativeRemoval(quarantine.path);
|
|
295
295
|
return this.#mapRemoval(await this.#capability.binding.removeOwnedTree(this.#capability.parent.fd, quarantine.name, this.#directory.fd));
|
|
296
296
|
}
|
|
297
|
-
let
|
|
297
|
+
let removalFailure;
|
|
298
298
|
try {
|
|
299
299
|
this.#assertQuarantine(quarantine);
|
|
300
300
|
try {
|
|
301
301
|
await fs.rm(quarantine.path, { recursive: true, force: true });
|
|
302
302
|
}
|
|
303
303
|
catch (error) {
|
|
304
|
-
|
|
304
|
+
removalFailure = { error };
|
|
305
305
|
throw error;
|
|
306
306
|
}
|
|
307
307
|
this.#capability.assertCurrent();
|
|
308
308
|
return "removed";
|
|
309
309
|
}
|
|
310
310
|
catch (error) {
|
|
311
|
-
if (
|
|
312
|
-
throw error;
|
|
311
|
+
if (removalFailure !== undefined)
|
|
312
|
+
throw removalFailure.error;
|
|
313
313
|
return "indeterminate";
|
|
314
314
|
}
|
|
315
315
|
}
|
|
@@ -318,22 +318,22 @@ export class TempWorkspaceCleanupOwner {
|
|
|
318
318
|
getFsSafeTestHooks()?.beforeTempWorkspaceNativeRemovalSync?.(quarantine.path);
|
|
319
319
|
return this.#mapRemoval(this.#capability.binding.removeOwnedTreeSync(this.#capability.parent.fd, quarantine.name, this.#directory.fd));
|
|
320
320
|
}
|
|
321
|
-
let
|
|
321
|
+
let removalFailure;
|
|
322
322
|
try {
|
|
323
323
|
this.#assertQuarantine(quarantine);
|
|
324
324
|
try {
|
|
325
325
|
fsSync.rmSync(quarantine.path, { recursive: true, force: true });
|
|
326
326
|
}
|
|
327
327
|
catch (error) {
|
|
328
|
-
|
|
328
|
+
removalFailure = { error };
|
|
329
329
|
throw error;
|
|
330
330
|
}
|
|
331
331
|
this.#capability.assertCurrent();
|
|
332
332
|
return "removed";
|
|
333
333
|
}
|
|
334
334
|
catch (error) {
|
|
335
|
-
if (
|
|
336
|
-
throw error;
|
|
335
|
+
if (removalFailure !== undefined)
|
|
336
|
+
throw removalFailure.error;
|
|
337
337
|
return "indeterminate";
|
|
338
338
|
}
|
|
339
339
|
}
|
package/dist/walk.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export type WalkDirectoryOptions = {
|
|
|
16
16
|
include?: (entry: WalkDirectoryEntry) => boolean;
|
|
17
17
|
descend?: (entry: WalkDirectoryEntry) => boolean;
|
|
18
18
|
};
|
|
19
|
+
export type AsyncWalkDirectoryOptions = Omit<WalkDirectoryOptions, "include" | "descend"> & {
|
|
20
|
+
include?: (entry: WalkDirectoryEntry) => boolean | Promise<boolean>;
|
|
21
|
+
descend?: (entry: WalkDirectoryEntry) => boolean | Promise<boolean>;
|
|
22
|
+
};
|
|
19
23
|
export type WalkDirectoryFailure = {
|
|
20
24
|
path: string;
|
|
21
25
|
relativePath: string;
|
|
@@ -32,6 +36,6 @@ type WalkDirectoryResultWithFailures = WalkDirectoryResult & {
|
|
|
32
36
|
failedDirs: WalkDirectoryFailure[];
|
|
33
37
|
};
|
|
34
38
|
export declare function walkDirectorySync(rootDir: string, options?: WalkDirectoryOptions): WalkDirectoryResultWithFailures;
|
|
35
|
-
export declare function walkDirectory(rootDir: string, options?:
|
|
39
|
+
export declare function walkDirectory(rootDir: string, options?: AsyncWalkDirectoryOptions): Promise<WalkDirectoryResultWithFailures>;
|
|
36
40
|
export {};
|
|
37
41
|
//# sourceMappingURL=walk.d.ts.map
|
package/dist/walk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAS7B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAGnB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC;AAEF,KAAK,+BAA+B,GAAG,mBAAmB,GAAG;IAC3D,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC,CAAC;
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAS7B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG;IAC1F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAGnB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC;AAEF,KAAK,+BAA+B,GAAG,mBAAmB,GAAG;IAC3D,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC,CAAC;AAoFF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,+BAA+B,CA4DjC;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,+BAA+B,CAAC,CAiE1C"}
|