@openclaw/fs-safe 0.15.0 → 0.17.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 +86 -0
- package/README.md +36 -7
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +2 -8
- package/dist/advanced.d.ts +3 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +3 -0
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +22 -23
- 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 +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +4 -4
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +1 -0
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +6 -7
- 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-count.d.ts.map +1 -1
- package/dist/archive-zip-count.js +21 -1
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +23 -1
- package/dist/archive-zip-loader.d.ts +2 -0
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +7 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +7 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +14 -9
- package/dist/byte-view.d.ts +3 -0
- package/dist/byte-view.d.ts.map +1 -0
- package/dist/byte-view.js +13 -0
- 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/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 +6 -0
- package/dist/creation-darwin.d.ts.map +1 -0
- package/dist/creation-darwin.js +70 -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 +7 -7
- 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-receipt.d.ts +24 -0
- package/dist/directory-receipt.d.ts.map +1 -0
- package/dist/directory-receipt.js +123 -0
- package/dist/file-cleanup.d.ts +20 -0
- package/dist/file-cleanup.d.ts.map +1 -0
- package/dist/file-cleanup.js +81 -0
- package/dist/file-contents.d.ts +6 -0
- package/dist/file-contents.d.ts.map +1 -0
- package/dist/file-contents.js +40 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +16 -4
- package/dist/file-lock-sync-root-acquire.d.ts.map +1 -1
- package/dist/file-lock-sync-root-acquire.js +3 -0
- package/dist/file-lock-sync-root-held.d.ts +1 -2
- package/dist/file-lock-sync-root-held.d.ts.map +1 -1
- package/dist/file-lock-sync-root-held.js +7 -5
- package/dist/file-lock-sync-stale-admission.d.ts.map +1 -1
- package/dist/file-lock-sync-stale-admission.js +3 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +8 -11
- 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-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +2 -5
- package/dist/file-store.js +3 -3
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +27 -19
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +2 -5
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +2 -6
- package/dist/json-durable-queue-paths.d.ts.map +1 -1
- package/dist/json-durable-queue-paths.js +2 -24
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +10 -9
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +32 -75
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +19 -21
- package/dist/move-path-cleanup.d.ts +5 -19
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +57 -21
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +63 -40
- package/dist/native-binding.d.ts +11 -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 +2 -0
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +3 -2
- 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 +173 -28
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +19 -3
- package/dist/native-policy-parent-windows.d.ts.map +1 -1
- package/dist/native-policy-parent-windows.js +15 -6
- package/dist/native-staged-file.d.ts +5 -3
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +90 -40
- package/dist/native.js +2 -2
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +11 -2
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +10 -4
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +3 -17
- 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 +35 -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 +31 -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 +186 -0
- package/dist/pinned-write-types.d.ts +3 -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 +41 -147
- 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/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 +2 -2
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +58 -98
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +35 -44
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +28 -26
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +13 -22
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +10 -16
- package/dist/replace-file-temp-owner.d.ts +0 -7
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +9 -60
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +9 -13
- package/dist/root-create-input.d.ts +2 -1
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +13 -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.map +1 -1
- package/dist/root-directory-list.js +20 -3
- package/dist/root-file-final-admission.d.ts +1 -1
- package/dist/root-file-final-admission.d.ts.map +1 -1
- package/dist/root-file-final-admission.js +5 -2
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +3 -2
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +78 -27
- package/dist/root-move-noreplace.d.ts +2 -0
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +22 -13
- package/dist/root-options.d.ts +12 -4
- package/dist/root-options.d.ts.map +1 -1
- package/dist/root-path-stat.d.ts.map +1 -1
- package/dist/root-path-stat.js +59 -7
- package/dist/root-read-admission.d.ts.map +1 -1
- package/dist/root-read-admission.js +7 -2
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +15 -1
- package/dist/root-write-publication.js +1 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- 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/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +4 -6
- package/dist/sidecar-lock-handle.d.ts +3 -0
- package/dist/sidecar-lock-handle.d.ts.map +1 -1
- package/dist/sidecar-lock-handle.js +6 -0
- package/dist/sidecar-lock-reclaim.d.ts +1 -1
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +11 -8
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +2 -1
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +3 -5
- package/dist/staged-directory.d.ts +2 -2
- 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/strict-file-identity.d.ts +1 -1
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +9 -9
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +2 -27
- package/dist/temp-workspace-owner.js +4 -4
- package/dist/unicode-path.d.ts.map +1 -1
- package/dist/unicode-path.js +3 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +4 -2
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +2 -1
- 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/dist/write-file-handle.d.ts +7 -0
- package/dist/write-file-handle.d.ts.map +1 -1
- package/dist/write-file-handle.js +23 -0
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -8
- package/dist/write-queue.d.ts.map +1 -1
- package/dist/write-queue.js +1 -4
- package/docs/advanced.md +71 -2
- package/docs/archive.md +102 -39
- package/docs/atomic.md +29 -5
- package/docs/config.md +6 -2
- package/docs/contributing.md +48 -4
- package/docs/copy.md +2 -0
- package/docs/creation.md +132 -0
- package/docs/durability.md +59 -0
- package/docs/file-contents.md +68 -0
- package/docs/install.md +31 -7
- package/docs/json.md +5 -4
- package/docs/local-roots.md +2 -0
- package/docs/migrating-to-0.5.md +15 -6
- package/docs/migrating-to-0.6.md +9 -4
- package/docs/mutation-policy-proof.md +5 -3
- package/docs/native-helper.md +22 -9
- package/docs/native.md +47 -15
- package/docs/path.md +4 -4
- package/docs/permissions.md +37 -14
- package/docs/public-api.md +5 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +2 -2
- package/docs/regular-file.md +3 -0
- package/docs/root.md +43 -0
- package/docs/secret-file.md +11 -2
- package/docs/secure-file.md +9 -4
- package/docs/sidecar-lock.md +14 -5
- package/docs/staged-file.md +9 -3
- package/docs/store.md +3 -1
- package/docs/temp.md +4 -1
- package/docs/types.md +18 -2
- package/docs/walk.md +7 -0
- package/docs/writing.md +80 -7
- package/package.json +18 -15
|
@@ -3,7 +3,8 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { ensureAbsoluteDirectory } from "./absolute-path.js";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
|
-
import {
|
|
6
|
+
import { assertDirectoryReceiptCurrentSync, copyRetainedDirectoryReceipt, createDirectoryReceiptSync, directoryReceiptAuthority, directoryReceiptIdentity, ownDirectoryReceipt, } from "./directory-receipt.js";
|
|
7
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
7
8
|
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
|
|
8
9
|
import { realpathSync } from "./realpath.js";
|
|
9
10
|
function directoryOpenFlags() {
|
|
@@ -38,81 +39,23 @@ function unsupportedOutcome(error) {
|
|
|
38
39
|
const code = error.code;
|
|
39
40
|
return code ? { status: "unsupported", code } : { status: "unsupported" };
|
|
40
41
|
}
|
|
41
|
-
function assertDirectory(identity, pathname, label) {
|
|
42
|
-
if (identity.isSymbolicLink() || !identity.isDirectory()) {
|
|
43
|
-
throw new FsSafeError("not-file", `${label} must be a real directory: ${pathname}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function ownDirectoryReceipt(receipt) {
|
|
47
|
-
const pathname = receipt.path;
|
|
48
|
-
const realPath = receipt.realPath;
|
|
49
|
-
const identity = receipt.identity;
|
|
50
|
-
return { path: pathname, realPath, identity };
|
|
51
|
-
}
|
|
52
|
-
function assertDirectoryReceiptPaths(receipt, label) {
|
|
53
|
-
assertNoWindowsPathAlias(receipt.path, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
54
|
-
assertNoWindowsPathAlias(receipt.realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
55
|
-
}
|
|
56
42
|
async function createDirectoryReceipt(directoryPath, label) {
|
|
57
|
-
|
|
58
|
-
const resolvedPath = resolvePathPreservingWindowsRoot(directoryPath);
|
|
59
|
-
assertNoWindowsPathAlias(resolvedPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
60
|
-
const operationPath = pathForWindowsFilesystem(resolvedPath);
|
|
61
|
-
const identity = fsSync.lstatSync(operationPath);
|
|
62
|
-
assertDirectory(identity, resolvedPath, label);
|
|
63
|
-
const realPath = realpathSync.native(operationPath);
|
|
64
|
-
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
65
|
-
return {
|
|
66
|
-
path: resolvedPath,
|
|
67
|
-
realPath,
|
|
68
|
-
identity,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function createDirectoryReceiptSync(directoryPath, label) {
|
|
72
|
-
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
73
|
-
const resolvedPath = resolvePathPreservingWindowsRoot(directoryPath);
|
|
74
|
-
assertNoWindowsPathAlias(resolvedPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
75
|
-
const operationPath = pathForWindowsFilesystem(resolvedPath);
|
|
76
|
-
const identity = fsSync.lstatSync(operationPath);
|
|
77
|
-
assertDirectory(identity, resolvedPath, label);
|
|
78
|
-
const realPath = realpathSync(operationPath);
|
|
79
|
-
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
80
|
-
return {
|
|
81
|
-
path: resolvedPath,
|
|
82
|
-
realPath,
|
|
83
|
-
identity,
|
|
84
|
-
};
|
|
43
|
+
return createDirectoryReceiptSync(directoryPath, label, realpathSync.native);
|
|
85
44
|
}
|
|
86
45
|
async function assertDirectoryReceiptCurrent(receipt, label) {
|
|
87
|
-
|
|
88
|
-
const operationPath = pathForWindowsFilesystem(receipt.path);
|
|
89
|
-
const currentIdentity = fsSync.lstatSync(operationPath);
|
|
90
|
-
assertDirectory(currentIdentity, receipt.path, label);
|
|
91
|
-
const realPath = realpathSync.native(operationPath);
|
|
92
|
-
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
93
|
-
if (!sameFileIdentity(receipt.identity, currentIdentity) ||
|
|
94
|
-
realPath !== receipt.realPath) {
|
|
95
|
-
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
|
|
96
|
-
}
|
|
46
|
+
assertDirectoryReceiptCurrentSync(receipt, label, realpathSync.native);
|
|
97
47
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
realPath !== receipt.realPath) {
|
|
107
|
-
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
|
|
108
|
-
}
|
|
48
|
+
function assertOpenDirectoryIdentity(descriptor, receipt, label) {
|
|
49
|
+
inspectFileIdentitySync(() => {
|
|
50
|
+
const opened = fsSync.fstatSync(descriptor, { bigint: true });
|
|
51
|
+
if (!opened.isDirectory()) {
|
|
52
|
+
throw new FsSafeError("not-file", `${label} must be a real directory: ${receipt.path}`);
|
|
53
|
+
}
|
|
54
|
+
return opened;
|
|
55
|
+
}, directoryReceiptAuthority(receipt).identity);
|
|
109
56
|
}
|
|
110
57
|
async function assertOpenDirectoryCurrent(handle, receipt, label) {
|
|
111
|
-
|
|
112
|
-
assertDirectory(openedIdentity, receipt.path, label);
|
|
113
|
-
if (!sameFileIdentity(receipt.identity, openedIdentity)) {
|
|
114
|
-
throw new FsSafeError("path-mismatch", `${label} handle changed during directory sync: ${receipt.path}`);
|
|
115
|
-
}
|
|
58
|
+
assertOpenDirectoryIdentity(handle.fd, receipt, label);
|
|
116
59
|
await assertDirectoryReceiptCurrent(receipt, label);
|
|
117
60
|
}
|
|
118
61
|
class PinnedDirectoryImpl {
|
|
@@ -124,7 +67,7 @@ class PinnedDirectoryImpl {
|
|
|
124
67
|
constructor(handle, receipt, label) {
|
|
125
68
|
this.#handle = handle;
|
|
126
69
|
this.#authority = receipt;
|
|
127
|
-
this.receipt =
|
|
70
|
+
this.receipt = copyRetainedDirectoryReceipt(receipt);
|
|
128
71
|
this.#label = label;
|
|
129
72
|
}
|
|
130
73
|
async assertCurrent() {
|
|
@@ -213,11 +156,7 @@ export function syncDirectorySync(directory, options = {}) {
|
|
|
213
156
|
return unsupportedOutcome(error);
|
|
214
157
|
}
|
|
215
158
|
try {
|
|
216
|
-
|
|
217
|
-
assertDirectory(openedIdentity, receipt.path, label);
|
|
218
|
-
if (!sameFileIdentity(receipt.identity, openedIdentity)) {
|
|
219
|
-
throw new FsSafeError("path-mismatch", `${label} handle changed during directory sync: ${receipt.path}`);
|
|
220
|
-
}
|
|
159
|
+
assertOpenDirectoryIdentity(descriptor, receipt, label);
|
|
221
160
|
assertDirectoryReceiptCurrentSync(receipt, label);
|
|
222
161
|
try {
|
|
223
162
|
fsSync.fsyncSync(descriptor);
|
|
@@ -273,13 +212,16 @@ export async function ensureDurableDirectory(options) {
|
|
|
273
212
|
assertNoWindowsPathAlias(requestedDirectoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
274
213
|
const directoryPath = resolvePathPreservingWindowsRoot(requestedDirectoryPath);
|
|
275
214
|
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
215
|
+
const expectedInput = options.expectedExistingIdentity;
|
|
216
|
+
const expectedIdentity = expectedInput && directoryReceiptIdentity(expectedInput);
|
|
276
217
|
const ancestorReceipt = await findExistingAncestorReceipt(directoryPath, label);
|
|
277
218
|
const targetExists = ancestorReceipt.path === directoryPath;
|
|
278
|
-
|
|
279
|
-
if (expectedExistingIdentity &&
|
|
280
|
-
(!targetExists || !sameFileIdentity(expectedExistingIdentity, ancestorReceipt.identity))) {
|
|
219
|
+
if (expectedIdentity && !targetExists) {
|
|
281
220
|
throw new FsSafeError("path-mismatch", `${label} changed before durable directory pinning: ${directoryPath}`);
|
|
282
221
|
}
|
|
222
|
+
if (expectedIdentity) {
|
|
223
|
+
inspectFileIdentitySync(() => directoryReceiptAuthority(ancestorReceipt).identity, expectedIdentity);
|
|
224
|
+
}
|
|
283
225
|
const ancestor = await pinDirectory(ancestorReceipt, { label });
|
|
284
226
|
const pinnedDirectories = [ancestor];
|
|
285
227
|
try {
|
|
@@ -336,7 +278,7 @@ export async function ensureDurableDirectory(options) {
|
|
|
336
278
|
}
|
|
337
279
|
await ancestor.assertCurrent();
|
|
338
280
|
await assertDirectoryReceiptCurrent(finalReceipt, label);
|
|
339
|
-
return
|
|
281
|
+
return Object.assign(copyRetainedDirectoryReceipt(finalReceipt), { parentSync });
|
|
340
282
|
}
|
|
341
283
|
finally {
|
|
342
284
|
await Promise.all(pinnedDirectories.toReversed().map(async (directory) => directory.close()));
|
|
@@ -34,6 +34,9 @@ export declare function assertAsyncDirectoryGuard(guard: AnyAsyncDirectoryGuard)
|
|
|
34
34
|
export declare function createSyncDirectoryGuard(dir: string): SyncDirectoryGuard;
|
|
35
35
|
export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard | AnyAsyncDirectoryGuard): void;
|
|
36
36
|
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string): Promise<AsyncDirectoryGuard>;
|
|
37
|
+
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
|
|
38
|
+
bigint: true;
|
|
39
|
+
}): Promise<AsyncDirectoryGuard<BigIntStats>>;
|
|
37
40
|
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
|
|
38
41
|
bigint: boolean;
|
|
39
42
|
}): Promise<AnyAsyncDirectoryGuard>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type BigIntStats } from "node:fs";
|
|
2
2
|
import { type DirectoryModeOwner } from "./directory-mode-owner.js";
|
|
3
|
+
/** Darwin descriptor inspection avoids requesting directory-content reads. */
|
|
4
|
+
export declare function nodeDarwinDirectoryMetadataFlags(): number;
|
|
3
5
|
export declare function nodeDirectorySearchOnlyFlags(): {
|
|
4
6
|
flags: number;
|
|
5
7
|
proc: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE5G,wBAAgB,4BAA4B,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAsFlE"}
|
|
1
|
+
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE5G,8EAA8E;AAC9E,wBAAgB,gCAAgC,IAAI,MAAM,CAMzD;AAED,wBAAgB,4BAA4B,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAsFlE"}
|
|
@@ -4,6 +4,14 @@ import { FsSafeError } from "./errors.js";
|
|
|
4
4
|
import { inspectDirectoryIdentity, inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
5
5
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
6
|
import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
|
|
7
|
+
/** Darwin descriptor inspection avoids requesting directory-content reads. */
|
|
8
|
+
export function nodeDarwinDirectoryMetadataFlags() {
|
|
9
|
+
if (process.platform !== "darwin" || (process.arch !== "x64" && process.arch !== "arm64")) {
|
|
10
|
+
throw new FsSafeError("helper-unavailable", "Darwin directory metadata descriptors are unavailable");
|
|
11
|
+
}
|
|
12
|
+
// Darwin SDK O_EVTONLY; Node omits this flag from its exported constants.
|
|
13
|
+
return 0x8000 | constants.O_DIRECTORY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
14
|
+
}
|
|
7
15
|
export function nodeDirectorySearchOnlyFlags() {
|
|
8
16
|
if (process.arch !== "x64" && process.arch !== "arm64")
|
|
9
17
|
return undefined;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type BigIntStats, type Stats } from "node:fs";
|
|
2
|
+
import type { DirectoryReceipt } from "./directory-durability.js";
|
|
3
|
+
import type { FileIdentityStat } from "./file-identity.js";
|
|
4
|
+
type ExactIdentity = Readonly<Pick<BigIntStats, "dev" | "ino">>;
|
|
5
|
+
declare const numericFields: readonly ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
|
|
6
|
+
declare const timeFields: readonly ["atime", "mtime", "ctime", "birthtime"];
|
|
7
|
+
type DirectoryMetadata = Pick<Stats, typeof numericFields[number] | `${typeof timeFields[number]}Ms`>;
|
|
8
|
+
type DirectoryProvenance = Readonly<{
|
|
9
|
+
identity: ExactIdentity;
|
|
10
|
+
metadata: Readonly<DirectoryMetadata>;
|
|
11
|
+
}>;
|
|
12
|
+
type DirectoryAuthority = DirectoryProvenance & Readonly<{
|
|
13
|
+
path: string;
|
|
14
|
+
realPath: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function directoryReceiptIdentity(identity: FileIdentityStat): ExactIdentity;
|
|
17
|
+
export declare function directoryReceiptAuthority(receipt: DirectoryReceipt<Stats | BigIntStats>): DirectoryAuthority;
|
|
18
|
+
export declare function ownDirectoryReceipt(receipt: DirectoryReceipt<Stats | BigIntStats>): DirectoryReceipt;
|
|
19
|
+
export declare function copyRetainedDirectoryReceipt(receipt: DirectoryReceipt): DirectoryReceipt;
|
|
20
|
+
export declare function createDirectoryReceiptFromIdentity(pathname: string, realPath: string, exactStat: BigIntStats): DirectoryReceipt;
|
|
21
|
+
export declare function createDirectoryReceiptSync(directoryPath: string, label: string, canonicalize?: (pathname: string) => string): DirectoryReceipt;
|
|
22
|
+
export declare function assertDirectoryReceiptCurrentSync(receipt: DirectoryReceipt, label: string, canonicalize?: (pathname: string) => string): void;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=directory-receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-receipt.d.ts","sourceRoot":"","sources":["../src/directory-receipt.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAQ3D,KAAK,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC;AAChE,QAAA,MAAM,aAAa,YAAI,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAClH,QAAA,MAAM,UAAU,YAAI,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;AACrE,KAAK,iBAAiB,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACtG,KAAK,mBAAmB,GAAG,QAAQ,CAAC;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;CACvC,CAAC,CAAC;AACH,KAAK,kBAAkB,GAAG,mBAAmB,GAAG,QAAQ,CAAC;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAOH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,aAAa,CAuBlF;AAsCD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,kBAAkB,CAE5G;AAQD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,gBAAgB,CAEpG;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAGxF;AAWD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,WAAW,GACrB,gBAAgB,CASlB;AAED,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,YAAY,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAqB,GACxD,gBAAgB,CASlB;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,MAAM,EACb,YAAY,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAqB,GACxD,IAAI,CAUN"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import fs, {} from "node:fs";
|
|
2
|
+
import { inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { realpathSync } from "./realpath.js";
|
|
5
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
6
|
+
const numericFields = ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
|
|
7
|
+
const timeFields = ["atime", "mtime", "ctime", "birthtime"];
|
|
8
|
+
// Live pins retain private authority. New admissions snapshot the supplied
|
|
9
|
+
// fields and accept exact provenance only while its public identity is intact.
|
|
10
|
+
const authorities = new WeakMap();
|
|
11
|
+
const identities = new WeakMap();
|
|
12
|
+
export function directoryReceiptIdentity(identity) {
|
|
13
|
+
if (!identity) {
|
|
14
|
+
throw new FsSafeError("path-mismatch", "directory receipt identity is missing");
|
|
15
|
+
}
|
|
16
|
+
const known = identities.get(identity);
|
|
17
|
+
const dev = identity.dev;
|
|
18
|
+
const ino = identity.ino;
|
|
19
|
+
if (known) {
|
|
20
|
+
const matches = (value, expected) => typeof value === "bigint" ? value === expected : value === Number(expected);
|
|
21
|
+
if (!matches(dev, known.identity.dev) || !matches(ino, known.identity.ino)) {
|
|
22
|
+
throw new FsSafeError("path-mismatch", "directory receipt identity changed");
|
|
23
|
+
}
|
|
24
|
+
return known.identity;
|
|
25
|
+
}
|
|
26
|
+
const exact = (value) => {
|
|
27
|
+
if ((typeof value !== "bigint" && !Number.isSafeInteger(value)) ||
|
|
28
|
+
(process.platform === "win32" && (value === 0 || value === 0n))) {
|
|
29
|
+
throw new FsSafeError("path-mismatch", "directory receipt identity could not be verified");
|
|
30
|
+
}
|
|
31
|
+
return BigInt(value);
|
|
32
|
+
};
|
|
33
|
+
return Object.freeze({ dev: exact(dev), ino: exact(ino) });
|
|
34
|
+
}
|
|
35
|
+
function snapshotDirectoryReceipt(receipt) {
|
|
36
|
+
const pathname = receipt?.path;
|
|
37
|
+
const realPath = receipt?.realPath;
|
|
38
|
+
const stat = receipt?.identity;
|
|
39
|
+
if (typeof pathname !== "string" || typeof realPath !== "string" || stat == null) {
|
|
40
|
+
throw new FsSafeError("path-mismatch", "directory receipt is incomplete");
|
|
41
|
+
}
|
|
42
|
+
assertNoWindowsPathAlias(pathname, "filesystem");
|
|
43
|
+
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
44
|
+
const identity = directoryReceiptIdentity(stat);
|
|
45
|
+
const known = authorities.get(receipt);
|
|
46
|
+
if (known && (pathname !== known.path || realPath !== known.realPath ||
|
|
47
|
+
identity.dev !== known.identity.dev || identity.ino !== known.identity.ino)) {
|
|
48
|
+
throw new FsSafeError("path-mismatch", "directory receipt changed before admission");
|
|
49
|
+
}
|
|
50
|
+
const metadata = known?.metadata ?? identities.get(stat)?.metadata ?? snapshotDirectoryMetadata(stat, identity);
|
|
51
|
+
return Object.freeze({ path: pathname, realPath, identity, metadata });
|
|
52
|
+
}
|
|
53
|
+
function snapshotDirectoryMetadata(stat, identity) {
|
|
54
|
+
const metadata = {};
|
|
55
|
+
for (const field of numericFields) {
|
|
56
|
+
metadata[field] = Number(field === "dev" || field === "ino" ? identity[field] : stat[field]);
|
|
57
|
+
}
|
|
58
|
+
for (const field of timeFields) {
|
|
59
|
+
if ("atimeNs" in stat) {
|
|
60
|
+
const nanoseconds = stat[`${field}Ns`];
|
|
61
|
+
const remainder = ((nanoseconds % 1000000000n) + 1000000000n) % 1000000000n;
|
|
62
|
+
metadata[`${field}Ms`] = Number((nanoseconds - remainder) / 1000000000n) * 1_000 + Number(remainder) / 1_000_000;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
metadata[`${field}Ms`] = stat[`${field}Ms`];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return Object.freeze(metadata);
|
|
69
|
+
}
|
|
70
|
+
export function directoryReceiptAuthority(receipt) {
|
|
71
|
+
return authorities.get(receipt) ?? snapshotDirectoryReceipt(receipt);
|
|
72
|
+
}
|
|
73
|
+
function rememberReceipt(receipt, authority) {
|
|
74
|
+
authorities.set(receipt, authority);
|
|
75
|
+
identities.set(receipt.identity, authority);
|
|
76
|
+
return receipt;
|
|
77
|
+
}
|
|
78
|
+
export function ownDirectoryReceipt(receipt) {
|
|
79
|
+
return receiptFromAuthority(snapshotDirectoryReceipt(receipt));
|
|
80
|
+
}
|
|
81
|
+
export function copyRetainedDirectoryReceipt(receipt) {
|
|
82
|
+
const authority = directoryReceiptAuthority(receipt);
|
|
83
|
+
return receiptFromAuthority(authority);
|
|
84
|
+
}
|
|
85
|
+
function receiptFromAuthority(authority) {
|
|
86
|
+
return rememberReceipt({
|
|
87
|
+
path: authority.path,
|
|
88
|
+
realPath: authority.realPath,
|
|
89
|
+
identity: Object.assign(Object.create(fs.Stats.prototype), authority.metadata),
|
|
90
|
+
}, authority);
|
|
91
|
+
}
|
|
92
|
+
// The caller already admitted the exact observation and its paths.
|
|
93
|
+
export function createDirectoryReceiptFromIdentity(pathname, realPath, exactStat) {
|
|
94
|
+
const identity = Object.freeze({ dev: exactStat.dev, ino: exactStat.ino });
|
|
95
|
+
return receiptFromAuthority(Object.freeze({
|
|
96
|
+
path: pathname,
|
|
97
|
+
realPath,
|
|
98
|
+
identity,
|
|
99
|
+
// Project metadata from the admitted observation, never a fresh pathname stat.
|
|
100
|
+
metadata: snapshotDirectoryMetadata(exactStat, identity),
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
export function createDirectoryReceiptSync(directoryPath, label, canonicalize = realpathSync) {
|
|
104
|
+
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
105
|
+
const pathname = resolvePathPreservingWindowsRoot(directoryPath);
|
|
106
|
+
assertNoWindowsPathAlias(pathname, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
107
|
+
const exact = inspectDirectoryIdentitySync(pathname);
|
|
108
|
+
const operationPath = pathForWindowsFilesystem(pathname);
|
|
109
|
+
const realPath = canonicalize(operationPath);
|
|
110
|
+
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
111
|
+
return createDirectoryReceiptFromIdentity(pathname, realPath, exact);
|
|
112
|
+
}
|
|
113
|
+
export function assertDirectoryReceiptCurrentSync(receipt, label, canonicalize = realpathSync) {
|
|
114
|
+
const authority = directoryReceiptAuthority(receipt);
|
|
115
|
+
assertNoWindowsPathAlias(authority.path, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
116
|
+
assertNoWindowsPathAlias(authority.realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
117
|
+
inspectDirectoryIdentitySync(authority.path, authority.identity);
|
|
118
|
+
const realPath = canonicalize(pathForWindowsFilesystem(authority.path));
|
|
119
|
+
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
120
|
+
if (realPath !== authority.realPath) {
|
|
121
|
+
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${authority.path}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import fs, { type FileHandle } from "node:fs/promises";
|
|
3
|
+
import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
|
|
4
|
+
export declare function readErrorCode(error: unknown): unknown;
|
|
5
|
+
export declare function hasErrorCode(error: unknown, expected: string): boolean;
|
|
6
|
+
type OwnedPathCleanupStatus = "removed" | "name-absent" | "preserved";
|
|
7
|
+
export declare function removeOwnedPath(params: {
|
|
8
|
+
fsModule: Pick<typeof fs, "lstat" | "unlink">;
|
|
9
|
+
pathname: string;
|
|
10
|
+
identity?: BigIntStats;
|
|
11
|
+
}): Promise<OwnedPathCleanupStatus>;
|
|
12
|
+
export declare function cleanupPinnedFilePath(params: {
|
|
13
|
+
pathname: string;
|
|
14
|
+
handle: Pick<FileHandle, "fd">;
|
|
15
|
+
identity?: BigIntStats;
|
|
16
|
+
parentGuard: AnyAsyncDirectoryGuard;
|
|
17
|
+
throwOnCleanupError?: boolean;
|
|
18
|
+
}): Promise<OwnedPathCleanupStatus>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=file-cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-cleanup.d.ts","sourceRoot":"","sources":["../src/file-cleanup.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAI9F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASrD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,KAAK,sBAAsB,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAEtE,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAoBlC;AAGD,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAgClC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import syncFs, {} from "node:fs";
|
|
2
|
+
import fs, {} from "node:fs/promises";
|
|
3
|
+
import { assertAsyncDirectoryGuard } from "./directory-guard.js";
|
|
4
|
+
import { FsSafeError } from "./errors.js";
|
|
5
|
+
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
6
|
+
export function readErrorCode(error) {
|
|
7
|
+
if ((typeof error !== "object" || error === null) && typeof error !== "function") {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return Reflect.get(error, "code");
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function hasErrorCode(error, expected) {
|
|
18
|
+
return readErrorCode(error) === expected;
|
|
19
|
+
}
|
|
20
|
+
export async function removeOwnedPath(params) {
|
|
21
|
+
if (!params.identity)
|
|
22
|
+
return "preserved";
|
|
23
|
+
try {
|
|
24
|
+
const current = params.fsModule === fs
|
|
25
|
+
? syncFs.lstatSync(params.pathname, { bigint: true })
|
|
26
|
+
: await params.fsModule.lstat(params.pathname, { bigint: true });
|
|
27
|
+
if (current.isSymbolicLink() ||
|
|
28
|
+
!current.isFile() ||
|
|
29
|
+
current.nlink !== 1n ||
|
|
30
|
+
!sameFileIdentityForCleanup(current, params.identity)) {
|
|
31
|
+
return "preserved";
|
|
32
|
+
}
|
|
33
|
+
await params.fsModule.unlink(params.pathname);
|
|
34
|
+
return "removed";
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
38
|
+
return "name-absent";
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Borrowed handle: the caller retains it until guarded cleanup finishes.
|
|
43
|
+
export async function cleanupPinnedFilePath(params) {
|
|
44
|
+
if (!params.identity)
|
|
45
|
+
return "preserved";
|
|
46
|
+
try {
|
|
47
|
+
const guard = params.parentGuard;
|
|
48
|
+
if ([guard.stat.dev, guard.stat.ino].some((value) => typeof value === "number" && !Number.isSafeInteger(value)))
|
|
49
|
+
return "preserved";
|
|
50
|
+
await assertAsyncDirectoryGuard(guard);
|
|
51
|
+
const parent = syncFs.lstatSync(guard.dir, { bigint: true });
|
|
52
|
+
if (parent.isSymbolicLink() || !parent.isDirectory() ||
|
|
53
|
+
!sameFileIdentityForCleanup(parent, guard.stat))
|
|
54
|
+
return "preserved";
|
|
55
|
+
const opened = syncFs.fstatSync(params.handle.fd, { bigint: true });
|
|
56
|
+
if (!opened.isFile() || opened.nlink !== 1n ||
|
|
57
|
+
!sameFileIdentityForCleanup(opened, params.identity))
|
|
58
|
+
return "preserved";
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// Unverifiable authority must preserve the path and the original write failure.
|
|
62
|
+
if (params.throwOnCleanupError &&
|
|
63
|
+
!(error instanceof FsSafeError && error.category === "policy") &&
|
|
64
|
+
!hasErrorCode(error, "ENOENT") && !hasErrorCode(error, "ENOTDIR") &&
|
|
65
|
+
!hasErrorCode(error, "ELOOP"))
|
|
66
|
+
throw error;
|
|
67
|
+
return "preserved";
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return await removeOwnedPath({
|
|
71
|
+
fsModule: fs,
|
|
72
|
+
pathname: params.pathname,
|
|
73
|
+
identity: params.identity,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (params.throwOnCleanupError)
|
|
78
|
+
throw error;
|
|
79
|
+
return "preserved";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type SameFileContentsOptions = {
|
|
2
|
+
maxBytes?: number;
|
|
3
|
+
};
|
|
4
|
+
/** Compares borrowed regular files from offset zero without moving or closing either descriptor. */
|
|
5
|
+
export declare function sameFileContentsSync(leftFd: number, rightFd: number, options?: SameFileContentsOptions): boolean;
|
|
6
|
+
//# sourceMappingURL=file-contents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-contents.d.ts","sourceRoot":"","sources":["../src/file-contents.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,oGAAoG;AACpG,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAgCT"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { normalizeMaxBytes } from "./byte-budget.js";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { readFileWindowFullySync } from "./positional-read.js";
|
|
5
|
+
/** Compares borrowed regular files from offset zero without moving or closing either descriptor. */
|
|
6
|
+
export function sameFileContentsSync(leftFd, rightFd, options = {}) {
|
|
7
|
+
const maxBytes = normalizeMaxBytes(options.maxBytes) ?? Infinity;
|
|
8
|
+
const limit = Math.min(maxBytes, Number.MAX_SAFE_INTEGER);
|
|
9
|
+
const tooLarge = () => new FsSafeError("too-large", `file comparison exceeds ${limit} bytes`);
|
|
10
|
+
for (const fd of [leftFd, rightFd]) {
|
|
11
|
+
const stat = fs.fstatSync(fd, { bigint: true });
|
|
12
|
+
if (!stat.isFile()) {
|
|
13
|
+
throw new FsSafeError("not-file", "file comparison requires regular-file descriptors");
|
|
14
|
+
}
|
|
15
|
+
if (stat.size > BigInt(limit))
|
|
16
|
+
throw tooLarge();
|
|
17
|
+
}
|
|
18
|
+
const left = Buffer.allocUnsafe(Math.min(1024 * 1024, Math.max(1, limit)));
|
|
19
|
+
const right = Buffer.allocUnsafe(left.length);
|
|
20
|
+
let position = 0;
|
|
21
|
+
while (true) {
|
|
22
|
+
const length = Math.min(left.length, limit - position);
|
|
23
|
+
if (length === 0) {
|
|
24
|
+
// Probe EOF at the limit without forming an unsafe exclusive window end.
|
|
25
|
+
const leftExtra = fs.readSync(leftFd, left, 0, 1, position);
|
|
26
|
+
const rightExtra = fs.readSync(rightFd, right, 0, 1, position);
|
|
27
|
+
if (leftExtra !== 0 || rightExtra !== 0)
|
|
28
|
+
throw tooLarge();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const leftRead = readFileWindowFullySync(leftFd, left.subarray(0, length), position);
|
|
32
|
+
const rightRead = readFileWindowFullySync(rightFd, right.subarray(0, length), position);
|
|
33
|
+
if (leftRead !== rightRead || !left.subarray(0, leftRead).equals(right.subarray(0, rightRead))) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (leftRead < length)
|
|
37
|
+
return true;
|
|
38
|
+
position += leftRead;
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/file-hash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAKnE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAwD3B;
|
|
1
|
+
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAKnE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAwD3B;AA8DD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AA2DD,8FAA8F;AAC9F,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAMlB"}
|
package/dist/file-hash.js
CHANGED
|
@@ -99,14 +99,20 @@ async function hashPath(filePath, options) {
|
|
|
99
99
|
catch (error) {
|
|
100
100
|
rethrowHashOpenError(error);
|
|
101
101
|
}
|
|
102
|
+
let completed = false;
|
|
102
103
|
try {
|
|
103
104
|
options.signal?.throwIfAborted();
|
|
104
105
|
const opened = await inspectFileIdentity(() => hashDescriptorIdentity(handle.fd), before);
|
|
105
106
|
await inspectFileIdentity(() => hashPathIdentity(filePath, true), opened);
|
|
106
|
-
|
|
107
|
+
const result = await hashFileHandle(handle, getNativeBinding(), options);
|
|
108
|
+
completed = true;
|
|
109
|
+
return result;
|
|
107
110
|
}
|
|
108
111
|
finally {
|
|
109
|
-
|
|
112
|
+
if (completed)
|
|
113
|
+
await handle.close();
|
|
114
|
+
else
|
|
115
|
+
await handle.close().catch(() => undefined);
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
export async function sha256File(input, options = {}) {
|
|
@@ -157,17 +163,23 @@ function hashPathSync(filePath, options) {
|
|
|
157
163
|
catch (error) {
|
|
158
164
|
rethrowHashOpenError(error);
|
|
159
165
|
}
|
|
166
|
+
let completed = false;
|
|
160
167
|
try {
|
|
161
168
|
options.signal?.throwIfAborted();
|
|
162
169
|
const opened = inspectFileIdentitySync(() => hashDescriptorIdentity(fd), before);
|
|
163
170
|
inspectFileIdentitySync(() => hashPathIdentity(filePath, true), opened);
|
|
164
|
-
|
|
171
|
+
const result = hashDescriptorSync(fd, options);
|
|
172
|
+
completed = true;
|
|
173
|
+
return result;
|
|
165
174
|
}
|
|
166
175
|
finally {
|
|
167
176
|
try {
|
|
168
177
|
fsSync.closeSync(fd);
|
|
169
178
|
}
|
|
170
|
-
catch {
|
|
179
|
+
catch (error) {
|
|
180
|
+
if (completed)
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
171
183
|
}
|
|
172
184
|
}
|
|
173
185
|
/** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock-sync-root-acquire.d.ts","sourceRoot":"","sources":["../src/file-lock-sync-root-acquire.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-lock-sync-root-acquire.d.ts","sourceRoot":"","sources":["../src/file-lock-sync-root-acquire.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAiD3C,wBAAgB,2BAA2B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClF,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,0BAA0B,CAAC,QAAQ,CAAC,EAClD,QAAQ,EAAE,IAAI,GACb,kBAAkB,CAgWpB"}
|
|
@@ -7,6 +7,7 @@ import { computeSidecarLockDelayMs, isTransientLockFileDenial, maxTransientLockD
|
|
|
7
7
|
import { serializeSidecarLockPayload, } from "./sidecar-lock-reclaim.js";
|
|
8
8
|
import { createSuppressedError } from "./suppressed-error.js";
|
|
9
9
|
import { sleepSync } from "./timing.js";
|
|
10
|
+
import { assertSynchronousCallbackResult } from "./mutation-authority.js";
|
|
10
11
|
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
11
12
|
import { admitFileLockSyncRootPath, assertFileLockSyncRootPathsCurrent, captureFileLockSyncRootAuthority, normalizeFileLockSyncTargetWithRoot, } from "./file-lock-sync-root.js";
|
|
12
13
|
import { cleanupCreatedRootSyncLock, FileLockSyncRootArbitrationCollision, tryReuseCurrentRootSyncHeldLock, } from "./file-lock-sync-root-arbitration.js";
|
|
@@ -299,6 +300,7 @@ export function acquireFileLockSyncWithRoot(targetPath, inputOptions, lockRoot)
|
|
|
299
300
|
nowMs,
|
|
300
301
|
heldByThisProcess: false,
|
|
301
302
|
}]);
|
|
303
|
+
assertSynchronousCallbackResult(reclaim, "shouldReclaim");
|
|
302
304
|
assertAdmission();
|
|
303
305
|
if (!fileLockSyncRootSnapshotStillCurrent(lockRootPath, current)) {
|
|
304
306
|
throw new FsSafeError("path-mismatch", "sidecar changed during reclaim policy callback");
|
|
@@ -324,6 +326,7 @@ export function acquireFileLockSyncWithRoot(targetPath, inputOptions, lockRoot)
|
|
|
324
326
|
raw: snapshot.raw,
|
|
325
327
|
payload: snapshot.payload,
|
|
326
328
|
}]);
|
|
329
|
+
assertSynchronousCallbackResult(approved, "shouldRemoveStaleLock");
|
|
327
330
|
assertAdmission();
|
|
328
331
|
assertOwnedReclaimGuardCurrent();
|
|
329
332
|
if (approved) {
|
|
@@ -28,7 +28,6 @@ export declare function getRootSyncHeldLocks(): Map<string, RootSyncHeldLock>;
|
|
|
28
28
|
export declare function ensureRootSyncExitCleanupRegistered(): void;
|
|
29
29
|
export declare function verifyRootSyncHeldLock(held: RootSyncHeldLock): boolean;
|
|
30
30
|
export declare function createRootSyncHeldLockHandle(held: RootSyncHeldLock): FileLockSyncHandle;
|
|
31
|
-
export declare function
|
|
32
|
-
export declare function withRootSyncHeldLockHandle<T>(lock: FileLockSyncHandle, fn: () => T): T;
|
|
31
|
+
export declare function withSyncHeldLockHandle<T>(lock: FileLockSyncHandle, fn: () => T): T;
|
|
33
32
|
export {};
|
|
34
33
|
//# sourceMappingURL=file-lock-sync-root-held.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock-sync-root-held.d.ts","sourceRoot":"","sources":["../src/file-lock-sync-root-held.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,6BAA6B,CAAC;AAQrC,KAAK,4BAA4B,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,UAAU,CAAC;AAE1F,KAAK,iCAAiC,GAAG;IACvC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAOF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,oBAAoB,CAAC,EAAE,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC9D,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,4BAA4B,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,yBAAyB,CAAC;IACzC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,EAAE,2BAA2B,CAAC;IACzC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CACxB,CAAC;AAQF,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,oBAAoB,EAC9B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACxC,eAAe,CAAC,EAAE,2BAA2B,GAC5C,wBAAwB,GAAG,IAAI,CAmCjC;AAED,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAQpE;AA+KD,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAWtE;AAsHD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CAqBvF;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"file-lock-sync-root-held.d.ts","sourceRoot":"","sources":["../src/file-lock-sync-root-held.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,6BAA6B,CAAC;AAQrC,KAAK,4BAA4B,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,UAAU,CAAC;AAE1F,KAAK,iCAAiC,GAAG;IACvC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAOF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,oBAAoB,CAAC,EAAE,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC9D,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,4BAA4B,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,yBAAyB,CAAC;IACzC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,EAAE,2BAA2B,CAAC;IACzC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CACxB,CAAC;AAQF,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,oBAAoB,EAC9B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACxC,eAAe,CAAC,EAAE,2BAA2B,GAC5C,wBAAwB,GAAG,IAAI,CAmCjC;AAED,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAQpE;AA+KD,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAWtE;AAsHD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CAqBvF;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,IAAI,EAAE,kBAAkB,EACxB,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAoBH"}
|