@openclaw/fs-safe 0.14.0 → 0.16.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 +88 -0
- package/README.md +38 -10
- package/dist/advanced.d.ts +2 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +2 -1
- package/dist/archive-kind.d.ts +0 -1
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +5 -17
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +113 -46
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +10 -11
- package/dist/archive-tar-stream.d.ts +3 -0
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +56 -37
- package/dist/archive-tar-wasm.d.ts +16 -4
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +134 -34
- 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.d.ts.map +1 -1
- package/dist/archive.js +11 -11
- 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/clone-metadata.d.ts +1 -0
- package/dist/clone-metadata.d.ts.map +1 -1
- package/dist/clone-metadata.js +6 -2
- 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/create-directory.d.ts +20 -0
- package/dist/create-directory.d.ts.map +1 -0
- package/dist/create-directory.js +130 -0
- package/dist/create-file-async.d.ts +7 -0
- package/dist/create-file-async.d.ts.map +1 -0
- package/dist/create-file-async.js +121 -0
- package/dist/create-file.d.ts +8 -0
- package/dist/create-file.d.ts.map +1 -0
- package/dist/create-file.js +190 -0
- package/dist/create-owned-file.d.ts +8 -0
- package/dist/create-owned-file.d.ts.map +1 -0
- package/dist/create-owned-file.js +16 -0
- package/dist/create.d.ts +4 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +2 -0
- package/dist/creation-darwin.d.ts +7 -0
- package/dist/creation-darwin.d.ts.map +1 -0
- package/dist/creation-darwin.js +79 -0
- package/dist/creation-file-state.d.ts +19 -0
- package/dist/creation-file-state.d.ts.map +1 -0
- package/dist/creation-file-state.js +118 -0
- package/dist/creation-path.d.ts +21 -0
- package/dist/creation-path.d.ts.map +1 -0
- package/dist/creation-path.js +71 -0
- package/dist/creation-permissions.d.ts +19 -0
- package/dist/creation-permissions.d.ts.map +1 -0
- package/dist/creation-permissions.js +125 -0
- package/dist/directory-durability.d.ts +1 -1
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +22 -80
- package/dist/directory-guard.d.ts +3 -0
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-mode-node.d.ts +2 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +8 -0
- package/dist/directory-mode-owner.js +5 -5
- package/dist/directory-receipt.d.ts +24 -0
- package/dist/directory-receipt.d.ts.map +1 -0
- package/dist/directory-receipt.js +127 -0
- package/dist/file-cleanup.d.ts +19 -0
- package/dist/file-cleanup.d.ts.map +1 -0
- package/dist/file-cleanup.js +78 -0
- 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-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -6
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +3 -9
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +5 -1
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +5 -8
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +47 -12
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +36 -7
- 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/move-path.js +1 -1
- package/dist/native-binding.d.ts +13 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-fallback-warning.d.ts +4 -0
- package/dist/native-fallback-warning.d.ts.map +1 -0
- package/dist/native-fallback-warning.js +11 -0
- package/dist/native-operations.d.ts +0 -2
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +0 -24
- package/dist/native-parent-admission.d.ts +5 -2
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +27 -7
- package/dist/native-pinned-write-windows.d.ts +1 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +174 -29
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +26 -14
- 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 +209 -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 +3 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +121 -72
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +12 -8
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +10 -4
- 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-input.d.ts +4 -0
- package/dist/pinned-write-input.d.ts.map +1 -0
- package/dist/pinned-write-input.js +25 -0
- package/dist/pinned-write-mode.d.ts +5 -0
- package/dist/pinned-write-mode.d.ts.map +1 -0
- package/dist/pinned-write-mode.js +24 -0
- package/dist/pinned-write-staged.d.ts +6 -0
- package/dist/pinned-write-staged.d.ts.map +1 -0
- package/dist/pinned-write-staged.js +187 -0
- package/dist/pinned-write-types.d.ts +5 -0
- package/dist/pinned-write-types.d.ts.map +1 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +35 -145
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +18 -4
- package/dist/private-producer-handoff-sync.d.ts +14 -0
- package/dist/private-producer-handoff-sync.d.ts.map +1 -0
- package/dist/private-producer-handoff-sync.js +114 -0
- package/dist/private-producer-handoff.d.ts +22 -4
- package/dist/private-producer-handoff.d.ts.map +1 -1
- package/dist/private-producer-handoff.js +140 -77
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +75 -121
- package/dist/publish-copy-stage.d.ts +2 -1
- package/dist/publish-copy-stage.d.ts.map +1 -1
- package/dist/publish-copy-stage.js +16 -7
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +2 -2
- 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 -9
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +56 -72
- 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 +8 -1
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +17 -4
- package/dist/root-directory-creation.d.ts +3 -3
- package/dist/root-directory-creation.d.ts.map +1 -1
- package/dist/root-directory-creation.js +15 -3
- 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 +46 -17
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +24 -15
- package/dist/root-options.d.ts +12 -4
- package/dist/root-options.d.ts.map +1 -1
- 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-stat.d.ts.map +1 -1
- package/dist/root-path-stat.js +59 -7
- 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-publication.js +1 -1
- 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 +3 -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/secure-file-windows.d.ts +6 -0
- package/dist/secure-file-windows.d.ts.map +1 -1
- package/dist/secure-file-windows.js +34 -117
- package/dist/secure-file.js +2 -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-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +2 -1
- 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/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +6 -6
- package/dist/staged-file-settlement.d.ts +17 -0
- package/dist/staged-file-settlement.d.ts.map +1 -0
- package/dist/staged-file-settlement.js +57 -0
- 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 +4 -3
- package/dist/windows-security-bridge.cs +336 -0
- package/dist/windows-security-bridge.ps1 +15 -0
- package/dist/windows-security-command.d.ts +26 -0
- package/dist/windows-security-command.d.ts.map +1 -0
- package/dist/windows-security-command.js +363 -0
- package/dist/windows-security-facts.d.ts +6 -0
- package/dist/windows-security-facts.d.ts.map +1 -0
- package/dist/windows-security-facts.js +108 -0
- package/docs/advanced.md +4 -2
- package/docs/archive.md +97 -46
- package/docs/atomic.md +85 -8
- package/docs/config.md +6 -2
- package/docs/contributing.md +44 -4
- package/docs/copy.md +37 -0
- package/docs/creation.md +128 -0
- package/docs/durability.md +24 -0
- package/docs/file-store.md +19 -0
- package/docs/install.md +31 -7
- package/docs/json-store.md +5 -0
- package/docs/migrating-to-0.5.md +15 -6
- package/docs/migrating-to-0.6.md +9 -4
- package/docs/native-helper.md +32 -12
- package/docs/native.md +38 -7
- 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 +50 -14
- package/docs/public-api.md +3 -2
- package/docs/root.md +56 -3
- package/docs/secret-file.md +11 -2
- package/docs/secure-file.md +9 -4
- package/docs/sidecar-lock.md +114 -8
- package/docs/staged-file.md +12 -3
- package/docs/temp.md +20 -3
- package/docs/walk.md +67 -1
- package/docs/writing.md +76 -6
- package/package.json +19 -16
- 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":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAE/B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,SAAS,EAAE,uBAAuB,CAAC;IACnC,WAAW,EAAE,4BAA4B,CAAC;IAC1C,IAAI,EAAE,WAAW,CAAC;IAClB,cAAc,CAAC,EAAE,yBAAyB,CAAC;IAC3C,kBAAkB,CAAC,IAAI,IAAI,CAAC;CAC7B,CAAC,CAAC;AAEH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7C,OAAO,CAKT;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,uBAAuB,CAY7F;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CAS1F;AASD,wBAAgB,6BAA6B,CAC3C,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,aAAa,GACtB,qBAAqB,CA2CvB;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,qBAAqB,GAC9B,WAAW,CAeb;AAED,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,qBAAqB,GAC9B,4BAA4B,CAoB9B;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,uBAAuB,CAAC;CAClC,CAgCA"}
|
package/dist/staged-directory.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs, {} from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { directoryReceiptAuthority, ownDirectoryReceipt } from "./directory-receipt.js";
|
|
3
4
|
import { FsSafeError } from "./errors.js";
|
|
4
5
|
import { inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
5
6
|
import { nativePolicyDirectoryObserver, NativePolicyDirectoryMismatch } from "./native-policy-directory-observation.js";
|
|
@@ -116,16 +117,15 @@ export function refreshPolicyStagedDirectoryObservation(captured) {
|
|
|
116
117
|
return checkedMutationDirectory(captured.directory.path, canonicalPath, current);
|
|
117
118
|
}
|
|
118
119
|
export function openStagedDirectory(directory) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
};
|
|
120
|
+
const expected = typeof directory === "string"
|
|
121
|
+
? undefined
|
|
122
|
+
: directoryReceiptAuthority(ownDirectoryReceipt(directory));
|
|
123
|
+
const pathname = resolvePathPreservingWindowsRoot(typeof directory === "string" ? directory : expected.path);
|
|
124
124
|
const before = fs.lstatSync(pathname, { bigint: true });
|
|
125
125
|
if (!before.isDirectory()) {
|
|
126
126
|
throw new FsSafeError("not-file", "staging parent must be a real directory");
|
|
127
127
|
}
|
|
128
|
-
if (expected && (!exactIdentityMatches(expected, before) || realpathSync(pathname) !== expected.realPath)) {
|
|
128
|
+
if (expected && (!exactIdentityMatches(expected.identity, before) || realpathSync(pathname) !== expected.realPath)) {
|
|
129
129
|
throw new FsSafeError("path-mismatch", "stale staging directory receipt");
|
|
130
130
|
}
|
|
131
131
|
const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BigIntStats } from "node:fs";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
import type { StagedFileCleanupReceipt, StagedFileFailureDetails, StagedFilePublication, StagedFileReceipt } from "./staged-file-types.js";
|
|
4
|
+
export declare function createStagedFileReceipt(directory: StagedFileReceipt["directory"], temporaryBasename: string, stat: BigIntStats): StagedFileReceipt;
|
|
5
|
+
export declare function stagedFileFailure(error: unknown, details: StagedFileFailureDetails): FsSafeError;
|
|
6
|
+
/** Finish owned cleanup and every close before surfacing publication evidence. */
|
|
7
|
+
export declare function settleStagedFile(params: {
|
|
8
|
+
temporaryBasename: string;
|
|
9
|
+
publication: StagedFilePublication;
|
|
10
|
+
phase: StagedFileFailureDetails["phase"];
|
|
11
|
+
failure?: {
|
|
12
|
+
error: unknown;
|
|
13
|
+
};
|
|
14
|
+
cleanup: () => Promise<StagedFileCleanupReceipt["status"]>;
|
|
15
|
+
close: readonly (() => Promise<void> | void)[];
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=staged-file-settlement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-file-settlement.d.ts","sourceRoot":"","sources":["../src/staged-file-settlement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAEhC,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,EACzC,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,WAAW,GAChB,iBAAiB,CAUnB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,GAAG,WAAW,CAKhG;AAED,kFAAkF;AAClF,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,qBAAqB,CAAC;IACnC,KAAK,EAAE,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7B,OAAO,EAAE,MAAM,OAAO,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,SAAS,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;CAChD,GAAG,OAAO,CAAC,IAAI,CAAC,CAkChB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
export function createStagedFileReceipt(directory, temporaryBasename, stat) {
|
|
3
|
+
return Object.freeze({
|
|
4
|
+
directory,
|
|
5
|
+
temporaryBasename,
|
|
6
|
+
identity: Object.freeze({
|
|
7
|
+
dev: stat.dev, ino: stat.ino, mode: Number(stat.mode & 4095n),
|
|
8
|
+
nlink: stat.nlink, size: stat.size, uid: Number(stat.uid), gid: Number(stat.gid),
|
|
9
|
+
mtimeNs: stat.mtimeNs, ctimeNs: stat.ctimeNs,
|
|
10
|
+
}),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function stagedFileFailure(error, details) {
|
|
14
|
+
const code = error instanceof FsSafeError
|
|
15
|
+
? error.code
|
|
16
|
+
: error?.code === "EEXIST" ? "already-exists" : "helper-failed";
|
|
17
|
+
return new FsSafeError(code, `staged file ${details.phase} failed`, { cause: error, details });
|
|
18
|
+
}
|
|
19
|
+
/** Finish owned cleanup and every close before surfacing publication evidence. */
|
|
20
|
+
export async function settleStagedFile(params) {
|
|
21
|
+
const errors = params.failure ? [params.failure.error] : [];
|
|
22
|
+
let status;
|
|
23
|
+
try {
|
|
24
|
+
status = await params.cleanup();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
status = "failed";
|
|
28
|
+
errors.push(error);
|
|
29
|
+
}
|
|
30
|
+
let resources = "closed";
|
|
31
|
+
for (const close of params.close) {
|
|
32
|
+
try {
|
|
33
|
+
await close();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
resources = "close-failed";
|
|
37
|
+
errors.push(error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const incomplete = status === "failed" || status === "preserved" || resources === "close-failed";
|
|
41
|
+
// A settled pre-publication failure retains the producer/authority rejection exactly.
|
|
42
|
+
if (!incomplete && (!params.failure || params.publication.status === "not-published"))
|
|
43
|
+
return;
|
|
44
|
+
const error = errors.length > 1 ? new AggregateError(errors, "staged operation and settlement failed")
|
|
45
|
+
: errors.length === 1 ? errors[0] : new FsSafeError("not-removable", "staged cleanup preserved an unverified entry");
|
|
46
|
+
const cleanup = Object.freeze({
|
|
47
|
+
temporaryBasename: params.temporaryBasename,
|
|
48
|
+
publication: params.publication,
|
|
49
|
+
status,
|
|
50
|
+
resources,
|
|
51
|
+
});
|
|
52
|
+
throw stagedFileFailure(error, {
|
|
53
|
+
phase: params.failure ? params.phase : "cleanup",
|
|
54
|
+
publication: params.publication,
|
|
55
|
+
cleanup,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -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"}
|