@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
@@ -9,7 +9,7 @@ import { isNotFoundPathError } from "./path.js";
9
9
  import { assertRootIdentityCurrent, assertRootIdentityCurrentSync } from "./root-context.js";
10
10
  import { resolveRootPathSync } from "./root-path.js";
11
11
  import { admitPathInsideRoot } from "./root-boundary.js";
12
- import { fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, outsideWorkspaceError, } from "./root-errors.js";
12
+ import { errorCauseOptions, fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, outsideWorkspaceError, } from "./root-errors.js";
13
13
  import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
14
14
  import { createSuppressedError } from "./suppressed-error.js";
15
15
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
@@ -44,17 +44,13 @@ function normalizeMoveError(error) {
44
44
  return fileNotFoundError(error instanceof Error ? error : undefined);
45
45
  }
46
46
  if (nativePrimitiveUnavailable(error)) {
47
- return new FsSafeError("helper-unavailable", "native no-replace move is unavailable", {
48
- cause: error instanceof Error ? error : undefined,
49
- });
47
+ return new FsSafeError("helper-unavailable", "native no-replace move is unavailable", errorCauseOptions(error));
50
48
  }
51
49
  return error;
52
50
  }
53
51
  function normalizeRenameNoReplaceError(error) {
54
52
  if (error?.code === "EINVAL") {
55
- return new FsSafeError("helper-unavailable", "native no-replace move is unavailable on this filesystem", {
56
- cause: error instanceof Error ? error : undefined,
57
- });
53
+ return new FsSafeError("helper-unavailable", "native no-replace move is unavailable on this filesystem", errorCauseOptions(error));
58
54
  }
59
55
  return normalizeMoveError(error);
60
56
  }
@@ -157,9 +153,7 @@ export async function movePathNoReplaceNative(root, params, paths) {
157
153
  }
158
154
  catch (error) {
159
155
  if (isAlreadyExistsError(error) || error?.code === "ENOTEMPTY") {
160
- throw new FsSafeError("already-exists", "destination exists", {
161
- cause: error instanceof Error ? error : undefined,
162
- });
156
+ throw new FsSafeError("already-exists", "destination exists", errorCauseOptions(error));
163
157
  }
164
158
  throw normalizeRenameNoReplaceError(error);
165
159
  }
@@ -7,7 +7,7 @@ import { admitPathInsideRoot } from "./root-boundary.js";
7
7
  import { assertRootPathObservationReceiptCurrent } from "./root-directory-list.js";
8
8
  import { RootPathObservationError, resolveRootPathWithObservation, } from "./root-path.js";
9
9
  import { createRootObservationGuard, ensureTrailingSep, expandRelativePathWithHome, } from "./root-context.js";
10
- import { fileNotFoundError, outsideWorkspaceError } from "./root-errors.js";
10
+ import { errorCauseOptions, fileNotFoundError, outsideWorkspaceError } from "./root-errors.js";
11
11
  import { getFsSafeTestHooks } from "./test-hooks.js";
12
12
  export async function resolvePinnedObservedPathInRoot(root, relativePath, kind) {
13
13
  const rootGuard = await createRootObservationGuard(root);
@@ -50,9 +50,7 @@ export async function resolvePinnedObservedPathInRoot(root, relativePath, kind)
50
50
  if (kind === "stat") {
51
51
  throw fileNotFoundError(error.error instanceof Error ? error.error : undefined);
52
52
  }
53
- throw new FsSafeError("not-found", "directory not found", {
54
- cause: error.error instanceof Error ? error.error : undefined,
55
- });
53
+ throw new FsSafeError("not-found", "directory not found", errorCauseOptions(error.error));
56
54
  }
57
55
  throw error.error;
58
56
  }
@@ -1,5 +1,5 @@
1
1
  import type { DenyMutationPolicy } from "./deny-mutations.js";
2
- import type { RenameIdentityPolicy } from "./pinned-write.js";
2
+ import type { RenameIdentityPolicy } from "./pinned-write-types.js";
3
3
  import type { MutationSymlinkPolicy, SymlinkPolicy } from "./root-symlink-policy.js";
4
4
  import type { CopyCloneMode } from "./copy-policy.js";
5
5
  import type { RootCopyPublicationReceipt } from "./copy-publication.js";
@@ -6,6 +6,7 @@ import { isNotFoundPathError, isPathInside, isSymlinkOpenError } from "./path.js
6
6
  import { realpathSync } from "./realpath.js";
7
7
  import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathFromBasePreservingWindowsRoot, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
8
8
  import { admitPathInsideRoot } from "./root-boundary.js";
9
+ import { errorCauseOptions } from "./root-errors.js";
9
10
  export function absolutePathWithRawSegments(candidate) {
10
11
  if (path.sep !== "\\") {
11
12
  if (path.isAbsolute(candidate))
@@ -144,9 +145,7 @@ export function resolveSymlinkHopPath(symlinkPath, mode, rejectUnresolved) {
144
145
  }
145
146
  catch (error) {
146
147
  if (isSymlinkOpenError(error) || (rejectUnresolved && isNotFoundPathError(error))) {
147
- throw new FsSafeError("symlink", "symlink path could not be resolved", {
148
- cause: error instanceof Error ? error : undefined,
149
- });
148
+ throw new FsSafeError("symlink", "symlink path could not be resolved", errorCauseOptions(error));
150
149
  }
151
150
  if (!isNotFoundPathError(error))
152
151
  throw error;
@@ -4,7 +4,7 @@ import { FsSafeError } from "./errors.js";
4
4
  import { fileObservation, recordFileObservationFailure } from "./file-observation.js";
5
5
  import { hasNodeErrorCode, isNotFoundPathError } from "./path.js";
6
6
  import { assertRootDirectoryObservationGuard, assertRootPathObservationReceiptCurrent, createRootDirectoryObservationGuard, pathStatFromStats, } from "./root-directory-list.js";
7
- import { fileNotFoundError } from "./root-errors.js";
7
+ import { errorCauseOptions, fileNotFoundError } from "./root-errors.js";
8
8
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
9
9
  import { assertStatObservationSync } from "./stat-observation.js";
10
10
  import { getFsSafeTestHooks } from "./test-hooks.js";
@@ -32,9 +32,7 @@ async function captureChangedObservedFile(error, pathname, before, observed, ass
32
32
  }
33
33
  }
34
34
  async function missingObservedFileError(cause, pathname, before, assertParents, proof) {
35
- const failure = new FsSafeError("path-mismatch", "file changed during operation", {
36
- cause: cause instanceof Error ? cause : undefined,
37
- });
35
+ const failure = new FsSafeError("path-mismatch", "file changed during operation", errorCauseOptions(cause));
38
36
  if (hasNodeErrorCode(cause, "ENOENT") && before?.isFile() && !before.isSymbolicLink() &&
39
37
  (before.nlink === 1 || before.nlink === 1n)) {
40
38
  try {
@@ -0,0 +1,11 @@
1
+ export type { OpenResult, WritableOpenResult, } from "./root-impl.js";
2
+ export type { HardlinkPolicy, RootAppendOptions, RootCopyOptions, RootCopySource, RootCreateJsonOptions, RootCreateOptions, RootCreateStreamOptions, RootDefaults, RootMkdirOptions, RootMoveOptions, RootOpenOptions, RootOpenWritableOptions, RootOptions, RootReadOptions, RootRemoveOptions, RootWriteJsonOptions, RootWriteOptions, WritableOpenMode, } from "./root-options.js";
3
+ export type { DenyMutationPolicy } from "./deny-mutations.js";
4
+ export type { RenameIdentityPolicy } from "./pinned-write-types.js";
5
+ export type { MutationSymlinkPolicy, SymlinkPolicy, } from "./root-symlink-policy.js";
6
+ export type { ReadResult } from "./read-opened-file.js";
7
+ export type { RootCopyPublicationReceipt } from "./copy-publication.js";
8
+ export type { CopyCloneMode } from "./copy-policy.js";
9
+ export type { RootEntriesOptions } from "./root-entries.js";
10
+ export type { ContainmentGuarantee } from "./containment.js";
11
+ export type { RootWalkDataEntry, RootWalkDataEntryKind, RootWalkDirectoryErrorBehavior, RootWalkEntry, RootWalkEntryFilter, RootWalkEntryFilterResult, RootWalkEntryKind, RootWalkLimitBehavior, RootWalkOptions, RootWalkSymlinkPolicy, } from "./root-walk.js";
@@ -2,12 +2,10 @@ import fsSync from "node:fs";
2
2
  import { inspectDirectoryIdentitySync } from "./directory-guard.js";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  import { realpathSync } from "./realpath.js";
5
- import { directoryComponentNotDirectoryError } from "./root-errors.js";
5
+ import { directoryComponentNotDirectoryError, errorCauseOptions } from "./root-errors.js";
6
6
  const MAX_SAFE_DIRECTORY_IDENTITY = BigInt(Number.MAX_SAFE_INTEGER);
7
7
  function identityMismatch(cause) {
8
- return new FsSafeError("path-mismatch", "removal ancestor identity changed or could not be verified", {
9
- cause: cause instanceof Error ? cause : undefined,
10
- });
8
+ return new FsSafeError("path-mismatch", "removal ancestor identity changed or could not be verified", errorCauseOptions(cause));
11
9
  }
12
10
  function safeIdentityNumber(value) {
13
11
  if (value < 0n || value > MAX_SAFE_DIRECTORY_IDENTITY)
@@ -8,7 +8,7 @@ import { sameFileIdentityForCleanup } from "./file-identity.js";
8
8
  import { MutationAuthorityError } from "./mutation-authority.js";
9
9
  import { isNotFoundPathError, isPathInside } from "./path.js";
10
10
  import { assertRootIdentityCurrent } from "./root-context.js";
11
- import { normalizeRemoveGuardError, normalizeRemovePathError, rootPathChangedError } from "./root-errors.js";
11
+ import { errorCauseOptions, normalizeRemoveGuardError, normalizeRemovePathError, rootPathChangedError } from "./root-errors.js";
12
12
  import { assertRemovalDirectoryCurrent, createRemovalDirectoryAssertion, } from "./root-remove-identity.js";
13
13
  import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
14
14
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
@@ -44,9 +44,7 @@ function assertNotAborted(signal) {
44
44
  }
45
45
  }
46
46
  function removalAncestorChanged(error) {
47
- return new FsSafeError("path-mismatch", "removal ancestor changed during operation", {
48
- cause: error instanceof Error ? error : undefined,
49
- });
47
+ return new FsSafeError("path-mismatch", "removal ancestor changed during operation", errorCauseOptions(error));
50
48
  }
51
49
  function sameCanonicalDirectory(left, right) {
52
50
  return left === right || (isPathInside(left, right) && isPathInside(right, left));
@@ -1,7 +1,7 @@
1
1
  import type { BigIntStats } from "node:fs";
2
2
  import { type DenyMutationPolicy } from "./deny-mutations.js";
3
3
  import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
4
- import type { PinnedWriteMutationAdmission } from "./pinned-write.js";
4
+ import type { PinnedWriteMutationAdmission } from "./pinned-write-types.js";
5
5
  import type { RootContext } from "./root-context.js";
6
6
  import { resolvePathInRoot } from "./root-context.js";
7
7
  import { type MutationSymlinkPolicy } from "./root-symlink-policy.js";
@@ -8,7 +8,7 @@ import { assertNoPathAliasEscape } from "./path-policy.js";
8
8
  import { preparePinnedWriteMutationAdmission, snapshotPinnedMutationPolicy, } from "./pinned-mutation-admission.js";
9
9
  import { admitPathInsideRoot, sameNormalizedPathSpelling } from "./root-boundary.js";
10
10
  import { resolvePathInRoot } from "./root-context.js";
11
- import { hardlinkedPathNotAllowedError, outsideWorkspaceError } from "./root-errors.js";
11
+ import { errorCauseOptions, hardlinkedPathNotAllowedError, outsideWorkspaceError } from "./root-errors.js";
12
12
  import { inheritWriteTargetMode } from "./root-write-mode.js";
13
13
  import { mutationSymlinkResolution, } from "./root-symlink-policy.js";
14
14
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
@@ -23,9 +23,7 @@ export function createRootWriteSelectionForFd(selection, fd) {
23
23
  });
24
24
  }
25
25
  function writeSelectionChanged(cause) {
26
- return new FsSafeError("path-mismatch", "write target changed during operation", {
27
- cause: cause instanceof Error ? cause : undefined,
28
- });
26
+ return new FsSafeError("path-mismatch", "write target changed during operation", errorCauseOptions(cause));
29
27
  }
30
28
  function inspectRegularSelectionPath(pathname, expected, follow) {
31
29
  let observed;
@@ -186,9 +184,7 @@ export async function resolveGuardedWritePathInRoot(root, params) {
186
184
  });
187
185
  }
188
186
  catch (error) {
189
- throw new FsSafeError("path-alias", "path alias escape blocked", {
190
- cause: error instanceof Error ? error : undefined,
191
- });
187
+ throw new FsSafeError("path-alias", "path alias escape blocked", errorCauseOptions(error));
192
188
  }
193
189
  }
194
190
  return resolvedPath;
@@ -8,13 +8,12 @@ import { realpathSync } from "./realpath.js";
8
8
  import { admitPathInsideRoot, sameNormalizedPathSpelling } from "./root-boundary.js";
9
9
  import { ordinaryWindowsSegments } from "./pinned-mutation-shared-route.js";
10
10
  import { prepareRootWriteTarget } from "./root-directory-creation.js";
11
+ import { errorCauseOptions } from "./root-errors.js";
11
12
  import { canReuseParentWithMutationAssertion } from "./root-write-lock-binding.js";
12
13
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
13
14
  import { getFsSafeTestHooks } from "./test-hooks.js";
14
15
  function writeSelectionChanged(cause) {
15
- return new FsSafeError("path-mismatch", "write target changed during operation", {
16
- cause: cause instanceof Error ? cause : undefined,
17
- });
16
+ return new FsSafeError("path-mismatch", "write target changed during operation", errorCauseOptions(cause));
18
17
  }
19
18
  function ordinarySharedWriteRoute(root, relativePath, operationTargetPath) {
20
19
  if (process.versions.bun || relativePath === "" || relativePath.startsWith("~") ||
@@ -1,5 +1,5 @@
1
1
  import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
2
- import type { PublishedWriteIdentity } from "./pinned-write.js";
2
+ import type { PublishedWriteIdentity } from "./pinned-write-types.js";
3
3
  import { type RootContext } from "./root-context.js";
4
4
  export declare function verifyAtomicWriteResult(params: {
5
5
  root: RootContext;
package/dist/root.d.ts CHANGED
@@ -1,6 +1,2 @@
1
- export { DEFAULT_ROOT_MAX_BYTES, openLocalFileSafely, readLocalFileSafely, resolveOpenedFileRealPathForHandle, root, type DenyMutationPolicy, type HardlinkPolicy, type MutationSymlinkPolicy, type RenameIdentityPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCopySource, type RootCreateJsonOptions, type RootCreateOptions, type RootCreateStreamOptions, type RootDefaults, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions, type SymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root-impl.js";
2
- export type { RootCopyPublicationReceipt } from "./copy-publication.js";
3
- export type { CopyCloneMode } from "./copy-policy.js";
4
- export type { RootEntriesOptions } from "./root-entries.js";
5
- export type { ContainmentGuarantee } from "./containment.js";
6
- export type { RootWalkDataEntry, RootWalkDataEntryKind, RootWalkDirectoryErrorBehavior, RootWalkEntry, RootWalkEntryFilter, RootWalkEntryFilterResult, RootWalkEntryKind, RootWalkLimitBehavior, RootWalkOptions, RootWalkSymlinkPolicy, } from "./root-walk.js";
1
+ export { DEFAULT_ROOT_MAX_BYTES, openLocalFileSafely, readLocalFileSafely, resolveOpenedFileRealPathForHandle, root, type Root, } from "./root-impl.js";
2
+ export type * from "./root-public-types.js";
@@ -5,10 +5,8 @@ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
5
5
  import { canonicalPathFromExistingAncestor } from "./absolute-path.js";
6
6
  import { readFileDescriptorBoundedSync } from "./bounded-read.js";
7
7
  import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, inspectDirectoryIdentity } from "./directory-guard.js";
8
- import { pinNodeDirectoryForMode } from "./directory-mode-node.js";
9
- import { assertOwnedDirectory } from "./directory-mode-owner.js";
8
+ import { pinNodeDirectoryForMode, assertOwnedDirectory } from "./directory-mode-node.js";
10
9
  import { FsSafeError } from "./errors.js";
11
- import { resolveHomeRelativePath } from "./home-dir.js";
12
10
  import { openPinnedFileSync } from "./pinned-open.js";
13
11
  import { runPinnedWriteHelper } from "./pinned-write.js";
14
12
  import { ensureTrailingSep } from "./root-context.js";
@@ -200,7 +198,11 @@ async function ensurePrivateDirectory(rootDir, targetDir, mode) {
200
198
  }
201
199
  return { rootGuard, parentGuard: targetGuard };
202
200
  }
203
- function snapshotSecretFileWriteParams(params, rootDir, filePath) {
201
+ function snapshotSecretFileWriteParams(params) {
202
+ const rootDir = params.rootDir;
203
+ const filePath = params.filePath;
204
+ assertNoWindowsPathAlias(rootDir, "filesystem", "private secret root uses a Windows filesystem namespace alias");
205
+ assertNoWindowsPathAlias(filePath, "filesystem", "private secret path uses a Windows filesystem namespace alias");
204
206
  return {
205
207
  rootDir,
206
208
  filePath,
@@ -306,31 +308,23 @@ async function materializeSecretFileAtomic(params, createOnly) {
306
308
  });
307
309
  }
308
310
  export async function writeSecretFileAtomic(params) {
309
- const rootDir = params.rootDir;
310
- const filePath = params.filePath;
311
- assertNoWindowsPathAlias(rootDir, "filesystem", "private secret root uses a Windows filesystem namespace alias");
312
- assertNoWindowsPathAlias(filePath, "filesystem", "private secret path uses a Windows filesystem namespace alias");
313
- const ownedParams = snapshotSecretFileWriteParams(params, rootDir, filePath);
314
- const canonicalPath = await secretFileWriteQueueKey(filePath);
311
+ const ownedParams = snapshotSecretFileWriteParams(params);
312
+ const canonicalPath = await secretFileWriteQueueKey(ownedParams.filePath);
315
313
  await serializePathWrite(canonicalPath, async () => {
316
314
  await materializeSecretFileAtomic(ownedParams, false);
317
315
  });
318
316
  }
319
317
  export async function createSecretFileAtomic(params) {
320
318
  try {
321
- const rootDir = params.rootDir;
322
- const filePath = params.filePath;
323
- assertNoWindowsPathAlias(rootDir, "filesystem", "private secret root uses a Windows filesystem namespace alias");
324
- assertNoWindowsPathAlias(filePath, "filesystem", "private secret path uses a Windows filesystem namespace alias");
325
- const ownedParams = snapshotSecretFileWriteParams(params, rootDir, filePath);
326
- const canonicalPath = await secretFileWriteQueueKey(filePath);
319
+ const ownedParams = snapshotSecretFileWriteParams(params);
320
+ const canonicalPath = await secretFileWriteQueueKey(ownedParams.filePath);
327
321
  await serializePathWrite(canonicalPath, async () => {
328
322
  await materializeSecretFileAtomic(ownedParams, true);
329
323
  });
330
324
  }
331
325
  catch (error) {
332
326
  if ((error instanceof FsSafeError && error.code === "already-exists") ||
333
- error.code === "EEXIST") {
327
+ error?.code === "EEXIST") {
334
328
  throw new FsSafeError("secret-exists", "Private secret file already exists.", { cause: error });
335
329
  }
336
330
  throw error;
@@ -7,7 +7,7 @@ import { inspectFileIdentitySync } from "./strict-file-identity.js";
7
7
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
8
8
  export const DEFAULT_SECRET_FILE_MAX_BYTES = 16 * 1024;
9
9
  export function secretPathErrorCode(error) {
10
- const code = error.code;
10
+ const code = error?.code;
11
11
  return code === "ENOENT" || code === "ENOTDIR" ? "not-found" : "invalid-path";
12
12
  }
13
13
  export function secretReadError(code, action, label, resolvedPath, error) {
@@ -1,5 +1,5 @@
1
1
  import { FsSafeError } from "./errors.js";
2
- import { recordFileObservationFailure } from "./file-observation.js";
2
+ import { fileIdentityMismatchError } from "./strict-file-identity.js";
3
3
  import { getNativeBinding } from "./native.js";
4
4
  import { getFsSafeNativeConfig } from "./native-config.js";
5
5
  import { warnNativeFallback } from "./native-fallback-warning.js";
@@ -10,20 +10,15 @@ const TRUSTED_OWNER_CLASSES = new Set(["current-user", "system", "administrators
10
10
  function permissionUnverified(message, cause) {
11
11
  throw new FsSafeError("permission-unverified", message, cause === undefined ? {} : { cause });
12
12
  }
13
- function identityMismatch() {
14
- const error = new FsSafeError("path-mismatch", "file identity changed or could not be verified");
15
- recordFileObservationFailure(error, "identity");
16
- throw error;
17
- }
18
13
  function isRecord(value) {
19
14
  return typeof value === "object" && value !== null;
20
15
  }
21
16
  function parseIdentity(value) {
22
17
  if (typeof value !== "string")
23
- identityMismatch();
18
+ throw fileIdentityMismatchError();
24
19
  const match = IDENTITY_RE.exec(value);
25
20
  if (!match)
26
- identityMismatch();
21
+ throw fileIdentityMismatchError();
27
22
  return {
28
23
  dev: BigInt(`0x${match[1]}`),
29
24
  ino: BigInt(`0x${match[2]}`),
@@ -34,7 +29,7 @@ function inspectDescriptorResult(params, result, mechanism) {
34
29
  permissionUnverified("Windows descriptor ACL facts were malformed");
35
30
  const observed = parseIdentity(result.identity);
36
31
  if (observed.dev !== params.identity.dev || observed.ino !== params.identity.ino) {
37
- identityMismatch();
32
+ throw fileIdentityMismatchError();
38
33
  }
39
34
  const facts = validateSecureWindowsSecurityFacts(result.security);
40
35
  return {
@@ -3,15 +3,10 @@ import { tmpdir as getOsTmpDir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { directoryEntryPath } from "./directory-entry-path.js";
5
5
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
6
+ import { hasNodeErrorCode } from "./path.js";
6
7
  import { assertSafePathSegment } from "./safe-path-segment.js";
7
8
  import { assertNoWindowsPathAlias, pathForWindowsFilesystem } from "./windows-path-alias.js";
8
9
  import { captureSecureTempRepairAdapter, repairSecureTempDirectory, secureTempDirectoryReceipt, } from "./secure-temp-repair.js";
9
- function isNodeErrorWithCode(err, code) {
10
- return (typeof err === "object" &&
11
- err !== null &&
12
- "code" in err &&
13
- err.code === code);
14
- }
15
10
  export function resolveSecureTempRoot(options) {
16
11
  const { fallbackPrefix: prefix, accessSync: suppliedAccess, lstatSync: suppliedLstat, mkdirSync: suppliedMkdir, descriptor: suppliedDescriptor, getuid: suppliedGetuid, tmpdir: suppliedTmpdir, platform: suppliedPlatform, preferredDir, skipPreferredOnWindows, warn: suppliedWarn, warningPrefix: suppliedWarningPrefix, unsafeFallbackLabel: suppliedFallbackLabel, } = options;
17
12
  const platform = suppliedPlatform ?? process.platform;
@@ -87,7 +82,7 @@ export function resolveSecureTempRoot(options) {
87
82
  candidate = lstatSync(candidatePath);
88
83
  }
89
84
  catch (error) {
90
- return { kind: isNodeErrorWithCode(error, "ENOENT") ? "missing" : "invalid", error };
85
+ return { kind: hasNodeErrorCode(error, "ENOENT") ? "missing" : "invalid", error };
91
86
  }
92
87
  let receipt;
93
88
  try {
@@ -14,9 +14,13 @@ export function captureSecureTempRepairAdapter(supplied, hasLegacyFsHooks) {
14
14
  }
15
15
  return { lstatSync, fstatSync, openSync, fchmodSync, closeSync, flags: O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK };
16
16
  }
17
+ // Node exposes stat identity through BigInt64Array; a high OS bit can be negative.
18
+ // Preserve raw values and existing positive adapter receipts, never normalize them.
19
+ const MIN_NODE_STAT_IDENTITY = -(1n << 63n);
17
20
  export function secureTempDirectoryReceipt(stat, uid) {
18
21
  const { dev, ino, uid: owner, mode } = stat;
19
- if (typeof dev !== "bigint" || dev < 0n || typeof ino !== "bigint" || ino <= 0n ||
22
+ if (typeof dev !== "bigint" || dev < MIN_NODE_STAT_IDENTITY ||
23
+ typeof ino !== "bigint" || ino < MIN_NODE_STAT_IDENTITY || ino === 0n ||
20
24
  typeof owner !== "bigint" || owner !== BigInt(uid) || typeof mode !== "bigint" ||
21
25
  mode < 0n || mode > 0xffffffffn || (mode & 61440n) !== 16384n ||
22
26
  stat.isDirectory() !== true || stat.isSymbolicLink() !== false) {
@@ -69,7 +69,7 @@ export async function acquireSidecarLock(options, context) {
69
69
  if (activeDescendant)
70
70
  throw sidecarLockTimeout(lockPath, normalizedTargetPath);
71
71
  const admission = createSidecarAdmissionController(admissionAncestry, context.admissions, normalizedTargetPath);
72
- const startedAt = Date.now();
72
+ const startedAt = performance.now();
73
73
  const reclaimGuardPath = `${lockPath}.reclaim`;
74
74
  let reclaimGuard;
75
75
  const releaseReclaimGuard = async () => {
@@ -90,7 +90,7 @@ export async function acquireSidecarLock(options, context) {
90
90
  const withinDenialBudget = () => ++transientDenials <= maxTransientLockDenials;
91
91
  const waitForRetry = async () => {
92
92
  admission.release();
93
- const delay = sidecarLockRetryDelay(retry, timeoutMs === Number.POSITIVE_INFINITY ? undefined : timeoutMs, Date.now() - startedAt, attempt);
93
+ const delay = sidecarLockRetryDelay(retry, timeoutMs === Number.POSITIVE_INFINITY ? undefined : timeoutMs, performance.now() - startedAt, attempt);
94
94
  if (delay === undefined)
95
95
  throw sidecarLockTimeout(lockPath, normalizedTargetPath);
96
96
  attempt += 1;
@@ -102,7 +102,7 @@ export async function acquireSidecarLock(options, context) {
102
102
  await waitForRetry();
103
103
  }
104
104
  catch (waitError) {
105
- if (waitError.code === "file_lock_timeout")
105
+ if (waitError?.code === "file_lock_timeout")
106
106
  throw denial;
107
107
  throw waitError;
108
108
  }
@@ -357,8 +357,9 @@ export async function acquireSidecarLock(options, context) {
357
357
  try {
358
358
  if (createdHeld)
359
359
  stopSidecarLockMonitoring(createdHeld);
360
+ let failedSnapshot = createdSnapshot;
360
361
  if (handle) {
361
- const failedSnapshot = createdSnapshot ?? { payload: null };
362
+ failedSnapshot ??= { payload: null };
362
363
  try {
363
364
  failedSnapshot.stat = fsSync.fstatSync(handle.fd);
364
365
  }
@@ -370,14 +371,10 @@ export async function acquireSidecarLock(options, context) {
370
371
  context.held.delete(normalizedTargetPath);
371
372
  }
372
373
  await handle.close().catch(() => undefined);
373
- // Root records use the creator receipt; partial raw writes use fd identity.
374
- await removeSidecarLockIfUnchanged(lockPath, failedSnapshot, {
375
- lockRoot,
376
- parsePayload: conditionalSidecarLockParser(parserState, admission, () => admission.hasToken() && !context.held.has(normalizedTargetPath)),
377
- });
378
374
  }
379
- else if (createdSnapshot) {
380
- await removeSidecarLockIfUnchanged(lockPath, createdSnapshot, {
375
+ // Root records use the creator receipt; partial raw writes use fd identity.
376
+ if (failedSnapshot) {
377
+ await removeSidecarLockIfUnchanged(lockPath, failedSnapshot, {
381
378
  lockRoot,
382
379
  parsePayload: conditionalSidecarLockParser(parserState, admission, () => admission.hasToken() && !context.held.has(normalizedTargetPath)),
383
380
  });
@@ -5,6 +5,7 @@ export declare function validateSidecarLockStaleMs(staleMs: number | undefined):
5
5
  export declare function validateSidecarLockCompromiseCheckIntervalMs(intervalMs: number | undefined): void;
6
6
  export declare function computeSidecarLockDelayMs(retry: SidecarLockRetryOptions, attempt: number): number;
7
7
  export declare function sidecarLockTimeout(lockPath: string, normalizedTargetPath: string): Error;
8
+ export declare function sidecarLockStale(lockPath: string, normalizedTargetPath: string): Error;
8
9
  /** Undefined means the deadline or retry count is exhausted. */
9
10
  export declare function sidecarLockRetryDelay(retry: SidecarLockRetryOptions, timeoutMs: number | undefined, elapsed: number, attempt: number): number | undefined;
10
11
  export declare const maxTransientLockDenials = 8;
@@ -62,6 +62,13 @@ export function sidecarLockTimeout(lockPath, normalizedTargetPath) {
62
62
  normalizedTargetPath,
63
63
  });
64
64
  }
65
+ export function sidecarLockStale(lockPath, normalizedTargetPath) {
66
+ return Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
67
+ code: "file_lock_stale",
68
+ lockPath,
69
+ normalizedTargetPath,
70
+ });
71
+ }
65
72
  /** Undefined means the deadline or retry count is exhausted. */
66
73
  export function sidecarLockRetryDelay(retry, timeoutMs, elapsed, attempt) {
67
74
  if ((timeoutMs !== undefined && elapsed >= timeoutMs) ||
@@ -357,13 +357,12 @@ export async function removeStaleSidecarLockIfAllowed(params) {
357
357
  if (params.assertGuardHeld)
358
358
  await params.assertGuardHeld();
359
359
  params.assertAuthorized?.();
360
+ if (params.lockRoot) {
361
+ await params.lockRoot.remove(relativeSidecarLockPath(params.lockRoot, params.lockPath), { assertBeforeMutation: params.assertAuthorized });
362
+ return "removed";
363
+ }
360
364
  try {
361
- if (params.lockRoot) {
362
- await params.lockRoot.remove(relativeSidecarLockPath(params.lockRoot, params.lockPath), { assertBeforeMutation: params.assertAuthorized });
363
- }
364
- else {
365
- await fs.rm(params.lockPath);
366
- }
365
+ await fs.rm(params.lockPath);
367
366
  return "removed";
368
367
  }
369
368
  catch (err) {
@@ -1,4 +1,4 @@
1
- import { defaultSidecarLockShouldReclaim, isTransientLockFileDenial } from "./sidecar-lock-policy.js";
1
+ import { defaultSidecarLockShouldReclaim, isTransientLockFileDenial, sidecarLockStale } from "./sidecar-lock-policy.js";
2
2
  import { readSidecarLockRawSnapshot, removeStaleSidecarLockIfAllowed, sidecarLockSnapshotStillPresent, tryAcquireSidecarReclaimGuard, } from "./sidecar-lock-reclaim.js";
3
3
  import { observeSidecarLockParser, parseSidecarLockSnapshotUnderAdmission, scopedSidecarLockParser, } from "./sidecar-lock-admission-parser.js";
4
4
  /** Handle one historical EEXIST attempt while its canonical admission is held. */
@@ -224,9 +224,5 @@ export async function handleStaleSidecarAdmission(params) {
224
224
  }
225
225
  await params.releaseReclaimGuard();
226
226
  }
227
- throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
228
- code: "file_lock_stale",
229
- lockPath,
230
- normalizedTargetPath,
231
- });
227
+ throw sidecarLockStale(lockPath, normalizedTargetPath);
232
228
  }
@@ -12,27 +12,35 @@ export type SidecarLockCompromisedInfo = {
12
12
  lockPath: string;
13
13
  normalizedTargetPath: string;
14
14
  };
15
- export type SidecarLockAcquireOptions<TPayload extends Record<string, unknown>> = {
16
- targetPath: string;
15
+ export type SidecarLockOptionFields<T> = {
16
+ [K in keyof T]: T[K];
17
+ };
18
+ export type SidecarLockCommonAcquireOptions = {
17
19
  lockPath?: string;
18
- staleMs: number;
19
20
  timeoutMs?: number;
20
21
  retry?: SidecarLockRetryOptions;
21
22
  staleRecovery?: SidecarLockStaleRecovery;
22
23
  reentrantOwner?: string;
24
+ parsePayload?: (raw: string) => unknown;
25
+ lockRoot?: Root;
26
+ onCompromised?: (info: SidecarLockCompromisedInfo) => void;
27
+ compromiseCheckIntervalMs?: number;
28
+ };
29
+ export type SidecarLockReclaimParams<Held extends boolean = boolean> = {
30
+ lockPath: string;
31
+ normalizedTargetPath: string;
32
+ payload: unknown;
33
+ staleMs: number;
34
+ nowMs: number;
35
+ heldByThisProcess: Held;
36
+ };
37
+ export type SidecarLockAcquireOptions<TPayload extends Record<string, unknown>> = SidecarLockOptionFields<SidecarLockCommonAcquireOptions & {
38
+ targetPath: string;
39
+ staleMs: number;
23
40
  payload: () => TPayload | Promise<TPayload>;
24
- shouldReclaim?: (params: {
25
- lockPath: string;
26
- normalizedTargetPath: string;
27
- payload: unknown;
28
- staleMs: number;
29
- nowMs: number;
30
- heldByThisProcess: boolean;
31
- }) => boolean | Promise<boolean>;
41
+ shouldReclaim?: (params: SidecarLockReclaimParams) => boolean | Promise<boolean>;
32
42
  shouldRemoveStaleLock?: (snapshot: SidecarLockStaleSnapshot) => boolean | Promise<boolean>;
33
43
  metadata?: Record<string, unknown>;
34
- parsePayload?: (raw: string) => unknown;
35
- lockRoot?: Root;
36
44
  /**
37
45
  * Keep the lock file when the process exits naturally. Default `false`:
38
46
  * process-exit handlers release held locks. Set only for deliberately
@@ -40,9 +48,7 @@ export type SidecarLockAcquireOptions<TPayload extends Record<string, unknown>>
40
48
  * liveness is governed by the caller's own stale policy.
41
49
  */
42
50
  retainOnExit?: boolean;
43
- onCompromised?: (info: SidecarLockCompromisedInfo) => void;
44
- compromiseCheckIntervalMs?: number;
45
- };
51
+ }>;
46
52
  export type SidecarLockHandle = {
47
53
  lockPath: string;
48
54
  normalizedTargetPath: string;
@@ -1,11 +1,4 @@
1
- import { FsSafeError } from "./errors.js";
2
- import { recordFileObservationFailure } from "./file-observation.js";
3
- import { inspectFileIdentitySync } from "./strict-file-identity.js";
4
- function identityMismatch() {
5
- const error = new FsSafeError("path-mismatch", "file identity changed or could not be verified");
6
- recordFileObservationFailure(error, "identity");
7
- throw error;
8
- }
1
+ import { fileIdentityMismatchError, inspectFileIdentitySync } from "./strict-file-identity.js";
9
2
  function safeNumber(value) {
10
3
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
11
4
  }
@@ -35,7 +28,7 @@ function observeStatSync(inspect, expected, initial, platform = process.platform
35
28
  const ino = numericIno ? BigInt(rawIno) : typeof rawIno === "bigint" ? rawIno : undefined;
36
29
  if (expected && ((dev !== undefined && dev !== expected.dev) ||
37
30
  (ino !== undefined && ino !== expected.ino)))
38
- identityMismatch();
31
+ throw fileIdentityMismatchError();
39
32
  if (numericDev && numericIno) {
40
33
  return (captureIdentity
41
34
  ? { stat, identity: expected ?? { dev: dev, ino: ino } }
@@ -47,7 +40,7 @@ function observeStatSync(inspect, expected, initial, platform = process.platform
47
40
  const current = inspect(true);
48
41
  if ((dev !== undefined && current.dev !== dev) ||
49
42
  (ino !== undefined && current.ino !== ino))
50
- identityMismatch();
43
+ throw fileIdentityMismatchError();
51
44
  return current;
52
45
  }, expected, platform);
53
46
  return (captureIdentity
@@ -1,5 +1,7 @@
1
1
  import type { BigIntStats } from "node:fs";
2
+ import { FsSafeError } from "./errors.js";
2
3
  type ExactFileIdentity = Pick<BigIntStats, "dev" | "ino">;
4
+ export declare function fileIdentityMismatchError(): FsSafeError;
3
5
  export declare function inspectFileIdentity<T extends ExactFileIdentity>(inspect: () => T | Promise<T>, expected?: ExactFileIdentity, platform?: NodeJS.Platform): Promise<T>;
4
6
  export declare function inspectFileIdentitySync<T extends ExactFileIdentity>(inspect: () => T, expected?: ExactFileIdentity, platform?: NodeJS.Platform, mismatch?: () => Error): T;
5
7
  export {};