@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,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): DirectoryAuthority;
|
|
18
|
+
export declare function ownDirectoryReceipt(receipt: DirectoryReceipt): 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;AA8BD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,kBAAkB,CAEvF;AAQD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAE/E;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAGxF;AA0BD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,WAAW,GACrB,gBAAgB,CAOlB;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,127 @@
|
|
|
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] = field === "dev" || field === "ino" ? Number(identity[field]) : stat[field];
|
|
57
|
+
}
|
|
58
|
+
for (const field of timeFields)
|
|
59
|
+
metadata[`${field}Ms`] = stat[`${field}Ms`];
|
|
60
|
+
return Object.freeze(metadata);
|
|
61
|
+
}
|
|
62
|
+
export function directoryReceiptAuthority(receipt) {
|
|
63
|
+
return authorities.get(receipt) ?? snapshotDirectoryReceipt(receipt);
|
|
64
|
+
}
|
|
65
|
+
function rememberReceipt(receipt, authority) {
|
|
66
|
+
authorities.set(receipt, authority);
|
|
67
|
+
identities.set(receipt.identity, authority);
|
|
68
|
+
return receipt;
|
|
69
|
+
}
|
|
70
|
+
export function ownDirectoryReceipt(receipt) {
|
|
71
|
+
return receiptFromAuthority(snapshotDirectoryReceipt(receipt));
|
|
72
|
+
}
|
|
73
|
+
export function copyRetainedDirectoryReceipt(receipt) {
|
|
74
|
+
const authority = directoryReceiptAuthority(receipt);
|
|
75
|
+
return receiptFromAuthority(authority);
|
|
76
|
+
}
|
|
77
|
+
function receiptFromAuthority(authority) {
|
|
78
|
+
return rememberReceipt({
|
|
79
|
+
path: authority.path,
|
|
80
|
+
realPath: authority.realPath,
|
|
81
|
+
identity: Object.assign(Object.create(fs.Stats.prototype), authority.metadata),
|
|
82
|
+
}, authority);
|
|
83
|
+
}
|
|
84
|
+
function numericDirectoryMetadata(exact) {
|
|
85
|
+
// Keep metadata and authority from one observation. A second pathname stat
|
|
86
|
+
// can describe a replacement even when a later fence sees the original again.
|
|
87
|
+
const metadata = {};
|
|
88
|
+
for (const field of numericFields) {
|
|
89
|
+
metadata[field] = Number(exact[field]);
|
|
90
|
+
}
|
|
91
|
+
for (const field of timeFields) {
|
|
92
|
+
const nanoseconds = exact[`${field}Ns`];
|
|
93
|
+
const remainder = ((nanoseconds % 1000000000n) + 1000000000n) % 1000000000n;
|
|
94
|
+
metadata[`${field}Ms`] = Number((nanoseconds - remainder) / 1000000000n) * 1_000 + Number(remainder) / 1_000_000;
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze(metadata);
|
|
97
|
+
}
|
|
98
|
+
// The caller already admitted the exact observation and its paths.
|
|
99
|
+
export function createDirectoryReceiptFromIdentity(pathname, realPath, exactStat) {
|
|
100
|
+
return receiptFromAuthority(Object.freeze({
|
|
101
|
+
path: pathname,
|
|
102
|
+
realPath,
|
|
103
|
+
identity: Object.freeze({ dev: exactStat.dev, ino: exactStat.ino }),
|
|
104
|
+
metadata: numericDirectoryMetadata(exactStat),
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
export function createDirectoryReceiptSync(directoryPath, label, canonicalize = realpathSync) {
|
|
108
|
+
assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
109
|
+
const pathname = resolvePathPreservingWindowsRoot(directoryPath);
|
|
110
|
+
assertNoWindowsPathAlias(pathname, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
111
|
+
const exact = inspectDirectoryIdentitySync(pathname);
|
|
112
|
+
const operationPath = pathForWindowsFilesystem(pathname);
|
|
113
|
+
const realPath = canonicalize(operationPath);
|
|
114
|
+
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
115
|
+
return createDirectoryReceiptFromIdentity(pathname, realPath, exact);
|
|
116
|
+
}
|
|
117
|
+
export function assertDirectoryReceiptCurrentSync(receipt, label, canonicalize = realpathSync) {
|
|
118
|
+
const authority = directoryReceiptAuthority(receipt);
|
|
119
|
+
assertNoWindowsPathAlias(authority.path, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
|
|
120
|
+
assertNoWindowsPathAlias(authority.realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
121
|
+
inspectDirectoryIdentitySync(authority.path, authority.identity);
|
|
122
|
+
const realPath = canonicalize(pathForWindowsFilesystem(authority.path));
|
|
123
|
+
assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
|
|
124
|
+
if (realPath !== authority.realPath) {
|
|
125
|
+
throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${authority.path}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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 hasErrorCode(error: unknown, expected: string): boolean;
|
|
5
|
+
type OwnedPathCleanupStatus = "removed" | "name-absent" | "preserved";
|
|
6
|
+
export declare function removeOwnedPath(params: {
|
|
7
|
+
fsModule: Pick<typeof fs, "lstat" | "unlink">;
|
|
8
|
+
pathname: string;
|
|
9
|
+
identity?: BigIntStats;
|
|
10
|
+
}): Promise<OwnedPathCleanupStatus>;
|
|
11
|
+
export declare function cleanupPinnedFilePath(params: {
|
|
12
|
+
pathname: string;
|
|
13
|
+
handle: Pick<FileHandle, "fd">;
|
|
14
|
+
identity?: BigIntStats;
|
|
15
|
+
parentGuard: AnyAsyncDirectoryGuard;
|
|
16
|
+
throwOnCleanupError?: boolean;
|
|
17
|
+
}): Promise<OwnedPathCleanupStatus>;
|
|
18
|
+
export {};
|
|
19
|
+
//# 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,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStE;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,78 @@
|
|
|
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 hasErrorCode(error, expected) {
|
|
7
|
+
if ((typeof error !== "object" || error === null) && typeof error !== "function") {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return Reflect.get(error, "code") === expected;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function removeOwnedPath(params) {
|
|
18
|
+
if (!params.identity)
|
|
19
|
+
return "preserved";
|
|
20
|
+
try {
|
|
21
|
+
const current = params.fsModule === fs
|
|
22
|
+
? syncFs.lstatSync(params.pathname, { bigint: true })
|
|
23
|
+
: await params.fsModule.lstat(params.pathname, { bigint: true });
|
|
24
|
+
if (current.isSymbolicLink() ||
|
|
25
|
+
!current.isFile() ||
|
|
26
|
+
current.nlink !== 1n ||
|
|
27
|
+
!sameFileIdentityForCleanup(current, params.identity)) {
|
|
28
|
+
return "preserved";
|
|
29
|
+
}
|
|
30
|
+
await params.fsModule.unlink(params.pathname);
|
|
31
|
+
return "removed";
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
35
|
+
return "name-absent";
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Borrowed handle: the caller retains it until guarded cleanup finishes.
|
|
40
|
+
export async function cleanupPinnedFilePath(params) {
|
|
41
|
+
if (!params.identity)
|
|
42
|
+
return "preserved";
|
|
43
|
+
try {
|
|
44
|
+
const guard = params.parentGuard;
|
|
45
|
+
if ([guard.stat.dev, guard.stat.ino].some((value) => typeof value === "number" && !Number.isSafeInteger(value)))
|
|
46
|
+
return "preserved";
|
|
47
|
+
await assertAsyncDirectoryGuard(guard);
|
|
48
|
+
const parent = syncFs.lstatSync(guard.dir, { bigint: true });
|
|
49
|
+
if (parent.isSymbolicLink() || !parent.isDirectory() ||
|
|
50
|
+
!sameFileIdentityForCleanup(parent, guard.stat))
|
|
51
|
+
return "preserved";
|
|
52
|
+
const opened = syncFs.fstatSync(params.handle.fd, { bigint: true });
|
|
53
|
+
if (!opened.isFile() || opened.nlink !== 1n ||
|
|
54
|
+
!sameFileIdentityForCleanup(opened, params.identity))
|
|
55
|
+
return "preserved";
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Unverifiable authority must preserve the path and the original write failure.
|
|
59
|
+
if (params.throwOnCleanupError &&
|
|
60
|
+
!(error instanceof FsSafeError && error.category === "policy") &&
|
|
61
|
+
!hasErrorCode(error, "ENOENT") && !hasErrorCode(error, "ENOTDIR") &&
|
|
62
|
+
!hasErrorCode(error, "ELOOP"))
|
|
63
|
+
throw error;
|
|
64
|
+
return "preserved";
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return await removeOwnedPath({
|
|
68
|
+
fsModule: fs,
|
|
69
|
+
pathname: params.pathname,
|
|
70
|
+
identity: params.identity,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (params.throwOnCleanupError)
|
|
75
|
+
throw error;
|
|
76
|
+
return "preserved";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -5,6 +5,8 @@ export type CopyFileHandleOptions = {
|
|
|
5
5
|
onChunk?: (chunk: Uint8Array) => void;
|
|
6
6
|
assertBeforeMutation?: () => void;
|
|
7
7
|
};
|
|
8
|
+
/** Copies caller-owned regular-file descriptors without moving either cursor. */
|
|
9
|
+
export declare function copyFileDescriptorSync(sourceFd: number, targetFd: number, options?: CopyFileHandleOptions): number;
|
|
8
10
|
/** Copies caller-owned regular files from offset zero without moving either cursor. */
|
|
9
11
|
export declare function copyFileHandle(source: FileHandle, target: FileHandle, options?: CopyFileHandleOptions): Promise<number>;
|
|
10
12
|
export declare function transferFileHandle(source: FileHandle, target: FileHandle | number, options: CopyFileHandleOptions & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAQF,iFAAiF;AACjF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,MAAM,CA6CR;AAgDD,uFAAuF;AACvF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAGD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
|
|
@@ -1,9 +1,64 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import { normalizeMaxBytes } from "./byte-budget.js";
|
|
2
3
|
import { FsSafeError } from "./errors.js";
|
|
3
4
|
import { assertSynchronousCallbackResult, composeMutationAssertions, rethrowMutationAuthorityError, } from "./mutation-authority.js";
|
|
4
5
|
import { writeAllToFile } from "./write-file-handle.js";
|
|
5
|
-
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
6
|
+
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
7
|
const NO_CALLBACK_ARGUMENTS = Object.freeze([]);
|
|
8
|
+
function createTransferBuffer(sizeHint, limit) {
|
|
9
|
+
return Buffer.allocUnsafe(Math.min(512 * 1024, Math.max(64 * 1024, sizeHint), limit + 1));
|
|
10
|
+
}
|
|
11
|
+
/** Copies caller-owned regular-file descriptors without moving either cursor. */
|
|
12
|
+
export function copyFileDescriptorSync(sourceFd, targetFd, options = {}) {
|
|
13
|
+
const signal = options.signal;
|
|
14
|
+
signal?.throwIfAborted();
|
|
15
|
+
const maxBytes = normalizeMaxBytes(options.maxBytes);
|
|
16
|
+
const onChunk = options.onChunk;
|
|
17
|
+
const assertBeforeMutation = options.assertBeforeMutation;
|
|
18
|
+
const sourceStat = inspectFileIdentitySync(() => {
|
|
19
|
+
signal?.throwIfAborted();
|
|
20
|
+
return fs.fstatSync(sourceFd, { bigint: true });
|
|
21
|
+
});
|
|
22
|
+
if (!sourceStat.isFile())
|
|
23
|
+
throw new FsSafeError("not-file", "copy source descriptor must be a regular file");
|
|
24
|
+
if (maxBytes !== undefined && Number.isFinite(maxBytes) && sourceStat.size > BigInt(maxBytes)) {
|
|
25
|
+
throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
|
|
26
|
+
}
|
|
27
|
+
const targetStat = inspectFileIdentitySync(() => {
|
|
28
|
+
signal?.throwIfAborted();
|
|
29
|
+
return fs.fstatSync(targetFd, { bigint: true });
|
|
30
|
+
});
|
|
31
|
+
if (!targetStat.isFile())
|
|
32
|
+
throw new FsSafeError("not-file", "copy target descriptor must be a regular file");
|
|
33
|
+
if (sourceStat.dev === targetStat.dev && sourceStat.ino === targetStat.ino) {
|
|
34
|
+
throw new FsSafeError("path-alias", "copy source and target descriptors refer to the same file");
|
|
35
|
+
}
|
|
36
|
+
const limit = maxBytes ?? Infinity;
|
|
37
|
+
const buffer = createTransferBuffer(Number(sourceStat.size), limit);
|
|
38
|
+
let position = 0;
|
|
39
|
+
while (true) {
|
|
40
|
+
signal?.throwIfAborted();
|
|
41
|
+
const bytesRead = fs.readSync(sourceFd, buffer, 0, Math.min(buffer.length, limit - position + 1), position);
|
|
42
|
+
if (bytesRead > limit - position) {
|
|
43
|
+
throw new FsSafeError("too-large", `file exceeds limit of ${limit} bytes`);
|
|
44
|
+
}
|
|
45
|
+
if (bytesRead === 0)
|
|
46
|
+
return position;
|
|
47
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
48
|
+
assertSynchronousCallbackResult(onChunk?.(chunk), "onChunk");
|
|
49
|
+
let offset = 0;
|
|
50
|
+
while (offset < bytesRead) {
|
|
51
|
+
signal?.throwIfAborted();
|
|
52
|
+
assertSynchronousCallbackResult(assertBeforeMutation?.(), "assertBeforeMutation");
|
|
53
|
+
signal?.throwIfAborted();
|
|
54
|
+
const written = fs.writeSync(targetFd, chunk, offset, bytesRead - offset, position + offset);
|
|
55
|
+
if (written <= 0)
|
|
56
|
+
throw new FsSafeError("helper-failed", "file write made no progress");
|
|
57
|
+
offset += written;
|
|
58
|
+
}
|
|
59
|
+
position += bytesRead;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
7
62
|
function defineReceiverValue(receiver, key, value) {
|
|
8
63
|
Object.defineProperty(receiver, key, {
|
|
9
64
|
value, enumerable: true, writable: true, configurable: true,
|
|
@@ -110,7 +165,7 @@ export function transferFileHandle(source, target, options) {
|
|
|
110
165
|
}
|
|
111
166
|
async function transferFileHandleCore(source, target, sizeHint, maxBytes, targetPosition, signal, onChunk, beforeWrite, callbackThis) {
|
|
112
167
|
const limit = maxBytes ?? Infinity;
|
|
113
|
-
const buffer =
|
|
168
|
+
const buffer = createTransferBuffer(sizeHint, limit);
|
|
114
169
|
let position = 0;
|
|
115
170
|
while (true) {
|
|
116
171
|
signal?.throwIfAborted();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-identity.d.ts","sourceRoot":"","sources":["../src/file-identity.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACtB,CAAC;AAsBF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAGlF;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAQT;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"file-identity.d.ts","sourceRoot":"","sources":["../src/file-identity.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACtB,CAAC;AAsBF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAGlF;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAQT;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAqBT"}
|
package/dist/file-identity.js
CHANGED
|
@@ -23,12 +23,26 @@ export function sameFileIdentity(left, right, platform = process.platform) {
|
|
|
23
23
|
!isStatValueProvablyDifferent(left.ino, right.ino, platform));
|
|
24
24
|
}
|
|
25
25
|
export function sameFileIdentityForCleanup(left, right, platform = process.platform) {
|
|
26
|
+
if (platform !== "win32") {
|
|
27
|
+
return sameStatValue(left.dev, right.dev) && sameStatValue(left.ino, right.ino);
|
|
28
|
+
}
|
|
26
29
|
// A zero Windows device or inode is unknown, not proof that a pathname still
|
|
27
30
|
// names the object we created. Cleanup must fail closed rather than delete a
|
|
28
31
|
// replacement that happens to share the known half of the identity.
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
// Capture each potentially adapter-backed observation once. This strengthens
|
|
33
|
+
// the fence against changing accessors; exact legacy getter traces are not a
|
|
34
|
+
// compatibility contract.
|
|
35
|
+
const leftDev = left.dev;
|
|
36
|
+
if (isZero(leftDev))
|
|
31
37
|
return false;
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const leftIno = left.ino;
|
|
39
|
+
if (isZero(leftIno))
|
|
40
|
+
return false;
|
|
41
|
+
const rightDev = right.dev;
|
|
42
|
+
if (isZero(rightDev))
|
|
43
|
+
return false;
|
|
44
|
+
const rightIno = right.ino;
|
|
45
|
+
if (isZero(rightIno))
|
|
46
|
+
return false;
|
|
47
|
+
return sameStatValue(leftDev, rightDev) && sameStatValue(leftIno, rightIno);
|
|
34
48
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SidecarLockSnapshot } from "./sidecar-lock-reclaim.js";
|
|
2
|
+
export type SyncHeldLock = {
|
|
3
|
+
fd: number | undefined;
|
|
4
|
+
lockPath: string;
|
|
5
|
+
normalizedTargetPath: string;
|
|
6
|
+
parsePayload?: (raw: string) => unknown;
|
|
7
|
+
refCount: number;
|
|
8
|
+
reentrantOwner?: string;
|
|
9
|
+
snapshot: SidecarLockSnapshot;
|
|
10
|
+
timer?: NodeJS.Timeout;
|
|
11
|
+
};
|
|
12
|
+
export declare function foreignSyncHeldLock(route: "raw" | "root", target: string): object | undefined;
|
|
13
|
+
export declare function getSyncHeldLocks(): Map<string, SyncHeldLock>;
|
|
14
|
+
export declare function getSyncLockAdmissions(): Map<string, object>;
|
|
15
|
+
export declare function syncLockTimeout(lockPath: string, normalizedTargetPath: string): Error;
|
|
16
|
+
export declare function defaultSyncShouldReclaim(snapshot: SidecarLockSnapshot, staleMs: number, nowMs: number): boolean;
|
|
17
|
+
export declare function syncReclaimGuardExists(reclaimGuardPath: string): boolean;
|
|
18
|
+
export declare function ensureSyncLockExitCleanupRegistered(cleanup: () => void, lockPath: string, normalizedTargetPath: string): void;
|
|
19
|
+
//# sourceMappingURL=file-lock-sync-admission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock-sync-admission.d.ts","sourceRoot":"","sources":["../src/file-lock-sync-admission.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,MAAM,MAAM,YAAY,GAAG;IACzB,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,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CACxB,CAAC;AAUF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7F;AAED,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAQ5D;AAED,wBAAgB,qBAAqB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ3D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,KAAK,CAMrF;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAIT;AAED,wBAAgB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAQxE;AAED,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,MAAM,IAAI,EACnB,QAAQ,EAAE,MAAM,EAChB,oBAAoB,EAAE,MAAM,GAC3B,IAAI,CAwCN"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { sidecarLockPayloadCreatedAtMs } from "./sidecar-lock-policy.js";
|
|
3
|
+
const SYNC_HELD_LOCKS_KEY = Symbol.for("fsSafe.syncSidecarLocks");
|
|
4
|
+
const SYNC_ADMISSIONS_KEY = Symbol.for("fsSafe.syncSidecarLockAdmissions");
|
|
5
|
+
const SYNC_CLEANUP_REGISTERED_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupRegistered");
|
|
6
|
+
const SYNC_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupHandler");
|
|
7
|
+
const SYNC_CLEANUP_REGISTRATION_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupRegistration");
|
|
8
|
+
const ROOT_SYNC_HELD_LOCKS_KEY = Symbol.for("fsSafe.syncRootSidecarLocks.v1");
|
|
9
|
+
export function foreignSyncHeldLock(route, target) {
|
|
10
|
+
const key = route === "raw" ? ROOT_SYNC_HELD_LOCKS_KEY : SYNC_HELD_LOCKS_KEY;
|
|
11
|
+
const state = globalThis;
|
|
12
|
+
return state[key]?.get(target);
|
|
13
|
+
}
|
|
14
|
+
export function getSyncHeldLocks() {
|
|
15
|
+
const globalWithState = globalThis;
|
|
16
|
+
if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
|
|
17
|
+
globalWithState[SYNC_HELD_LOCKS_KEY] = new Map();
|
|
18
|
+
}
|
|
19
|
+
return globalWithState[SYNC_HELD_LOCKS_KEY];
|
|
20
|
+
}
|
|
21
|
+
export function getSyncLockAdmissions() {
|
|
22
|
+
const globalWithState = globalThis;
|
|
23
|
+
if (!globalWithState[SYNC_ADMISSIONS_KEY]) {
|
|
24
|
+
globalWithState[SYNC_ADMISSIONS_KEY] = new Map();
|
|
25
|
+
}
|
|
26
|
+
return globalWithState[SYNC_ADMISSIONS_KEY];
|
|
27
|
+
}
|
|
28
|
+
export function syncLockTimeout(lockPath, normalizedTargetPath) {
|
|
29
|
+
return Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
|
|
30
|
+
code: "file_lock_timeout",
|
|
31
|
+
lockPath,
|
|
32
|
+
normalizedTargetPath,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function defaultSyncShouldReclaim(snapshot, staleMs, nowMs) {
|
|
36
|
+
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
37
|
+
if (createdAtMs !== null)
|
|
38
|
+
return nowMs - createdAtMs > staleMs;
|
|
39
|
+
return !snapshot.stat || nowMs - snapshot.stat.mtimeMs > staleMs;
|
|
40
|
+
}
|
|
41
|
+
export function syncReclaimGuardExists(reclaimGuardPath) {
|
|
42
|
+
try {
|
|
43
|
+
fs.lstatSync(reclaimGuardPath);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code === "ENOENT")
|
|
48
|
+
return false;
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function ensureSyncLockExitCleanupRegistered(cleanup, lockPath, normalizedTargetPath) {
|
|
53
|
+
const globalWithCleanup = globalThis;
|
|
54
|
+
if (globalWithCleanup[SYNC_CLEANUP_REGISTERED_KEY])
|
|
55
|
+
return;
|
|
56
|
+
if (globalWithCleanup[SYNC_CLEANUP_REGISTRATION_KEY]) {
|
|
57
|
+
// process.on() emits newListener synchronously. A nested synchronous
|
|
58
|
+
// acquisition cannot wait for this stack to finish registering.
|
|
59
|
+
throw syncLockTimeout(lockPath, normalizedTargetPath);
|
|
60
|
+
}
|
|
61
|
+
const registration = {};
|
|
62
|
+
globalWithCleanup[SYNC_CLEANUP_REGISTRATION_KEY] = registration;
|
|
63
|
+
try {
|
|
64
|
+
process.on("exit", cleanup);
|
|
65
|
+
globalWithCleanup[SYNC_CLEANUP_HANDLER_KEY] = cleanup;
|
|
66
|
+
globalWithCleanup[SYNC_CLEANUP_REGISTERED_KEY] = true;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (process.listeners("exit").includes(cleanup)) {
|
|
70
|
+
try {
|
|
71
|
+
process.off("exit", cleanup);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Reconcile the marker with the actual listener below.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (process.listeners("exit").includes(cleanup)) {
|
|
78
|
+
globalWithCleanup[SYNC_CLEANUP_HANDLER_KEY] = cleanup;
|
|
79
|
+
globalWithCleanup[SYNC_CLEANUP_REGISTERED_KEY] = true;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
if (globalWithCleanup[SYNC_CLEANUP_HANDLER_KEY] === cleanup) {
|
|
83
|
+
delete globalWithCleanup[SYNC_CLEANUP_HANDLER_KEY];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (globalWithCleanup[SYNC_CLEANUP_REGISTRATION_KEY] === registration) {
|
|
90
|
+
delete globalWithCleanup[SYNC_CLEANUP_REGISTRATION_KEY];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FileLockSyncAcquireOptions, FileLockSyncHandle } from "./file-lock-sync.js";
|
|
2
|
+
import type { Root } from "./root-impl.js";
|
|
3
|
+
export declare function acquireFileLockSyncWithRoot<TPayload extends Record<string, unknown>>(targetPath: string, inputOptions: FileLockSyncAcquireOptions<TPayload>, lockRoot: Root): FileLockSyncHandle;
|
|
4
|
+
//# sourceMappingURL=file-lock-sync-root-acquire.d.ts.map
|
|
@@ -0,0 +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;AAgD3C,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,CA8VpB"}
|