@openclaw/fs-safe 0.18.0 → 0.18.2

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.
Files changed (183) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/advanced.d.ts +4 -2
  3. package/dist/advanced.js +1 -1
  4. package/dist/archive-durability.d.ts +1 -1
  5. package/dist/archive-merge.js +0 -1
  6. package/dist/archive-parser.wasm +0 -0
  7. package/dist/archive-plan.d.ts +64 -3
  8. package/dist/archive-plan.js +55 -1
  9. package/dist/archive-staging.d.ts +1 -2
  10. package/dist/archive-staging.js +0 -2
  11. package/dist/archive-tar-wasm.d.ts +1 -1
  12. package/dist/archive-zip-directory.d.ts +1 -1
  13. package/dist/archive-zip-entry.d.ts +1 -1
  14. package/dist/archive-zip-entry.js +1 -1
  15. package/dist/archive-zip-preflight.d.ts +0 -2
  16. package/dist/archive-zip-preflight.js +0 -1
  17. package/dist/archive.d.ts +14 -8
  18. package/dist/archive.js +127 -12
  19. package/dist/atomic.d.ts +3 -1
  20. package/dist/copy-publication.d.ts +1 -1
  21. package/dist/create.d.ts +16 -2
  22. package/dist/create.js +388 -2
  23. package/dist/creation-boundary.d.ts +60 -0
  24. package/dist/creation-boundary.js +371 -0
  25. package/dist/directory-guard.d.ts +0 -1
  26. package/dist/directory-guard.js +0 -3
  27. package/dist/directory-mode-node.d.ts +20 -2
  28. package/dist/directory-mode-node.js +89 -3
  29. package/dist/durability.d.ts +2 -1
  30. package/dist/file-lock-sync-acquisition.js +3 -3
  31. package/dist/file-lock-sync-root-acquire.js +2 -6
  32. package/dist/file-lock-sync-root-options.d.ts +2 -10
  33. package/dist/file-lock-sync-root.d.ts +2 -1
  34. package/dist/file-lock-sync-stale-admission.js +3 -10
  35. package/dist/file-lock-sync.d.ts +4 -21
  36. package/dist/file-store-boundary.d.ts +7 -0
  37. package/dist/file-store-boundary.js +58 -1
  38. package/dist/file-store-sync-write.js +2 -3
  39. package/dist/file-store.js +1 -6
  40. package/dist/guarded-mkdir.d.ts +1 -1
  41. package/dist/index.d.ts +9 -6
  42. package/dist/json-durable-queue-ownership.d.ts +1 -5
  43. package/dist/json-durable-queue-read.js +7 -3
  44. package/dist/json-durable-queue.d.ts +0 -1
  45. package/dist/json-durable-queue.js +1 -3
  46. package/dist/json-store.js +1 -2
  47. package/dist/json.js +1 -2
  48. package/dist/move-path.js +1 -2
  49. package/dist/native-operations.js +0 -1
  50. package/dist/native-pinned-write-windows.d.ts +1 -1
  51. package/dist/native-pinned-write-windows.js +1 -1
  52. package/dist/native-pinned-write.d.ts +1 -1
  53. package/dist/native-pinned-write.js +3 -6
  54. package/dist/native-policy-parent-windows.d.ts +1 -1
  55. package/dist/native-staged-file.d.ts +2 -2
  56. package/dist/native-staged-file.js +1 -2
  57. package/dist/permission-exec.d.ts +6 -0
  58. package/dist/permissions-public.d.ts +2 -1
  59. package/dist/permissions-windows.d.ts +3 -6
  60. package/dist/permissions.d.ts +3 -10
  61. package/dist/permissions.js +0 -1
  62. package/dist/pinned-mutation-admission.d.ts +1 -1
  63. package/dist/pinned-open.d.ts +1 -0
  64. package/dist/pinned-open.js +7 -2
  65. package/dist/pinned-operation.d.ts +1 -1
  66. package/dist/pinned-operation.js +3 -16
  67. package/dist/pinned-write-mode.js +1 -1
  68. package/dist/pinned-write-staged.js +1 -2
  69. package/dist/pinned-write.d.ts +0 -1
  70. package/dist/pinned-write.js +3 -5
  71. package/dist/private-producer-handoff-sync.d.ts +2 -12
  72. package/dist/private-producer-handoff-sync.js +1 -1
  73. package/dist/private-producer-handoff.d.ts +3 -3
  74. package/dist/private-temp-workspace.js +1 -2
  75. package/dist/publish-copy-stage.d.ts +1 -1
  76. package/dist/publish-file.d.ts +0 -1
  77. package/dist/publish-file.js +1 -2
  78. package/dist/replace-directory.js +6 -19
  79. package/dist/replace-file-copy-fallback.d.ts +1 -8
  80. package/dist/replace-file-descriptor.d.ts +3 -11
  81. package/dist/replace-file-descriptor.js +1 -1
  82. package/dist/replace-file-rename-policy.d.ts +1 -2
  83. package/dist/replace-file.d.ts +3 -3
  84. package/dist/replace-file.js +1 -1
  85. package/dist/retained-directory-replacement.d.ts +1 -0
  86. package/dist/retained-directory-replacement.js +1 -1
  87. package/dist/root-context.js +3 -7
  88. package/dist/root-create-input.d.ts +1 -1
  89. package/dist/root-directory-creation.d.ts +1 -1
  90. package/dist/root-directory-creation.js +1 -1
  91. package/dist/root-directory-list.js +3 -7
  92. package/dist/root-entries.js +2 -2
  93. package/dist/root-errors.d.ts +4 -0
  94. package/dist/root-errors.js +5 -3
  95. package/dist/root-file-final-admission.d.ts +1 -1
  96. package/dist/root-file-final-admission.js +1 -6
  97. package/dist/root-file.js +2 -5
  98. package/dist/root-impl.d.ts +0 -5
  99. package/dist/root-impl.js +22 -21
  100. package/dist/root-move-noreplace.js +4 -10
  101. package/dist/root-observed-path.js +2 -4
  102. package/dist/root-options.d.ts +1 -1
  103. package/dist/root-path-existing.js +2 -3
  104. package/dist/root-path-stat.js +2 -4
  105. package/dist/root-public-types.d.ts +11 -0
  106. package/dist/root-remove-identity.js +2 -4
  107. package/dist/root-remove.js +2 -4
  108. package/dist/root-write-admission.d.ts +1 -1
  109. package/dist/root-write-admission.js +3 -7
  110. package/dist/root-write-complete-parent.js +2 -3
  111. package/dist/root-write-verification.d.ts +1 -1
  112. package/dist/root.d.ts +2 -6
  113. package/dist/secret-file.js +11 -17
  114. package/dist/secret-read-policy.js +1 -1
  115. package/dist/secure-file-windows.js +4 -9
  116. package/dist/secure-temp-dir.js +2 -7
  117. package/dist/secure-temp-repair.js +5 -1
  118. package/dist/sidecar-lock-acquire.js +8 -11
  119. package/dist/sidecar-lock-policy.d.ts +1 -0
  120. package/dist/sidecar-lock-policy.js +7 -0
  121. package/dist/sidecar-lock-reclaim.js +5 -6
  122. package/dist/sidecar-lock-stale-admission.js +2 -6
  123. package/dist/sidecar-lock-types.d.ts +22 -16
  124. package/dist/stat-observation.js +3 -10
  125. package/dist/strict-file-identity.d.ts +2 -0
  126. package/dist/strict-file-identity.js +6 -6
  127. package/dist/temp-target.js +3 -8
  128. package/dist/temp-workspace-admission.js +7 -15
  129. package/dist/temp-workspace-child-admission.d.ts +16 -2
  130. package/dist/temp-workspace-child-admission.js +65 -3
  131. package/dist/temp-workspace-descriptor.d.ts +1 -1
  132. package/dist/temp-workspace-descriptor.js +2 -4
  133. package/dist/text-atomic.js +1 -1
  134. package/dist/windows-owner.d.ts +3 -6
  135. package/dist/windows-owner.js +1 -1
  136. package/dist/windows-path-alias.d.ts +1 -0
  137. package/dist/windows-path-alias.js +5 -0
  138. package/dist/windows-security-bridge.cs +0 -2
  139. package/docs/advanced.md +3 -1
  140. package/docs/secret-file.md +13 -0
  141. package/docs/sidecar-lock.md +6 -0
  142. package/docs/store.md +6 -0
  143. package/docs/temp.md +8 -1
  144. package/docs/writing.md +5 -0
  145. package/package.json +8 -8
  146. package/dist/archive-native.d.ts +0 -10
  147. package/dist/archive-native.js +0 -73
  148. package/dist/archive-options.d.ts +0 -30
  149. package/dist/archive-policy.d.ts +0 -21
  150. package/dist/archive-policy.js +0 -38
  151. package/dist/archive-tar-inspect.d.ts +0 -6
  152. package/dist/archive-tar-inspect.js +0 -62
  153. package/dist/archive-tar.d.ts +0 -18
  154. package/dist/archive-tar.js +0 -21
  155. package/dist/create-directory.d.ts +0 -18
  156. package/dist/create-directory.js +0 -111
  157. package/dist/create-file-async.d.ts +0 -6
  158. package/dist/create-file-async.js +0 -121
  159. package/dist/create-file.d.ts +0 -7
  160. package/dist/create-file.js +0 -179
  161. package/dist/creation-darwin.d.ts +0 -5
  162. package/dist/creation-darwin.js +0 -70
  163. package/dist/creation-file-state.d.ts +0 -18
  164. package/dist/creation-file-state.js +0 -118
  165. package/dist/creation-path.d.ts +0 -22
  166. package/dist/creation-path.js +0 -83
  167. package/dist/creation-permissions.d.ts +0 -18
  168. package/dist/creation-permissions.js +0 -115
  169. package/dist/directory-mode-owner.d.ts +0 -20
  170. package/dist/directory-mode-owner.js +0 -78
  171. package/dist/file-store-copy-source.d.ts +0 -4
  172. package/dist/file-store-copy-source.js +0 -31
  173. package/dist/file-store-limit.d.ts +0 -1
  174. package/dist/file-store-limit.js +0 -7
  175. package/dist/file-store-path.d.ts +0 -2
  176. package/dist/file-store-path.js +0 -30
  177. package/dist/standalone-publication-path.d.ts +0 -1
  178. package/dist/standalone-publication-path.js +0 -6
  179. package/dist/temp-workspace-identity.d.ts +0 -14
  180. package/dist/temp-workspace-identity.js +0 -41
  181. package/dist/temp-workspace-permissions.d.ts +0 -3
  182. package/dist/temp-workspace-permissions.js +0 -32
  183. /package/dist/{archive-options.js → root-public-types.js} +0 -0
@@ -1,13 +1,3 @@
1
- import { type BigIntStats } from "node:fs";
2
1
  import { type OwnedFileDescriptorSync } from "./create-owned-file.js";
3
- export declare function handoffCreatedFileSync(params: {
4
- source: OwnedFileDescriptorSync;
5
- sourcePath: string;
6
- targetPath: string;
7
- identity: BigIntStats;
8
- assertSourceParent: () => void;
9
- assertTargetParent: () => void;
10
- assertBeforeMutation?: () => void;
11
- verifyDescriptor?: (fd: number, path: string, links: number) => void;
12
- onPublished?: () => void;
13
- }): OwnedFileDescriptorSync;
2
+ import { type CreatedHandoffParams } from "./private-producer-handoff.js";
3
+ export declare function handoffCreatedFileSync(params: CreatedHandoffParams<OwnedFileDescriptorSync, void>): OwnedFileDescriptorSync;
@@ -1,4 +1,4 @@
1
- import fs, {} from "node:fs";
1
+ import fs from "node:fs";
2
2
  import { ownFileDescriptorSync } from "./create-owned-file.js";
3
3
  import { assertSynchronousCallbackResult } from "./mutation-authority.js";
4
4
  import { assertInitialSource, FileHandoff, inspectLinkedFile, } from "./private-producer-handoff.js";
@@ -18,11 +18,11 @@ type HandoffPaths = HandoffParents & {
18
18
  type ProducerHandoffParams = HandoffPaths & {
19
19
  readWrite: boolean;
20
20
  };
21
- type CreatedHandoffParams = HandoffPaths & {
22
- source: FileHandle;
21
+ export type CreatedHandoffParams<TSource = FileHandle, TVerification = Promise<void>> = HandoffPaths & {
22
+ source: TSource;
23
23
  identity: BigIntStats;
24
24
  assertBeforeMutation?: () => void;
25
- verifyDescriptor?: (fd: number, path: string, links: number) => Promise<void>;
25
+ verifyDescriptor?: (fd: number, path: string, links: number) => TVerification;
26
26
  onPublished?: () => void;
27
27
  };
28
28
  export declare function assertInitialSource(stat: BigIntStats): void;
@@ -12,9 +12,8 @@ import { registerTempPathForExit, } from "./temp-cleanup.js";
12
12
  import { TempWorkspaceCleanupCapability, TempWorkspaceCleanupOwner, throwTempWorkspaceCreationFailure, } from "./temp-workspace-owner.js";
13
13
  import { assertNoWindowsPathAlias, hasWindowsPathAlias, } from "./windows-path-alias.js";
14
14
  import { TempWorkspaceRetainedChild } from "./temp-workspace-descriptor.js";
15
- import { validateInitialTempWorkspaceChild } from "./temp-workspace-child-admission.js";
15
+ import { validateInitialTempWorkspaceChild, validateTempWorkspaceDirMode } from "./temp-workspace-child-admission.js";
16
16
  import { admitTempWorkspaceRoot, admitTempWorkspaceRootSync, } from "./temp-workspace-admission.js";
17
- import { validateTempWorkspaceDirMode } from "./temp-workspace-permissions.js";
18
17
  function resolveTempWorkspaceCleanupSafety(value) {
19
18
  if (value === undefined || value === "compatible")
20
19
  return "compatible";
@@ -1,6 +1,6 @@
1
1
  import { type BigIntStats } from "node:fs";
2
2
  import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
3
- import type { PublishedWriteIdentity } from "./pinned-write.js";
3
+ import type { PublishedWriteIdentity } from "./pinned-write-types.js";
4
4
  export declare function publishCopyStage(params: {
5
5
  temporaryPath: string;
6
6
  targetPath: string;
@@ -2,7 +2,6 @@ import { type BigIntStats, type Stats } from "node:fs";
2
2
  import { type DirectoryReceipt, type DirectorySyncOutcome } from "./directory-durability.js";
3
3
  import { type FileIdentityStat } from "./file-identity.js";
4
4
  import { type PublishFileExclusiveSyncFailurePolicy } from "./publish-file-failure.js";
5
- export type { PublishFileExclusiveCleanup, PublishFileExclusiveDirectorySyncFailure, PublishFileExclusiveFailureDetails, PublishFileExclusiveFailurePhase, PublishFileExclusiveSyncFailurePolicy, } from "./publish-file-failure.js";
6
5
  export type PublishFileExclusiveStrategy = "link-or-copy" | "link-required" | "rename-noreplace";
7
6
  export type PublishFileExclusiveResult = {
8
7
  method: "hardlink" | "exclusive-copy" | "rename-noreplace";
@@ -12,9 +12,8 @@ import { getNativeBinding, requireNativeBinding } from "./native.js";
12
12
  import { captureNativeFdClose } from "./native-binding.js";
13
13
  import { resolveReadOpenFlags } from "./read-open-flags.js";
14
14
  import { directorySyncFailure, publicationFailure, rememberCreatedTarget, } from "./publish-file-failure.js";
15
- import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
15
+ import { admitStandalonePublicationPath, assertNoWindowsPathAlias } from "./windows-path-alias.js";
16
16
  import { getFsSafeTestHooks } from "./test-hooks.js";
17
- import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
18
17
  import { hasErrorCode } from "./file-cleanup.js";
19
18
  import { writeAllToFile } from "./write-file-handle.js";
20
19
  const HARDLINK_FALLBACK_CODES = new Set(["EPERM", "EXDEV", "ENOTSUP", "EOPNOTSUPP", "ENOSYS"]);
@@ -6,11 +6,10 @@ import { assertSyncDirectoryGuard, createAsyncDirectoryGuard, inspectDirectoryId
6
6
  import { FsSafeError } from "./errors.js";
7
7
  import { classifyNativeRenameFailure, NATIVE_RENAME_SOURCE_IDENTITY_MISMATCH, } from "./native-rename-outcome.js";
8
8
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
9
- import { RetainedDirectoryReplacement } from "./retained-directory-replacement.js";
9
+ import { copyOperationalCode, RetainedDirectoryReplacement } from "./retained-directory-replacement.js";
10
10
  import { assertSafePathPrefix } from "./safe-path-segment.js";
11
- import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
11
+ import { admitStandalonePublicationPath, assertNoWindowsPathAlias } from "./windows-path-alias.js";
12
12
  import { serializePathWrite } from "./write-queue.js";
13
- import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
14
13
  function distinctGuards(source, target) {
15
14
  return source === target ? [source] : [source, target];
16
15
  }
@@ -18,30 +17,18 @@ function assertGuardsCurrent(guards) {
18
17
  for (const guard of guards)
19
18
  assertSyncDirectoryGuard(guard);
20
19
  }
21
- function copyOperationalCode(target, source) {
22
- const code = source?.code;
23
- if (typeof code === "string") {
24
- Object.defineProperty(target, "code", { configurable: true, value: code });
25
- }
26
- }
27
- function replacementFailure(error, message, details) {
20
+ function replacementFailure(error, message, details, cause = error) {
28
21
  if (error instanceof FsSafeError) {
29
- return new FsSafeError(error.code, message, { cause: error, details });
22
+ return new FsSafeError(error.code, message, { cause, details });
30
23
  }
31
- const failure = new Error(message, { cause: error });
24
+ const failure = new Error(message, { cause });
32
25
  copyOperationalCode(failure, error);
33
26
  Object.defineProperty(failure, "details", { configurable: true, value: details });
34
27
  return failure;
35
28
  }
36
29
  function replacementAggregate(error, recoveryError, message, details) {
37
30
  const combined = new AggregateError([error, recoveryError], "directory replacement operation and recovery both failed");
38
- if (error instanceof FsSafeError) {
39
- return new FsSafeError(error.code, message, { cause: combined, details });
40
- }
41
- const failure = new Error(message, { cause: combined });
42
- copyOperationalCode(failure, error);
43
- Object.defineProperty(failure, "details", { configurable: true, value: details });
44
- return failure;
31
+ return replacementFailure(error, message, details, combined);
45
32
  }
46
33
  function isMissingDirectoryEntry(error) {
47
34
  return error?.code === "ENOENT";
@@ -24,15 +24,8 @@ export declare function copyFallbackReplace(params: {
24
24
  expectedSourceIdentity?: BigIntStats;
25
25
  sync: boolean;
26
26
  }): Promise<void>;
27
- export declare function copyFallbackReplaceSync(params: {
27
+ export declare function copyFallbackReplaceSync(params: Omit<Parameters<typeof copyFallbackReplace>[0], "fsModule"> & {
28
28
  fsModule: SyncFallbackFs;
29
- src: string;
30
- dest: string;
31
- destinationHardlinks?: ReplaceFileDestinationHardlinkPolicy;
32
- restore: ReplaceFileCopyFallbackRestorePolicy;
33
- maxRestoreBytes?: number;
34
- expectedSourceIdentity?: BigIntStats;
35
29
  fchmodSync?: (fd: number, mode: number) => void;
36
- sync: boolean;
37
30
  }): void;
38
31
  export {};
@@ -1,6 +1,6 @@
1
1
  import syncFs, { type BigIntStats } from "node:fs";
2
2
  import fs, { type FileHandle } from "node:fs/promises";
3
- import { type DirectoryModeOwner } from "./directory-mode-owner.js";
3
+ import { type DirectoryModeOwner } from "./directory-mode-node.js";
4
4
  type AsyncTempFileSystem = Pick<typeof fs, "lstat" | "open" | "writeFile">;
5
5
  type SyncTempFileSystem = Pick<typeof syncFs, "closeSync" | "fstatSync" | "fsyncSync" | "lstatSync" | "openSync" | "writeFileSync">;
6
6
  export type SyncFchmod = (fd: number, mode: number) => void;
@@ -12,11 +12,8 @@ export declare function pinDirectoryForMode(params: {
12
12
  /** Compatibility for best-effort directory modes; admission and close still fail closed. */
13
13
  ignoreChmodError?: boolean;
14
14
  }): Promise<DirectoryModeOwner | undefined>;
15
- export declare function applyDirectoryMode(params: {
16
- fsModule: AsyncTempFileSystem;
17
- dirPath: string;
15
+ export declare function applyDirectoryMode(params: Parameters<typeof pinDirectoryForMode>[0] & {
18
16
  mode: number;
19
- ignoreChmodError?: boolean;
20
17
  }): Promise<void>;
21
18
  export declare function applyDirectoryModeSync(params: {
22
19
  fsModule: SyncTempFileSystem;
@@ -35,14 +32,9 @@ export declare function writeTempFile(params: {
35
32
  handle: FileHandle;
36
33
  identity: BigIntStats;
37
34
  }>;
38
- export declare function writeTempFileSync(params: {
35
+ export declare function writeTempFileSync(params: Omit<Parameters<typeof writeTempFile>[0], "fsModule"> & {
39
36
  fsModule: SyncTempFileSystem;
40
- tempPath: string;
41
- content: string | Uint8Array;
42
- mode: number;
43
37
  fchmodSync?: SyncFchmod;
44
- sync: boolean;
45
- onIdentity?: (identity: BigIntStats) => void;
46
38
  }): {
47
39
  fd: number;
48
40
  identity: BigIntStats;
@@ -3,7 +3,7 @@ import syncFs, {} from "node:fs";
3
3
  import fs, {} from "node:fs/promises";
4
4
  import { FsSafeError } from "./errors.js";
5
5
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
6
- import { ownDirectoryMode } from "./directory-mode-owner.js";
6
+ import { ownDirectoryMode } from "./directory-mode-node.js";
7
7
  export async function syncDirectoryBestEffort(fsModule, dirPath) {
8
8
  let handle;
9
9
  try {
@@ -1,5 +1,4 @@
1
- import type { RenameIdentityPolicy } from "./pinned-write.js";
2
- export type { RenameIdentityPolicy };
1
+ import type { RenameIdentityPolicy } from "./pinned-write-types.js";
3
2
  export declare function validateRenameIdentity(policy: RenameIdentityPolicy | undefined): void;
4
3
  export declare function atomicExpectedContentHash(policy: RenameIdentityPolicy | undefined, content: string | Uint8Array): string | undefined;
5
4
  export declare function withAtomicRenameIdentityLock<T>(filePath: string, fn: () => Promise<T>): Promise<T>;
@@ -1,7 +1,7 @@
1
1
  import syncFs from "node:fs";
2
2
  import fs from "node:fs/promises";
3
- import { type ReplaceFileAtomicRestoreCleanup, type ReplaceFileAtomicRestoreFailureDetails, type ReplaceFileCopyFallbackRestorePolicy, type ReplaceFileDestinationHardlinkPolicy } from "./replace-file-copy-fallback.js";
4
- import { type RenameIdentityPolicy } from "./replace-file-rename-policy.js";
3
+ import type { RenameIdentityPolicy } from "./pinned-write-types.js";
4
+ import { type ReplaceFileCopyFallbackRestorePolicy, type ReplaceFileDestinationHardlinkPolicy } from "./replace-file-copy-fallback.js";
5
5
  export type ReplaceFileAtomicFileSystem = {
6
6
  promises: Pick<typeof fs, "mkdir" | "writeFile" | "rename" | "copyFile" | "unlink" | "rm" | "open" | "stat" | "lstat"> & {
7
7
  /** @deprecated Accepted for adapter compatibility but never called. */
@@ -13,7 +13,6 @@ export type ReplaceFileAtomicSyncFileSystem = Pick<typeof syncFs, "mkdirSync" |
13
13
  chmodSync?: typeof syncFs.chmodSync;
14
14
  fchmodSync?: typeof syncFs.fchmodSync;
15
15
  };
16
- export type { RenameIdentityPolicy, ReplaceFileAtomicRestoreCleanup, ReplaceFileAtomicRestoreFailureDetails, ReplaceFileCopyFallbackRestorePolicy, ReplaceFileDestinationHardlinkPolicy, };
17
16
  type ReplaceFileAtomicBaseOptions = {
18
17
  filePath: string;
19
18
  content: string | Uint8Array;
@@ -56,3 +55,4 @@ export type ReplaceFileAtomicResult = {
56
55
  export declare function replaceFileAtomic(options: ReplaceFileAtomicOptions): Promise<ReplaceFileAtomicResult>;
57
56
  export declare function replaceFileAtomicWithDirectorySync(options: ReplaceFileAtomicOptions, syncParent?: (directoryPath: string) => Promise<unknown>): Promise<ReplaceFileAtomicResult>;
58
57
  export declare function replaceFileAtomicSync(options: ReplaceFileAtomicSyncOptions): ReplaceFileAtomicResult;
58
+ export {};
@@ -9,7 +9,7 @@ import { inheritedRegularFileMode } from "./replace-file-mode.js";
9
9
  import { atomicExpectedContentHash, validateRenameIdentity, withAtomicRenameIdentityLock, withAtomicRenameIdentityLockSync, } from "./replace-file-rename-policy.js";
10
10
  import { AsyncAtomicTempOwner, SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
11
11
  import { assertSafePathPrefix } from "./safe-path-segment.js";
12
- import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
12
+ import { admitStandalonePublicationPath } from "./windows-path-alias.js";
13
13
  import { sleep, sleepSync } from "./timing.js";
14
14
  import { serializePathWrite } from "./write-queue.js";
15
15
  import { hasErrorCode, readErrorCode } from "./file-cleanup.js";
@@ -6,6 +6,7 @@ export type RetainedReplacementParent = Readonly<{
6
6
  realPath: string;
7
7
  identity: Pick<BigIntStats, "dev" | "ino">;
8
8
  }>;
9
+ export declare function copyOperationalCode(target: Error, source: unknown): void;
9
10
  export declare class RetainedDirectoryReplacement {
10
11
  #private;
11
12
  private constructor();
@@ -7,7 +7,7 @@ function helperUnavailable(message, cause) {
7
7
  ...(cause instanceof Error ? { cause } : {}),
8
8
  });
9
9
  }
10
- function copyOperationalCode(target, source) {
10
+ export function copyOperationalCode(target, source) {
11
11
  const code = source?.code;
12
12
  if (typeof code === "string") {
13
13
  Object.defineProperty(target, "code", { configurable: true, value: code });
@@ -7,7 +7,7 @@ import { sameFileIdentity } from "./file-identity.js";
7
7
  import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, } from "./path.js";
8
8
  import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath } from "./root-path.js";
9
9
  import { admitPathInsideRoot } from "./root-boundary.js";
10
- import { outsideWorkspaceError, rootPathChangedError } from "./root-errors.js";
10
+ import { errorCauseOptions, outsideWorkspaceError, rootPathChangedError } from "./root-errors.js";
11
11
  import { isDriveRelativePath } from "./safe-path-segment.js";
12
12
  import { realpathSync } from "./realpath.js";
13
13
  import { inspectFileIdentity } from "./strict-file-identity.js";
@@ -223,14 +223,10 @@ export async function resolvePathInRoot(root, relativePath, options) {
223
223
  throw error;
224
224
  }
225
225
  if (hasNodeErrorCode(error, "ENAMETOOLONG")) {
226
- throw new FsSafeError("invalid-path", "relative path is too long", {
227
- cause: error instanceof Error ? error : undefined,
228
- });
226
+ throw new FsSafeError("invalid-path", "relative path is too long", errorCauseOptions(error));
229
227
  }
230
228
  const code = options?.aliasErrorCode ?? "outside-workspace";
231
- throw new FsSafeError(code, code === "path-alias" ? "path alias escape blocked" : "file is outside workspace root", {
232
- cause: error instanceof Error ? error : undefined,
233
- });
229
+ throw new FsSafeError(code, code === "path-alias" ? "path alias escape blocked" : "file is outside workspace root", errorCauseOptions(error));
234
230
  }
235
231
  return { rootReal: root.rootReal, rootWithSep: root.rootWithSep, resolved };
236
232
  }
@@ -1,4 +1,4 @@
1
- import type { PinnedWriteInput } from "./pinned-write.js";
1
+ import type { PinnedWriteInput } from "./pinned-write-types.js";
2
2
  import type { RootCreateOptions, RootCreateStreamOptions, RootWriteOptions } from "./root-options.js";
3
3
  declare const exclusiveSidecarCreate: unique symbol;
4
4
  export declare const sidecarExclusiveCreate: Readonly<{
@@ -1,4 +1,4 @@
1
- import type { PinnedWriteMutationAdmission } from "./pinned-write.js";
1
+ import type { PinnedWriteMutationAdmission } from "./pinned-write-types.js";
2
2
  import { type RootContext } from "./root-context.js";
3
3
  /**
4
4
  * Complete the common Root.mkdir case with one exact existing parent. This is
@@ -1,7 +1,7 @@
1
1
  import fsSync, {} from "node:fs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { assertPrivateDirectory } from "./creation-permissions.js";
4
+ import { assertPrivateDirectory } from "./creation-boundary.js";
5
5
  import { assertAsyncDirectoryGuard, assertDirectoryIdentitySync, assertSyncDirectoryGuard, createAsyncDirectoryGuard, inspectDirectoryIdentitySync, } from "./directory-guard.js";
6
6
  import { FsSafeError } from "./errors.js";
7
7
  import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
@@ -7,7 +7,7 @@ import { FsSafeError } from "./errors.js";
7
7
  import { inspectNativeDirectoryObservation, isNativeDirectoryObservationGuard, } from "./native-directory-observation.js";
8
8
  import { isNotFoundPathError } from "./path.js";
9
9
  import { assertRootIdentityCurrent } from "./root-context.js";
10
- import { rootPathChangedError } from "./root-errors.js";
10
+ import { errorCauseOptions, rootPathChangedError } from "./root-errors.js";
11
11
  import { admitPathInsideRoot } from "./root-boundary.js";
12
12
  import { createSuppressedError } from "./suppressed-error.js";
13
13
  import { realpathSync } from "./realpath.js";
@@ -50,9 +50,7 @@ export async function createRootDirectoryObservationGuard(root, directory) {
50
50
  function directoryChangedError(error) {
51
51
  if (error instanceof FsSafeError && error.code === "path-mismatch")
52
52
  return error;
53
- return new FsSafeError("path-mismatch", "directory changed during operation", {
54
- cause: error instanceof Error ? error : undefined,
55
- });
53
+ return new FsSafeError("path-mismatch", "directory changed during operation", errorCauseOptions(error));
56
54
  }
57
55
  export async function assertRootDirectoryObservationGuard(root, guard) {
58
56
  if (isNativeDirectoryObservationGuard(guard)) {
@@ -162,9 +160,7 @@ export function assertRootPathObservationReceiptCurrent(root, receipt, finalTarg
162
160
  }
163
161
  function normalizeDirectoryError(error) {
164
162
  if (isNotFoundPathError(error)) {
165
- return new FsSafeError("not-found", "directory not found", {
166
- cause: error instanceof Error ? error : undefined,
167
- });
163
+ return new FsSafeError("not-found", "directory not found", errorCauseOptions(error));
168
164
  }
169
165
  return error;
170
166
  }
@@ -1,5 +1,5 @@
1
1
  import { FsSafeError } from "./errors.js";
2
- import { validatePinnedOperationPayload } from "./pinned-operation.js";
2
+ import { validatePinnedRelativePath } from "./pinned-operation.js";
3
3
  import { resolvePathInRoot } from "./root-context.js";
4
4
  import { openRootDirectoryListing } from "./root-directory-list.js";
5
5
  import { readSymlinkResolution } from "./root-symlink-policy.js";
@@ -13,7 +13,7 @@ export async function* entriesInRoot(root, relativePath, options) {
13
13
  throw new TypeError(`invalid root entries order: ${String(options.order)}`);
14
14
  }
15
15
  options.signal?.throwIfAborted();
16
- validatePinnedOperationPayload({ relativePath });
16
+ validatePinnedRelativePath(relativePath);
17
17
  const resolved = await resolvePathInRoot(root, relativePath, {
18
18
  ...readSymlinkResolution(options.symlinks),
19
19
  resolveCanonical: true,
@@ -10,3 +10,7 @@ export declare function normalizePinnedPathError(error: unknown, details?: FsSaf
10
10
  export declare function normalizeRemoveGuardError(error: unknown, details?: FsSafeErrorDetails): Error;
11
11
  export declare function normalizeRemovePathError(error: unknown, details?: FsSafeErrorDetails): Error;
12
12
  export declare function throwFsSafeReadError(error: unknown, label: string): never;
13
+ /** Existing boundaries retain only Error instances as their cause. */
14
+ export declare function errorCauseOptions(error: unknown): {
15
+ cause: Error | undefined;
16
+ };
@@ -41,9 +41,7 @@ export function normalizePinnedWriteError(error) {
41
41
  const message = code
42
42
  ? `${PINNED_WRITE_ERRNO_MESSAGES.get(code) ?? "filesystem write failed"} (${code})`
43
43
  : "path is not a regular file under root";
44
- return new FsSafeError("invalid-path", message, {
45
- cause: error instanceof Error ? error : undefined,
46
- });
44
+ return new FsSafeError("invalid-path", message, errorCauseOptions(error));
47
45
  }
48
46
  export function normalizePinnedPathError(error, details) {
49
47
  if (error instanceof FsSafeError) {
@@ -88,3 +86,7 @@ export function throwFsSafeReadError(error, label) {
88
86
  }
89
87
  throw error;
90
88
  }
89
+ /** Existing boundaries retain only Error instances as their cause. */
90
+ export function errorCauseOptions(error) {
91
+ return { cause: error instanceof Error ? error : undefined };
92
+ }
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs";
2
- import type { PinnedOpenSyncFinalAdmission } from "./pinned-open.js";
2
+ import { type PinnedOpenSyncFinalAdmission } from "./pinned-open.js";
3
3
  export type RootFileAdmissionFs = Pick<typeof fs, "lstatSync" | "realpathSync">;
4
4
  type ExactIdentity = Readonly<{
5
5
  dev: bigint;
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import { FsSafeError } from "./errors.js";
3
+ import { isExpectedPathError } from "./pinned-open.js";
3
4
  import { realpathSync } from "./realpath.js";
4
5
  import { admitPathInsideRoot } from "./root-boundary.js";
5
6
  import { hardlinkedPathNotAllowedError } from "./root-errors.js";
@@ -8,12 +9,6 @@ import { assertNoWindowsPathAlias, pathForWindowsFilesystem } from "./windows-pa
8
9
  function rootIdentityMismatch(cause) {
9
10
  return new FsSafeError("path-mismatch", "canonical root directory identity changed or could not be verified", cause === undefined ? {} : { cause });
10
11
  }
11
- function isExpectedPathError(error) {
12
- const code = typeof error === "object" && error !== null && "code" in error
13
- ? String(error.code)
14
- : "";
15
- return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
16
- }
17
12
  function inspectCanonicalRoot(ioFs, rootPath, expected) {
18
13
  try {
19
14
  assertNoWindowsPathAlias(rootPath);
package/dist/root-file.js CHANGED
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { resolveRootPathSyncWithCanonicalRootObservation, resolveRootPathWithCanonicalRootObservation, } from "./root-path.js";
4
4
  import { readSymlinkResolution } from "./root-symlink-policy.js";
5
- import { openPinnedFileSync, } from "./pinned-open.js";
5
+ import { isExpectedPathError, openPinnedFileSync, } from "./pinned-open.js";
6
6
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
7
7
  import { FsSafeError } from "./errors.js";
8
8
  import { createRootFileFinalAdmission, observeCanonicalRoot, } from "./root-file-final-admission.js";
@@ -198,10 +198,7 @@ function snapshotAsyncResolution(params, absolutePath) {
198
198
  function toRootObservationError(error) {
199
199
  if (error instanceof FsSafeError)
200
200
  return toBoundaryValidationError(error);
201
- const code = typeof error === "object" && error !== null && "code" in error
202
- ? String(error.code)
203
- : "";
204
- if (code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP") {
201
+ if (isExpectedPathError(error)) {
205
202
  return { ok: false, reason: "path", error };
206
203
  }
207
204
  return { ok: false, reason: "io", error };
@@ -7,13 +7,8 @@ import { type RootContext } from "./root-context.js";
7
7
  import type { DirEntry, PathStat } from "./types.js";
8
8
  import { type RootWalkEntry, type RootWalkOptions } from "./root-walk.js";
9
9
  import { type RootAppendOptions, type RootCopyOptions, type RootCopySource, type RootCreateJsonOptions, type RootCreateOptions, type RootCreateStreamOptions, type RootDefaults, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions } from "./root-options.js";
10
- export type { HardlinkPolicy, RootAppendOptions, RootCopyOptions, RootCopySource, RootCreateJsonOptions, RootCreateOptions, RootCreateStreamOptions, RootDefaults, RootMkdirOptions, RootMoveOptions, RootOpenOptions, RootOpenWritableOptions, RootOptions, RootReadOptions, RootRemoveOptions, RootWriteJsonOptions, RootWriteOptions, WritableOpenMode, } from "./root-options.js";
11
10
  export { DEFAULT_ROOT_MAX_BYTES } from "./root-options.js";
12
- export type { DenyMutationPolicy } from "./deny-mutations.js";
13
- export type { RenameIdentityPolicy } from "./pinned-write.js";
14
- export type { MutationSymlinkPolicy, SymlinkPolicy } from "./root-symlink-policy.js";
15
11
  export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
16
- export type { ReadResult } from "./read-opened-file.js";
17
12
  export type OpenResult = {
18
13
  handle: FileHandle;
19
14
  containment: ContainmentGuarantee;
package/dist/root-impl.js CHANGED
@@ -56,7 +56,7 @@ import fs from "node:fs/promises";
56
56
  import path from "node:path";
57
57
  import { normalizeMaxBytes } from "./byte-budget.js";
58
58
  import { assertCopySourceCurrent, resolveFileCopyCloneMode } from "./copy-file-input.js";
59
- import { assertPrivateFileCreationAvailable, resolveCreationPermissions } from "./creation-permissions.js";
59
+ import { assertPrivateFileCreationAvailable, resolveCreationPermissions } from "./creation-boundary.js";
60
60
  import { assertAsyncDirectoryGuard, assertSyncDirectoryGuard, createAsyncDirectoryGuard, createNearestExistingDirectoryGuard } from "./directory-guard.js";
61
61
  import { FsSafeError } from "./errors.js";
62
62
  import { syncDirectoryBestEffort } from "./directory-durability.js";
@@ -68,7 +68,7 @@ import { openedPathResolutionError, recordExclusiveCreateFailure, recordOpenedFi
68
68
  import { runPinnedWriteHelper, runPinnedWriteWithRenamePolicy } from "./pinned-write.js";
69
69
  import { preparePinnedWriteMutationAdmission, snapshotPinnedMutationPolicy } from "./pinned-mutation-admission.js";
70
70
  import { getNativeBinding } from "./native.js";
71
- import { validatePinnedOperationPayload } from "./pinned-operation.js";
71
+ import { validatePinnedRelativePath } from "./pinned-operation.js";
72
72
  import { PATH_ALIAS_POLICIES } from "./path-policy.js";
73
73
  import { assertNoNulPathInput, hasNodeErrorCode, isNotFoundPathError, isSymlinkOpenError, } from "./path.js";
74
74
  import { readOpenedFileSafely } from "./read-opened-file.js";
@@ -86,7 +86,7 @@ import { statResolvedPathInRoot } from "./root-path-stat.js";
86
86
  import { entriesInRoot } from "./root-entries.js";
87
87
  import { assertMoveMutationAllowed } from "./root-move-preflight.js";
88
88
  import { assertRootIdentityCurrent, assertRootIdentityCurrentSync, assertValidRootDestinationPath, assertValidRootRelativePath, ensureTrailingSep, expandRelativePathWithHome, resolvePathInRoot, resolveRootContext, rootRelativeReadPath, } from "./root-context.js";
89
- import { fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, normalizePinnedWriteError, outsideWorkspaceError, } from "./root-errors.js";
89
+ import { errorCauseOptions, fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, normalizePinnedWriteError, outsideWorkspaceError, } from "./root-errors.js";
90
90
  import { getFsSafeTestHooks } from "./test-hooks.js";
91
91
  import { stringifyJsonDocument } from "./json-stringify.js";
92
92
  import { walkRoot } from "./root-walk.js";
@@ -304,6 +304,9 @@ export class RootHandle {
304
304
  mode: this.defaults.mode,
305
305
  renameIdentity: this.defaults.renameIdentity,
306
306
  ...this.mutationOptions(options),
307
+ atomic: undefined,
308
+ private: undefined,
309
+ strictFileSync: undefined,
307
310
  relativePath,
308
311
  data,
309
312
  durable: options.durable ?? this.defaults.durable ?? true,
@@ -364,18 +367,19 @@ export class RootHandle {
364
367
  }
365
368
  async stat(relativePath) {
366
369
  assertValidRootRelativePath(relativePath);
367
- validatePinnedOperationPayload({ relativePath });
370
+ validatePinnedRelativePath(relativePath);
368
371
  return await statPathFallback(this.context, relativePath);
369
372
  }
370
373
  async list(relativePath, options = {}) {
371
374
  assertValidRootRelativePath(relativePath);
372
- validatePinnedOperationPayload({ relativePath });
375
+ validatePinnedRelativePath(relativePath);
373
376
  return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
374
377
  }
375
378
  async move(fromRelative, toRelative, options = {}) {
376
379
  assertValidRootRelativePath(fromRelative);
377
380
  assertValidRootDestinationPath(toRelative);
378
- validatePinnedOperationPayload({ from: fromRelative, to: toRelative });
381
+ validatePinnedRelativePath(fromRelative);
382
+ validatePinnedRelativePath(toRelative);
379
383
  const mutationOptions = this.mutationOptions(options);
380
384
  const { assertBeforeMutation } = mutationOptions;
381
385
  const { denyMutations, mutationSymlinks } = snapshotPinnedMutationPolicy(mutationOptions.denyMutations, mutationOptions.mutationSymlinks) ?? {};
@@ -407,7 +411,7 @@ export class RootHandle {
407
411
  rootReal: this.context.rootReal,
408
412
  stat: relative => this.stat(relative),
409
413
  list: async (relative, listingOptions) => {
410
- validatePinnedOperationPayload({ relativePath: relative });
414
+ validatePinnedRelativePath(relative);
411
415
  const resolved = await resolvePinnedPathInRoot(this.context, { relativePath: relative, allowRoot: true });
412
416
  return await openRootDirectoryListing(this.context, resolved.resolved, listingOptions);
413
417
  },
@@ -503,7 +507,6 @@ async function openWritableFileInRoot(root, params, options) {
503
507
  relativePath: params.relativePath, guardedTarget, mkdir: params.mkdir,
504
508
  assertBeforeMutation: params.assertBeforeMutation,
505
509
  }) : undefined;
506
- const mutationAdmission = prepared?.mutationAdmission;
507
510
  const preparedParent = prepared?.preparedParent;
508
511
  let ioPath = prepared?.targetPath ?? (params.mkdir === false ? resolved :
509
512
  await prepareRootWriteTarget(root, resolved, params.assertBeforeMutation));
@@ -763,7 +766,7 @@ async function appendFileInRoot(root, params) {
763
766
  }
764
767
  }
765
768
  async function removePathInRoot(root, params) {
766
- validatePinnedOperationPayload({ relativePath: params.relativePath });
769
+ validatePinnedRelativePath(params.relativePath);
767
770
  const removalReceipts = params.recursive ? undefined : new RemovalPathReceipts();
768
771
  const resolved = await resolvePinnedPathInRoot(root, {
769
772
  relativePath: params.relativePath,
@@ -783,7 +786,7 @@ async function removePathInRoot(root, params) {
783
786
  }
784
787
  async function mkdirPathInRoot(root, params) {
785
788
  const privateMode = resolveCreationPermissions(params, true).private;
786
- validatePinnedOperationPayload({ relativePath: params.relativePath });
789
+ validatePinnedRelativePath(params.relativePath);
787
790
  const policy = params.denyMutations === undefined && params.mutationSymlinks === undefined
788
791
  ? undefined
789
792
  : snapshotPinnedMutationPolicy(params.denyMutations, params.mutationSymlinks);
@@ -862,7 +865,7 @@ async function commitPinnedWriteInRoot(root, pinned, params, input) {
862
865
  overwrite: params.overwrite,
863
866
  rejectFinalSymlink: params.mutationSymlinks !== undefined,
864
867
  input,
865
- maxBytes: params.maxBytes,
868
+ maxBytes: input.kind === "buffer" ? undefined : params.maxBytes,
866
869
  rootIdentity: root.rootIdentity,
867
870
  mutationAdmission: pinned.mutationAdmission,
868
871
  assertBeforeMutation: params.assertBeforeMutation,
@@ -892,9 +895,7 @@ async function commitPinnedWriteInRoot(root, pinned, params, input) {
892
895
  throw error;
893
896
  }
894
897
  if (params.overwrite === false && isAlreadyExistsError(error)) {
895
- throw new FsSafeError("already-exists", "file already exists", {
896
- cause: error instanceof Error ? error : undefined,
897
- });
898
+ throw new FsSafeError("already-exists", "file already exists", errorCauseOptions(error));
898
899
  }
899
900
  throw normalizePinnedWriteError(error);
900
901
  }
@@ -1173,9 +1174,7 @@ async function movePathFallback(root, params) {
1173
1174
  throw fileNotFoundError(error instanceof Error ? error : undefined);
1174
1175
  }
1175
1176
  if (hasNodeErrorCode(error, "EEXIST")) {
1176
- throw new FsSafeError("already-exists", "destination exists", {
1177
- cause: error instanceof Error ? error : undefined,
1178
- });
1177
+ throw new FsSafeError("already-exists", "destination exists", errorCauseOptions(error));
1179
1178
  }
1180
1179
  throw error;
1181
1180
  }
@@ -1193,7 +1192,11 @@ async function writeFileFallback(root, params) {
1193
1192
  const policy = snapshotPinnedMutationPolicy(params.denyMutations, params.mutationSymlinks);
1194
1193
  if (policy)
1195
1194
  params = { ...params, ...policy };
1196
- const { rootReal, resolved } = await resolveGuardedWritePathInRoot(root, params);
1195
+ const { rootReal, resolved } = await resolveGuardedWritePathInRoot(root, {
1196
+ relativePath: params.relativePath,
1197
+ denyMutations: params.denyMutations,
1198
+ mutationSymlinks: params.mutationSymlinks,
1199
+ });
1197
1200
  await withRootFallbackCompatibilityLock({
1198
1201
  rootPath: rootReal, rootIdentity: root.rootIdentity, targetPath: resolved, assertBeforeMutation: params.assertBeforeMutation,
1199
1202
  }, async ({ targetPath, ...binding }) => await writeFileFallbackUnlocked(root, { ...params, ...binding }, targetPath));
@@ -1422,9 +1425,7 @@ async function writeMissingFileFallback(root, params, expectedWritePath) {
1422
1425
  if (verifyingPublication)
1423
1426
  throw err;
1424
1427
  if (hasNodeErrorCode(err, "EEXIST")) {
1425
- throw new FsSafeError("already-exists", "file already exists", {
1426
- cause: err instanceof Error ? err : undefined,
1427
- });
1428
+ throw new FsSafeError("already-exists", "file already exists", errorCauseOptions(err));
1428
1429
  }
1429
1430
  throw err;
1430
1431
  }