@openclaw/fs-safe 0.8.2 → 0.8.3

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 (159) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +1 -1
  3. package/dist/advanced.d.ts +2 -2
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +2 -2
  6. package/dist/archive-kind.d.ts +1 -0
  7. package/dist/archive-kind.d.ts.map +1 -1
  8. package/dist/archive-kind.js +6 -0
  9. package/dist/archive-limits.d.ts +0 -1
  10. package/dist/archive-limits.d.ts.map +1 -1
  11. package/dist/archive-limits.js +0 -3
  12. package/dist/archive-native.d.ts.map +1 -1
  13. package/dist/archive-native.js +5 -8
  14. package/dist/archive-parser-errors.d.ts +2 -0
  15. package/dist/archive-parser-errors.d.ts.map +1 -0
  16. package/dist/archive-parser-errors.js +12 -0
  17. package/dist/archive-read.d.ts.map +1 -1
  18. package/dist/archive-read.js +7 -13
  19. package/dist/archive-tar-wasm.d.ts +1 -1
  20. package/dist/archive-tar-wasm.d.ts.map +1 -1
  21. package/dist/archive-tar-wasm.js +5 -8
  22. package/dist/archive.d.ts.map +1 -1
  23. package/dist/archive.js +43 -6
  24. package/dist/atomic.d.ts +1 -1
  25. package/dist/atomic.d.ts.map +1 -1
  26. package/dist/atomic.js +1 -1
  27. package/dist/bounded-read-stream.d.ts +1 -5
  28. package/dist/bounded-read-stream.d.ts.map +1 -1
  29. package/dist/bounded-read-stream.js +5 -13
  30. package/dist/byte-budget.d.ts +0 -1
  31. package/dist/byte-budget.d.ts.map +1 -1
  32. package/dist/byte-budget.js +1 -1
  33. package/dist/directory-mode-owner.d.ts.map +1 -1
  34. package/dist/directory-mode-owner.js +2 -0
  35. package/dist/error-detail.d.ts +1 -0
  36. package/dist/error-detail.d.ts.map +1 -1
  37. package/dist/error-detail.js +6 -0
  38. package/dist/file-store-boundary.d.ts +1 -1
  39. package/dist/file-store-boundary.d.ts.map +1 -1
  40. package/dist/file-store-boundary.js +2 -13
  41. package/dist/file-store.js +1 -1
  42. package/dist/file-sync.d.ts +5 -0
  43. package/dist/file-sync.d.ts.map +1 -0
  44. package/dist/file-sync.js +19 -0
  45. package/dist/guarded-mutation.d.ts +3 -0
  46. package/dist/guarded-mutation.d.ts.map +1 -1
  47. package/dist/guarded-mutation.js +5 -1
  48. package/dist/home-dir.d.ts +0 -10
  49. package/dist/home-dir.d.ts.map +1 -1
  50. package/dist/home-dir.js +0 -28
  51. package/dist/json-durable-queue-ownership.d.ts +1 -0
  52. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  53. package/dist/json-durable-queue-ownership.js +139 -18
  54. package/dist/json-durable-queue.d.ts.map +1 -1
  55. package/dist/json-durable-queue.js +5 -10
  56. package/dist/json-store.d.ts.map +1 -1
  57. package/dist/json-store.js +1 -1
  58. package/dist/local-roots.d.ts.map +1 -1
  59. package/dist/local-roots.js +2 -3
  60. package/dist/move-path-cleanup.d.ts +1 -1
  61. package/dist/move-path-cleanup.d.ts.map +1 -1
  62. package/dist/move-path-cleanup.js +4 -2
  63. package/dist/move-path.d.ts +9 -0
  64. package/dist/move-path.d.ts.map +1 -1
  65. package/dist/move-path.js +53 -16
  66. package/dist/native-operations.d.ts +0 -1
  67. package/dist/native-operations.d.ts.map +1 -1
  68. package/dist/native-operations.js +0 -10
  69. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  70. package/dist/native-pinned-write-windows.js +5 -4
  71. package/dist/native-staged-file.d.ts +8 -1
  72. package/dist/native-staged-file.d.ts.map +1 -1
  73. package/dist/native-staged-file.js +30 -9
  74. package/dist/opened-realpath.d.ts +12 -1
  75. package/dist/opened-realpath.d.ts.map +1 -1
  76. package/dist/opened-realpath.js +8 -7
  77. package/dist/output.d.ts.map +1 -1
  78. package/dist/output.js +26 -2
  79. package/dist/path-policy.js +1 -1
  80. package/dist/permissions.d.ts +1 -0
  81. package/dist/permissions.d.ts.map +1 -1
  82. package/dist/permissions.js +3 -0
  83. package/dist/pinned-write.d.ts +1 -2
  84. package/dist/pinned-write.d.ts.map +1 -1
  85. package/dist/pinned-write.js +2 -11
  86. package/dist/private-temp-workspace.js +1 -1
  87. package/dist/publish-file.js +2 -2
  88. package/dist/replace-file-copy-source.d.ts.map +1 -1
  89. package/dist/replace-file-copy-source.js +2 -7
  90. package/dist/replace-file-descriptor.d.ts.map +1 -1
  91. package/dist/replace-file-descriptor.js +3 -16
  92. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  93. package/dist/replace-file-temp-owner.js +2 -7
  94. package/dist/replace-file.d.ts.map +1 -1
  95. package/dist/replace-file.js +21 -8
  96. package/dist/root-errors.d.ts +1 -0
  97. package/dist/root-errors.d.ts.map +1 -1
  98. package/dist/root-errors.js +9 -0
  99. package/dist/root-file.d.ts.map +1 -1
  100. package/dist/root-file.js +17 -28
  101. package/dist/root-impl.d.ts.map +1 -1
  102. package/dist/root-impl.js +12 -33
  103. package/dist/root-path.d.ts.map +1 -1
  104. package/dist/root-path.js +49 -94
  105. package/dist/root-write-mode.d.ts +6 -0
  106. package/dist/root-write-mode.d.ts.map +1 -0
  107. package/dist/root-write-mode.js +60 -0
  108. package/dist/root-write-verification.js +2 -2
  109. package/dist/safe-path-segment.d.ts +1 -0
  110. package/dist/safe-path-segment.d.ts.map +1 -1
  111. package/dist/safe-path-segment.js +1 -1
  112. package/dist/secret-file.d.ts.map +1 -1
  113. package/dist/secret-file.js +2 -9
  114. package/dist/secret-read-async.d.ts.map +1 -1
  115. package/dist/secret-read-async.js +2 -10
  116. package/dist/secret-read-policy.d.ts +4 -0
  117. package/dist/secret-read-policy.d.ts.map +1 -1
  118. package/dist/secret-read-policy.js +12 -0
  119. package/dist/sibling-staged-file.d.ts.map +1 -1
  120. package/dist/sibling-staged-file.js +2 -7
  121. package/dist/sidecar-lock-policy.d.ts +0 -1
  122. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  123. package/dist/sidecar-lock-policy.js +0 -4
  124. package/dist/string-coerce.d.ts +0 -9
  125. package/dist/string-coerce.d.ts.map +1 -1
  126. package/dist/string-coerce.js +0 -56
  127. package/dist/temp-target.d.ts.map +1 -1
  128. package/dist/temp-target.js +1 -12
  129. package/docs/atomic.md +53 -19
  130. package/docs/staged-file.md +6 -2
  131. package/docs/writing.md +5 -0
  132. package/package.json +8 -8
  133. package/dist/archive-tar-extract.d.ts +0 -11
  134. package/dist/archive-tar-extract.d.ts.map +0 -1
  135. package/dist/archive-tar-extract.js +0 -49
  136. package/dist/fsync.d.ts +0 -2
  137. package/dist/fsync.d.ts.map +0 -1
  138. package/dist/fsync.js +0 -1
  139. package/dist/json-durable-queue-retirement.d.ts +0 -9
  140. package/dist/json-durable-queue-retirement.d.ts.map +0 -1
  141. package/dist/json-durable-queue-retirement.js +0 -126
  142. package/dist/json-durable-queue-transfer-lock.d.ts +0 -2
  143. package/dist/json-durable-queue-transfer-lock.d.ts.map +0 -1
  144. package/dist/json-durable-queue-transfer-lock.js +0 -19
  145. package/dist/mode.d.ts +0 -2
  146. package/dist/mode.d.ts.map +0 -1
  147. package/dist/mode.js +0 -3
  148. package/dist/output-sibling.d.ts +0 -8
  149. package/dist/output-sibling.d.ts.map +0 -1
  150. package/dist/output-sibling.js +0 -30
  151. package/dist/read-error.d.ts +0 -2
  152. package/dist/read-error.d.ts.map +0 -1
  153. package/dist/read-error.js +0 -11
  154. package/dist/short-path.d.ts +0 -2
  155. package/dist/short-path.d.ts.map +0 -1
  156. package/dist/short-path.js +0 -7
  157. package/dist/staged-file.d.ts +0 -10
  158. package/dist/staged-file.d.ts.map +0 -1
  159. package/dist/staged-file.js +0 -15
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.3 - 2026-09-06
4
+
5
+ - Add optional synchronous move authority checks before renames and each copied-source removal, plus an exact bigint destination-publication receipt for caller-owned recovery after later failure; retain cross-device and Windows `EPERM` copy fallbacks and the existing `Promise<void>` contract.
6
+
7
+ - Simplify internals without changing public behavior: remove unused string/home helpers, `resolveUserPath`, `createBoundedReadStream`, `sidecarLockPayloadIsStale`, and `tarManifestEntryCost`; share filesystem utilities and merge private modules.
8
+ - Reduce filesystem calls per read and write while retaining boundary, hardlink, hook, retry, and post-mutation identity checks.
9
+ - Skip native publication's extra mode-only file fsync for modes that retain owner read/write when staging permissions have not widened; after a crash, a file may retain staged `0o600` instead of the wider requested mode, while restrictive modes retain the extra fsync.
10
+ - Add 1 MiB read/write and existing-mode inheritance benchmarks, with native-mode metadata and per-case iteration counts.
11
+
3
12
  ## 0.8.2 - 2026-09-05
4
13
 
5
14
  **Highlights:** TAR/gzip extraction now shares one parser across native and fallback modes, improving compatibility with system-tar output while keeping admission and publication guarded.
package/README.md CHANGED
@@ -294,7 +294,7 @@ original directory on Linux/macOS and requires native support for this operation
294
294
  It offers atomic replace/no-replace publication, not expected-inode replacement
295
295
  or a crash-durability promise; application checks and coordination remain yours.
296
296
 
297
- `replaceFileAtomic()` writes a sibling temp file, applies its exact mode through the still-open descriptor, optionally fsyncs it, and renames it over the destination. It never follows the published destination path to set file permissions. Mode preservation, pinned-destination hardlink rejection, rename retry / copy fallback on `EPERM`, bounded original-content restoration after a torn fallback, parent-directory fsync, and a `beforeRename` hook for backup or observer flows are all opt-in. `movePathWithCopyFallback()` stages cross-device moves before commit and removes only the copied source entries, so concurrent source additions or replacements are preserved.
297
+ `replaceFileAtomic()` writes a sibling temp file, applies its exact mode through the still-open descriptor, optionally fsyncs it, and renames it over the destination. It never follows the published destination path to set file permissions. Mode preservation, pinned-destination hardlink rejection, rename retry / copy fallback on `EPERM`, bounded original-content restoration after a torn fallback, parent-directory fsync, and a `beforeRename` hook for backup or observer flows are all opt-in. `movePathWithCopyFallback()` stages cross-device moves before commit and removes only the copied source entries, so concurrent source additions or replacements are preserved. Its optional synchronous `assertBeforeMutation` hook rechecks caller authority before renames and each source removal; `onDestinationPublished` reports an exact bigint destination identity before later checks or cleanup can fail. See [mutation authority and publication receipts](docs/atomic.md#mutation-authority-and-publication-receipts).
298
298
 
299
299
  ```ts
300
300
  import { replaceFileAtomic } from "@openclaw/fs-safe/atomic";
@@ -1,5 +1,5 @@
1
1
  export { createAsyncLock } from "./async-lock.js";
2
- export { stageFileInDirectory, type StagedFile, type StagedFileReceipt, type PublishedFileReceipt, type StagedFilePublication, type StagedFileCleanupReceipt, type StagedFileFailureDetails, } from "./staged-file.js";
2
+ export { stageFileInDirectory, type StagedFile, type StagedFileReceipt, type PublishedFileReceipt, type StagedFilePublication, type StagedFileCleanupReceipt, type StagedFileFailureDetails, } from "./native-staged-file.js";
3
3
  export { readFileDescriptorBounded, readFileDescriptorBoundedSync, readFileHandleBounded, } from "./bounded-read.js";
4
4
  export { assertNoUnsafeDeviceReadPath, isUnsafeDeviceReadPath, matchUnsafeDeviceReadPath, type UnsafeDeviceReadPathMatch, type UnsafeDeviceReadPathOptions, type UnsafeDeviceReadPathReason, } from "./device-path.js";
5
5
  export { assertAbsolutePathInput, canonicalPathFromExistingAncestor, ensureAbsoluteDirectory, findExistingAncestor, resolveAbsolutePathForRead, resolveAbsolutePathForWrite, type AbsolutePathSymlinkPolicy, type EnsureAbsoluteDirectoryOptions, type EnsureAbsoluteDirectoryResult, type ResolvedAbsolutePath, type ResolvedWritableAbsolutePath, } from "./absolute-path.js";
@@ -8,7 +8,7 @@ export { sanitizeUntrustedFileName } from "./filename.js";
8
8
  export { pathExists, pathExistsSync } from "./fs.js";
9
9
  export { resolveLocalPathFromRootsSync, readLocalFileFromRoots, type LocalRootsInputOptions, type LocalRootsPathResult, type LocalRootsReadResult, type ReadLocalFileFromRootsOptions, type ResolveLocalPathFromRootsSyncOptions, } from "./local-roots.js";
10
10
  export { assertNoWindowsNetworkPath, basenameFromMediaSource, hasEncodedFileUrlSeparator, isWindowsDriveLetterPath, isWindowsNetworkPath, safeFileURLToPath, trySafeFileURLToPath, } from "./local-file-access.js";
11
- export { formatPosixMode } from "./mode.js";
11
+ export { formatPosixMode } from "./permissions.js";
12
12
  export { configureFsSafeLocks, getFsSafeLockConfig, type FsSafeLockConfig, } from "./lock-config.js";
13
13
  export { assertNoHardlinkedFinalPath, assertNoPathAliasEscape, PATH_ALIAS_POLICIES, type PathAliasPolicy, } from "./path-policy.js";
14
14
  export { openRootFile, openRootFileSync, canUseRootFileOpen, matchRootFileOpenFailure, type OpenRootFileParams, type OpenRootFileSyncParams, type RootFileOpenFailure, type RootFileOpenFailureReason, type RootFileOpenResult, } from "./root-file.js";
@@ -1 +1 @@
1
- {"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"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"}
package/dist/advanced.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // public subpaths; prefer root/json/store/temp/archive unless you are building a
3
3
  // higher-level primitive.
4
4
  export { createAsyncLock } from "./async-lock.js";
5
- export { stageFileInDirectory, } from "./staged-file.js";
5
+ export { stageFileInDirectory, } from "./native-staged-file.js";
6
6
  export { readFileDescriptorBounded, readFileDescriptorBoundedSync, readFileHandleBounded, } from "./bounded-read.js";
7
7
  export { assertNoUnsafeDeviceReadPath, isUnsafeDeviceReadPath, matchUnsafeDeviceReadPath, } from "./device-path.js";
8
8
  export { assertAbsolutePathInput, canonicalPathFromExistingAncestor, ensureAbsoluteDirectory, findExistingAncestor, resolveAbsolutePathForRead, resolveAbsolutePathForWrite, } from "./absolute-path.js";
@@ -11,7 +11,7 @@ export { sanitizeUntrustedFileName } from "./filename.js";
11
11
  export { pathExists, pathExistsSync } from "./fs.js";
12
12
  export { resolveLocalPathFromRootsSync, readLocalFileFromRoots, } from "./local-roots.js";
13
13
  export { assertNoWindowsNetworkPath, basenameFromMediaSource, hasEncodedFileUrlSeparator, isWindowsDriveLetterPath, isWindowsNetworkPath, safeFileURLToPath, trySafeFileURLToPath, } from "./local-file-access.js";
14
- export { formatPosixMode } from "./mode.js";
14
+ export { formatPosixMode } from "./permissions.js";
15
15
  export { configureFsSafeLocks, getFsSafeLockConfig, } from "./lock-config.js";
16
16
  export { assertNoHardlinkedFinalPath, assertNoPathAliasEscape, PATH_ALIAS_POLICIES, } from "./path-policy.js";
17
17
  export { openRootFile, openRootFileSync, canUseRootFileOpen, matchRootFileOpenFailure, } from "./root-file.js";
@@ -4,5 +4,6 @@ type ResolvePackedRootDirOptions = {
4
4
  rootMarkers?: string[];
5
5
  };
6
6
  export declare function resolvePackedRootDir(extractDir: string, options?: ResolvePackedRootDirOptions): Promise<string>;
7
+ export declare function assertPortableArchiveKind(kind: ArchiveKind): void;
7
8
  export {};
8
9
  //# sourceMappingURL=archive-kind.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"archive-kind.d.ts","sourceRoot":"","sources":["../src/archive-kind.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;AAmBnE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAcvE;AAED,KAAK,2BAA2B,GAAG;IACjC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAkBF,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,MAAM,CAAC,CA4BjB"}
1
+ {"version":3,"file":"archive-kind.d.ts","sourceRoot":"","sources":["../src/archive-kind.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;AAmBnE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAcvE;AAED,KAAK,2BAA2B,GAAG;IACjC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAkBF,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAQjE"}
@@ -74,3 +74,9 @@ export async function resolvePackedRootDir(extractDir, options) {
74
74
  }
75
75
  return path.join(extractDir, onlyDir);
76
76
  }
77
+ export function assertPortableArchiveKind(kind) {
78
+ if (kind === "tar-zstd" || kind === "tar-bzip2") {
79
+ throw new FsSafeError("helper-unavailable", `${kind} archives require the matching optional native platform package; ` +
80
+ "install @openclaw/fs-safe with optional dependencies enabled on a supported platform and use FS_SAFE_NATIVE_MODE=auto or require");
81
+ }
82
+ }
@@ -42,7 +42,6 @@ export type TarMeterLimits = Pick<ResolvedArchiveExtractLimits, "maxEntries" | "
42
42
  maxManifestBytes: number;
43
43
  };
44
44
  export declare const MAX_TAR_MANIFEST_BYTES: number;
45
- export declare function tarManifestEntryCost(path: string): number;
46
45
  export declare function resolveTarMeterLimits(options?: ArchiveExtractLimits): TarMeterLimits;
47
46
  export declare function resolveExtractLimits(limits?: ArchiveExtractLimits): ResolvedArchiveExtractLimits;
48
47
  export declare function assertArchiveEntryPathComponentsWithinLimit(entryPath: string, limits: ResolvedArchiveExtractLimits): void;
@@ -1 +1 @@
1
- {"version":3,"file":"archive-limits.d.ts","sourceRoot":"","sources":["../src/archive-limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,6BAA6B,QAAoB,CAAC;AAC/D,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAC1C,eAAO,MAAM,2BAA2B,QAAoB,CAAC;AAC7D,eAAO,MAAM,uBAAuB,QAAoB,CAAC;AACzD,eAAO,MAAM,4BAA4B,QAAc,CAAC;AACxD,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD,eAAO,MAAM,wBAAwB;aACnC,0BAA0B,EAAE,4BAA4B;aACxD,yBAAyB,EAAE,mCAAmC;aAC9D,kCAAkC,EAAE,4CAA4C;aAChF,4BAA4B,EAAE,sCAAsC;aACpE,0BAA0B,EAAE,oCAAoC;aAChE,6BAA6B,EAAE,uCAAuC;aACtE,2BAA2B,EAAE,qCAAqC;aAClE,mCAAmC,EAAE,6CAA6C;CAC1E,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAC/B,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,OAAO,wBAAwB,CAAC,CAAC;AAiB3E,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAErC,YAAY,IAAI,EAAE,qBAAqB,EAItC;CACF;AAED,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AAE1E,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,4BAA4B,EAC5D,YAAY,GAAG,mBAAmB,CAAC,GAAG;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,cAAc,CAkBpF;AAUD,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,oBAAoB,GAC5B,4BAA4B,CAY9B;AAED,wBAAgB,2CAA2C,CACzD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,4BAA4B,GACnC,IAAI,CASN;AAED,wBAAgB,kCAAkC,CAChD,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,4BAA4B,QAKrC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,4BAA4B,GAAG;IAC7E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC,CAiCA;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE;IACnD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,GAAG,SAAS,CAYZ"}
1
+ {"version":3,"file":"archive-limits.d.ts","sourceRoot":"","sources":["../src/archive-limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,6BAA6B,QAAoB,CAAC;AAC/D,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAC1C,eAAO,MAAM,2BAA2B,QAAoB,CAAC;AAC7D,eAAO,MAAM,uBAAuB,QAAoB,CAAC;AACzD,eAAO,MAAM,4BAA4B,QAAc,CAAC;AACxD,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD,eAAO,MAAM,wBAAwB;aACnC,0BAA0B,EAAE,4BAA4B;aACxD,yBAAyB,EAAE,mCAAmC;aAC9D,kCAAkC,EAAE,4CAA4C;aAChF,4BAA4B,EAAE,sCAAsC;aACpE,0BAA0B,EAAE,oCAAoC;aAChE,6BAA6B,EAAE,uCAAuC;aACtE,2BAA2B,EAAE,qCAAqC;aAClE,mCAAmC,EAAE,6CAA6C;CAC1E,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAC/B,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,OAAO,wBAAwB,CAAC,CAAC;AAiB3E,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IAErC,YAAY,IAAI,EAAE,qBAAqB,EAItC;CACF;AAED,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AAE1E,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,4BAA4B,EAC5D,YAAY,GAAG,mBAAmB,CAAC,GAAG;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,cAAc,CAkBpF;AAUD,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,oBAAoB,GAC5B,4BAA4B,CAY9B;AAED,wBAAgB,2CAA2C,CACzD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,4BAA4B,GACnC,IAAI,CASN;AAED,wBAAgB,kCAAkC,CAChD,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,4BAA4B,QAKrC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,4BAA4B,GAAG;IAC7E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC,CAiCA;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE;IACnD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,GAAG,SAAS,CAYZ"}
@@ -34,9 +34,6 @@ export class ArchiveLimitError extends Error {
34
34
  }
35
35
  }
36
36
  export const MAX_TAR_MANIFEST_BYTES = 64 * 1024 * 1024;
37
- export function tarManifestEntryCost(path) {
38
- return 64 + 2 * Buffer.byteLength(path, "utf8");
39
- }
40
37
  export function resolveTarMeterLimits(options) {
41
38
  const limits = resolveExtractLimits(options);
42
39
  const payload = Math.min(limits.maxExtractedBytes, Number.MAX_SAFE_INTEGER);
@@ -1 +1 @@
1
- {"version":3,"file":"archive-native.d.ts","sourceRoot":"","sources":["../src/archive-native.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAML,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAQlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA2BjD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACrC,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;CAClD,GAAG,OAAO,CAAC,IAAI,CAAC,CAyJhB"}
1
+ {"version":3,"file":"archive-native.d.ts","sourceRoot":"","sources":["../src/archive-native.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAML,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAQlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuBjD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACrC,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAC;IACnD,UAAU,CAAC,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;CAClD,GAAG,OAAO,CAAC,IAAI,CAAC,CAyJhB"}
@@ -1,6 +1,7 @@
1
+ import { classifyArchiveParserError } from "./archive-parser-errors.js";
1
2
  import { constants as fsConstants } from "node:fs";
2
3
  import fs from "node:fs/promises";
3
- import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, isArchiveTarPathErrorMessage, } from "./archive-errors.js";
4
+ import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
4
5
  import { formatErrorDetail } from "./error-detail.js";
5
6
  import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
6
7
  import { stageArchiveFileForExtraction } from "./archive-input.js";
@@ -18,13 +19,9 @@ function policyKind(kind) {
18
19
  }
19
20
  function throwMappedNativeError(error) {
20
21
  if (error instanceof Error) {
21
- if (isArchiveTarPathErrorMessage(error.message)) {
22
- throw new ArchiveSecurityError("entry-path", error.message, { cause: error });
23
- }
24
- for (const code of Object.values(ARCHIVE_LIMIT_ERROR_CODE)) {
25
- if (error.message.includes(code))
26
- throw new ArchiveLimitError(code);
27
- }
22
+ const mapped = classifyArchiveParserError(error.message, { cause: error });
23
+ if (mapped)
24
+ throw mapped;
28
25
  if (isArchiveFormatErrorMessage(error.message)) {
29
26
  throw new ArchiveFormatError(error.message, { cause: error });
30
27
  }
@@ -0,0 +1,2 @@
1
+ export declare function classifyArchiveParserError(message: string, options?: ErrorOptions): Error | undefined;
2
+ //# sourceMappingURL=archive-parser-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive-parser-errors.d.ts","sourceRoot":"","sources":["../src/archive-parser-errors.ts"],"names":[],"mappings":"AAGA,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,KAAK,GAAG,SAAS,CAQrG"}
@@ -0,0 +1,12 @@
1
+ import { ArchiveSecurityError, isArchiveTarPathErrorMessage } from "./archive-errors.js";
2
+ import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError } from "./archive-limits.js";
3
+ export function classifyArchiveParserError(message, options) {
4
+ if (isArchiveTarPathErrorMessage(message)) {
5
+ return new ArchiveSecurityError("entry-path", message, options);
6
+ }
7
+ for (const code of Object.values(ARCHIVE_LIMIT_ERROR_CODE)) {
8
+ if (message.includes(code))
9
+ return new ArchiveLimitError(code);
10
+ }
11
+ return undefined;
12
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"archive-read.d.ts","sourceRoot":"","sources":["../src/archive-read.ts"],"names":[],"mappings":"AAcA,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkLzE,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAAE,GAChD,OAAO,CAAC,MAAM,CAAC,CAuFjB"}
1
+ {"version":3,"file":"archive-read.d.ts","sourceRoot":"","sources":["../src/archive-read.ts"],"names":[],"mappings":"AAcA,OAAO,EAAiD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkLpG,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAA;CAAE,GAChD,OAAO,CAAC,MAAM,CAAC,CA6EjB"}
@@ -1,10 +1,11 @@
1
+ import { classifyArchiveParserError } from "./archive-parser-errors.js";
1
2
  import fs from "node:fs/promises";
2
3
  import { Readable } from "node:stream";
3
4
  import { readFileHandleBounded } from "./bounded-read.js";
4
- import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, isArchiveTarPathErrorMessage, } from "./archive-errors.js";
5
+ import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
5
6
  import { formatErrorDetail } from "./error-detail.js";
6
7
  import { stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
7
- import { resolveArchiveKind } from "./archive-kind.js";
8
+ import { assertPortableArchiveKind, resolveArchiveKind } from "./archive-kind.js";
8
9
  import { DEFAULT_MAX_ARCHIVE_BYTES_ZIP, ArchiveLimitError, ARCHIVE_LIMIT_ERROR_CODE, } from "./archive-limits.js";
9
10
  import { inspectTar, replayTar } from "./archive-tar-stream.js";
10
11
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
@@ -202,14 +203,10 @@ export async function readArchiveEntry(archivePath, entryPath, options) {
202
203
  return await native.readArchiveEntryNative(staged.path, kind, rawEntryPath, options.maxBytes, limits, signal);
203
204
  }
204
205
  catch (error) {
205
- if (error instanceof Error && isArchiveTarPathErrorMessage(error.message)) {
206
- throw new ArchiveSecurityError("entry-path", error.message, { cause: error });
207
- }
208
206
  if (error instanceof Error) {
209
- for (const code of Object.values(ARCHIVE_LIMIT_ERROR_CODE)) {
210
- if (error.message.includes(code))
211
- throw new ArchiveLimitError(code);
212
- }
207
+ const mapped = classifyArchiveParserError(error.message, { cause: error });
208
+ if (mapped)
209
+ throw mapped;
213
210
  }
214
211
  if (error instanceof Error && isArchiveFormatErrorMessage(error.message)) {
215
212
  throw new ArchiveFormatError(error.message, { cause: error });
@@ -217,10 +214,7 @@ export async function readArchiveEntry(archivePath, entryPath, options) {
217
214
  throw error;
218
215
  }
219
216
  }
220
- if (kind === "tar-zstd" || kind === "tar-bzip2") {
221
- throw new FsSafeError("helper-unavailable", `${kind} archives require the matching optional native platform package; ` +
222
- "install @openclaw/fs-safe with optional dependencies enabled on a supported platform and use FS_SAFE_NATIVE_MODE=auto or require");
223
- }
217
+ assertPortableArchiveKind(kind);
224
218
  return kind === "zip"
225
219
  ? await readZipEntry(staged.buffer, requestedEntry, options.maxBytes)
226
220
  : await readTarEntry(staged.path, requestedEntry, options.maxBytes);
@@ -1,5 +1,5 @@
1
1
  import { Transform, type TransformCallback } from "node:stream";
2
- import { type TarMeterLimits } from "./archive-limits.js";
2
+ import type { TarMeterLimits } from "./archive-limits.js";
3
3
  import type { TarEntryInfo } from "./archive-tar.js";
4
4
  export type AdmittedTarMember = TarEntryInfo & {
5
5
  offset: number;
@@ -1 +1 @@
1
- {"version":3,"file":"archive-tar-wasm.d.ts","sourceRoot":"","sources":["../src/archive-tar-wasm.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAA+C,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAyClE,kFAAkF;AAClF,qBAAa,eAAgB,SAAQ,SAAS;IAER,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAD9D,OAAO,CAAC,GAAG,CAAkB;IAC7B,YAAY,MAAM,EAAE,cAAc,EAAmB,QAAQ,CAAC,GAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,aAAA,EAQjG;IACD,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,IAAI;IAIH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAkB/F;IACQ,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAKjD;IACQ,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAKpF;CACF"}
1
+ {"version":3,"file":"archive-tar-wasm.d.ts","sourceRoot":"","sources":["../src/archive-tar-wasm.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAuClE,kFAAkF;AAClF,qBAAa,eAAgB,SAAQ,SAAS;IAER,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAD9D,OAAO,CAAC,GAAG,CAAkB;IAC7B,YAAY,MAAM,EAAE,cAAc,EAAmB,QAAQ,CAAC,GAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,aAAA,EAQjG;IACD,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,IAAI;IAIH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAkB/F;IACQ,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAKjD;IACQ,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAKpF;CACF"}
@@ -1,7 +1,7 @@
1
+ import { classifyArchiveParserError } from "./archive-parser-errors.js";
1
2
  import { readFileSync } from "node:fs";
2
3
  import { Transform } from "node:stream";
3
- import { ArchiveFormatError, ArchiveSecurityError, isArchiveTarPathErrorMessage } from "./archive-errors.js";
4
- import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError } from "./archive-limits.js";
4
+ import { ArchiveFormatError } from "./archive-errors.js";
5
5
  // Node exposes WebAssembly without DOM globals; keep the private ABI types local.
6
6
  const wasm = globalThis.WebAssembly;
7
7
  let compiled;
@@ -18,12 +18,9 @@ const types = new Map([
18
18
  [54, "FIFO"], [55, "ContiguousFile"], [68, "GNUDumpDir"],
19
19
  ]);
20
20
  function parserError(message) {
21
- if (isArchiveTarPathErrorMessage(message))
22
- return new ArchiveSecurityError("entry-path", message);
23
- for (const code of Object.values(ARCHIVE_LIMIT_ERROR_CODE)) {
24
- if (message.includes(code))
25
- return new ArchiveLimitError(code);
26
- }
21
+ const mapped = classifyArchiveParserError(message);
22
+ if (mapped)
23
+ return mapped;
27
24
  return new ArchiveFormatError(message.replace(/^archive-header-invalid:/, "invalid TAR header:"));
28
25
  }
29
26
  /** Backpressure-aware transport only; all TAR semantics live in the Rust core. */
@@ -1 +1 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AAwDA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AA6MpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgEjF"}
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA6DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AA6MpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DjF"}
package/dist/archive.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { createTarEntryPreflightChecker } from "./archive-tar.js";
2
+ import { inspectTar, replayTar } from "./archive-tar-stream.js";
3
+ import { runPinnedWriteHelper } from "./pinned-write.js";
1
4
  import fsSync, { constants as fsConstants } from "node:fs";
2
5
  import fs from "node:fs/promises";
3
6
  import path from "node:path";
@@ -6,7 +9,7 @@ import { pipeline } from "node:stream/promises";
6
9
  import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
7
10
  import { createPipelineTimeoutError, waitForDeadline, withExtractionDeadline, } from "./archive-deadline.js";
8
11
  import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, createExtractBudgetTransform, resolveExtractLimits, resolveTarMeterLimits, } from "./archive-limits.js";
9
- import { resolveArchiveKind } from "./archive-kind.js";
12
+ import { assertPortableArchiveKind, resolveArchiveKind } from "./archive-kind.js";
10
13
  import { prepareArchiveDestinationDir, preparePrivateArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
11
14
  import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
12
15
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
@@ -18,7 +21,6 @@ import { extractNativeArchive } from "./archive-native.js";
18
21
  import { stageArchiveFileForExtraction } from "./archive-input.js";
19
22
  import { getNativeBinding } from "./native.js";
20
23
  import { resolveArchiveEntryMode, resolveArchiveFilteredEntryPolicy, shouldExtractArchiveEntry, } from "./archive-policy.js";
21
- import { extractWasmTar } from "./archive-tar-extract.js";
22
24
  import { writeSiblingTempFile } from "./sibling-temp.js";
23
25
  export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
24
26
  export { resolveArchiveKind, resolvePackedRootDir } from "./archive-kind.js";
@@ -221,10 +223,7 @@ export async function extractArchive(params) {
221
223
  }));
222
224
  return;
223
225
  }
224
- if (kind === "tar-zstd" || kind === "tar-bzip2") {
225
- throw new FsSafeError("helper-unavailable", `${kind} archives require the matching optional native platform package; ` +
226
- "install @openclaw/fs-safe with optional dependencies enabled on a supported platform and use FS_SAFE_NATIVE_MODE=auto or require");
227
- }
226
+ assertPortableArchiveKind(kind);
228
227
  if (kind === "tar") {
229
228
  await withExtractionDeadline(params.timeoutMs, label, async (deadline) => {
230
229
  const stagedArchive = await stageArchiveFileForExtraction({
@@ -252,3 +251,41 @@ export async function extractArchive(params) {
252
251
  onFiltered,
253
252
  }));
254
253
  }
254
+ async function extractWasmTar(params) {
255
+ const { options, deadline, tarLimits } = params;
256
+ const manifest = [];
257
+ await inspectTar({ archivePath: params.archivePath, limits: tarLimits, signal: deadline.signal,
258
+ onMember: (entry) => { manifest.push(entry); } });
259
+ deadline.check();
260
+ const destinationRealDir = await prepareArchiveDestinationDir(options.destDir);
261
+ await withStagedArchiveDestination({ destinationRealDir, run: async (stagingPath) => {
262
+ const stagingDir = await fs.realpath(stagingPath);
263
+ const check = createTarEntryPreflightChecker({ rootDir: destinationRealDir,
264
+ stripComponents: options.stripComponents, limits: params.limits,
265
+ entryFilter: options.entryFilter, onFiltered: options.onFiltered });
266
+ const strip = Math.max(0, Math.floor(options.stripComponents ?? 0));
267
+ const accepted = manifest.filter((entry) => { deadline.check(); return check(entry); }).map((entry) => {
268
+ const kind = entry.type === "Directory" || entry.type === "GNUDumpDir" ? "directory" : "file";
269
+ return { ...entry, path: stripArchivePath(entry.path, strip), kind,
270
+ mode: resolveArchiveEntryMode({ kind, archivedMode: entry.mode, policy: options.entryModes }) };
271
+ });
272
+ await replayTar({ archivePath: params.archivePath, limits: tarLimits, signal: deadline.signal, members: accepted,
273
+ async consume(member, payload) {
274
+ deadline.check();
275
+ await preparePrivateArchiveOutputPath({ destinationDir: stagingDir, destinationRealDir: stagingDir,
276
+ relPath: member.path, outPath: path.join(stagingDir, member.path), originalPath: member.path,
277
+ isDirectory: member.kind === "directory", deadline });
278
+ if (member.kind === "file") {
279
+ await runPinnedWriteHelper({ rootPath: stagingDir, relativeParentPath: path.posix.dirname(member.path),
280
+ basename: path.posix.basename(member.path), mkdir: false, mode: 0o600, overwrite: false,
281
+ maxBytes: member.size, input: { kind: "stream", stream: Readable.from(payload) } });
282
+ }
283
+ deadline.check();
284
+ },
285
+ });
286
+ deadline.check();
287
+ await mergePlannedArchiveIntoDestination({ entries: accepted, sourceDir: stagingDir,
288
+ destinationDir: options.destDir, destinationRealDir, deadline });
289
+ deadline.check();
290
+ } });
291
+ }
package/dist/atomic.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { replaceFileAtomic, replaceFileAtomicSync, type RenameIdentityPolicy, type ReplaceFileAtomicFileSystem, type ReplaceFileAtomicOptions, type ReplaceFileAtomicRestoreCleanup, type ReplaceFileAtomicRestoreFailureDetails, type ReplaceFileAtomicResult, type ReplaceFileCopyFallbackRestorePolicy, type ReplaceFileDestinationHardlinkPolicy, type ReplaceFileAtomicSyncFileSystem, type ReplaceFileAtomicSyncOptions, } from "./replace-file.js";
2
2
  export { writeTextAtomic, type WriteTextAtomicOptions } from "./text-atomic.js";
3
3
  export { replaceDirectoryAtomic, type ReplaceDirectoryAtomicOptions } from "./replace-directory.js";
4
- export { movePathWithCopyFallback, type MovePathWithCopyFallbackOptions } from "./move-path.js";
4
+ export { movePathWithCopyFallback, type MovePathPublicationReceipt, type MovePathWithCopyFallbackOptions, } from "./move-path.js";
5
5
  //# sourceMappingURL=atomic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"atomic.d.ts","sourceRoot":"","sources":["../src/atomic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,+BAA+B,EACpC,KAAK,sCAAsC,EAC3C,KAAK,uBAAuB,EAC5B,KAAK,oCAAoC,EACzC,KAAK,oCAAoC,EACzC,KAAK,+BAA+B,EACpC,KAAK,4BAA4B,GAClC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,KAAK,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,EAAE,wBAAwB,EAAE,KAAK,+BAA+B,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"atomic.d.ts","sourceRoot":"","sources":["../src/atomic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,+BAA+B,EACpC,KAAK,sCAAsC,EAC3C,KAAK,uBAAuB,EAC5B,KAAK,oCAAoC,EACzC,KAAK,oCAAoC,EACzC,KAAK,+BAA+B,EACpC,KAAK,4BAA4B,GAClC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,KAAK,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,EACL,wBAAwB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,gBAAgB,CAAC"}
package/dist/atomic.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { replaceFileAtomic, replaceFileAtomicSync, } from "./replace-file.js";
2
2
  export { writeTextAtomic } from "./text-atomic.js";
3
3
  export { replaceDirectoryAtomic } from "./replace-directory.js";
4
- export { movePathWithCopyFallback } from "./move-path.js";
4
+ export { movePathWithCopyFallback, } from "./move-path.js";
@@ -1,8 +1,4 @@
1
1
  import { Transform } from "node:stream";
2
2
  export declare function createMaxBytesTransform(maxBytes: number): Transform;
3
- export declare function createBoundedReadStream(opened: {
4
- handle: {
5
- createReadStream(): NodeJS.ReadableStream;
6
- };
7
- }, maxBytes: number | undefined): NodeJS.ReadableStream;
3
+ export declare function createByteLimitTransform(maxBytes: number, overflowError: (bytes: number) => Error): Transform;
8
4
  //# sourceMappingURL=bounded-read-stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,aAAa,CAAC;AAIlD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAmBnE;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE;IAAE,MAAM,EAAE;QAAE,gBAAgB,IAAI,MAAM,CAAC,cAAc,CAAA;KAAE,CAAA;CAAE,EACjE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAAC,cAAc,CAWvB"}
1
+ {"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAMnE;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,GACtC,SAAS,CAaX"}
@@ -1,29 +1,21 @@
1
- import { pipeline, Transform } from "node:stream";
1
+ import { Transform } from "node:stream";
2
2
  import { normalizeMaxBytes } from "./byte-budget.js";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  export function createMaxBytesTransform(maxBytes) {
5
5
  normalizeMaxBytes(maxBytes);
6
+ return createByteLimitTransform(maxBytes, (bytes) => new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
7
+ }
8
+ export function createByteLimitTransform(maxBytes, overflowError) {
6
9
  let bytes = 0;
7
10
  return new Transform({
8
11
  transform(chunk, _encoding, callback) {
9
12
  const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
10
13
  bytes += buffer.byteLength;
11
14
  if (bytes > maxBytes) {
12
- callback(new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
15
+ callback(overflowError(bytes));
13
16
  return;
14
17
  }
15
18
  callback(null, buffer);
16
19
  },
17
20
  });
18
21
  }
19
- export function createBoundedReadStream(opened, maxBytes) {
20
- const stream = opened.handle.createReadStream();
21
- if (maxBytes === undefined) {
22
- return stream;
23
- }
24
- const bounded = createMaxBytesTransform(maxBytes);
25
- // pipeline couples teardown in both directions: source failures reach the
26
- // returned stream, and an early consumer close destroys the file stream.
27
- pipeline(stream, bounded, () => undefined);
28
- return bounded;
29
- }
@@ -1,5 +1,4 @@
1
1
  export declare function normalizeMaxBytes(value: number | undefined, options?: {
2
2
  defaultValue?: number;
3
- label?: string;
4
3
  }): number | undefined;
5
4
  //# sourceMappingURL=byte-budget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"byte-budget.d.ts","sourceRoot":"","sources":["../src/byte-budget.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,MAAM,GAAG,SAAS,CASpB"}
1
+ {"version":3,"file":"byte-budget.d.ts","sourceRoot":"","sources":["../src/byte-budget.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,MAAM,GAAG,SAAS,CASpB"}
@@ -3,7 +3,7 @@ export function normalizeMaxBytes(value, options = {}) {
3
3
  if (selected === undefined || selected === Number.POSITIVE_INFINITY)
4
4
  return selected;
5
5
  if (!Number.isSafeInteger(selected) || selected < 0) {
6
- throw new RangeError(`${options.label ?? "maxBytes"} must be a non-negative safe integer or Infinity`);
6
+ throw new RangeError("maxBytes must be a non-negative safe integer or Infinity");
7
7
  }
8
8
  return selected;
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"directory-mode-owner.d.ts","sourceRoot":"","sources":["../src/directory-mode-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAIlD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,GAAG,WAAW,EAAE,MAAM,EAAE,KAAK,GAAG,WAAW,GAAG,IAAI,CAOrG;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,kBAAkB,CAuDrB"}
1
+ {"version":3,"file":"directory-mode-owner.d.ts","sourceRoot":"","sources":["../src/directory-mode-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAIlD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,GAAG,WAAW,EAAE,MAAM,EAAE,KAAK,GAAG,WAAW,GAAG,IAAI,CAOrG;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,kBAAkB,CAwDrB"}
@@ -32,6 +32,8 @@ export function ownDirectoryMode(params) {
32
32
  checks.check?.();
33
33
  const currentMode = await params.inspect();
34
34
  checks.check?.();
35
+ if (currentMode === mode && !checks.beforeChmod && !checks.check)
36
+ return;
35
37
  if (currentMode !== mode) {
36
38
  await params.prepareChmod?.();
37
39
  checks.check?.();
@@ -1,2 +1,3 @@
1
1
  export declare function formatErrorDetail(value: string): string;
2
+ export declare function shortPath(value: string): string;
2
3
  //# sourceMappingURL=error-detail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-detail.d.ts","sourceRoot":"","sources":["../src/error-detail.ts"],"names":[],"mappings":"AAGA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD"}
1
+ {"version":3,"file":"error-detail.d.ts","sourceRoot":"","sources":["../src/error-detail.ts"],"names":[],"mappings":"AAKA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C"}
@@ -1,4 +1,10 @@
1
+ import os from "node:os";
1
2
  const UNSAFE_ERROR_DETAIL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/gu;
2
3
  export function formatErrorDetail(value) {
3
4
  return value.replace(UNSAFE_ERROR_DETAIL_CHARACTERS, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
4
5
  }
6
+ export function shortPath(value) {
7
+ const home = os.homedir();
8
+ const shortened = value.startsWith(home) ? `~${value.slice(home.length)}` : value;
9
+ return formatErrorDetail(shortened);
10
+ }
@@ -1,4 +1,4 @@
1
- import { type Readable } from "node:stream";
1
+ import type { Readable } from "node:stream";
2
2
  import { type SyncDirectoryGuard } from "./directory-guard.js";
3
3
  import { type Root } from "./root.js";
4
4
  import { prepareSecretFileWrite } from "./secret-file.js";
@@ -1 +1 @@
1
- {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAgDD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
1
+ {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAsCD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
@@ -1,7 +1,7 @@
1
1
  import syncFs from "node:fs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { Transform } from "node:stream";
4
+ import { createByteLimitTransform } from "./bounded-read-stream.js";
5
5
  import { normalizeMaxBytes } from "./byte-budget.js";
6
6
  import { pipeline } from "node:stream/promises";
7
7
  import { assertSyncDirectoryGuard as assertDirectoryGuardSync, createSyncDirectoryGuard, } from "./directory-guard.js";
@@ -68,18 +68,7 @@ function createMaxBytesTransform(maxBytes) {
68
68
  if (limit === undefined) {
69
69
  return undefined;
70
70
  }
71
- let total = 0;
72
- return new Transform({
73
- transform(chunk, _encoding, callback) {
74
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
75
- total += buffer.byteLength;
76
- if (total > limit) {
77
- callback(new FsSafeError("too-large", `file exceeds maximum size of ${limit} bytes`));
78
- return;
79
- }
80
- callback(null, buffer);
81
- },
82
- });
71
+ return createByteLimitTransform(limit, () => new FsSafeError("too-large", `file exceeds maximum size of ${limit} bytes`));
83
72
  }
84
73
  export async function writeStreamToTempSource(params) {
85
74
  const maxBytes = normalizeMaxBytes(params.maxBytes);
@@ -11,7 +11,7 @@ import { writeFileSyncAtomic } from "./file-store-sync-write.js";
11
11
  import { createJsonStore } from "./json-document-store.js";
12
12
  import { stringifyJsonDocument } from "./json-stringify.js";
13
13
  import { isNotFoundPathError, resolveSafeRelativePath, splitSafeRelativePath } from "./path.js";
14
- import { throwFsSafeReadError } from "./read-error.js";
14
+ import { throwFsSafeReadError } from "./root-errors.js";
15
15
  import { root } from "./root.js";
16
16
  import { DEFAULT_ROOT_MAX_BYTES } from "./root-impl.js";
17
17
  import { readRegularFile } from "./regular-file.js";
@@ -0,0 +1,5 @@
1
+ import fs from "node:fs";
2
+ import type { FileHandle } from "node:fs/promises";
3
+ export declare function syncFileBestEffort(handle: Pick<FileHandle, "sync">): Promise<void>;
4
+ export declare function syncFileBestEffortSync(fd: number, fsModule?: Pick<typeof fs, "fsyncSync">): void;
5
+ //# sourceMappingURL=file-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-sync.d.ts","sourceRoot":"","sources":["../src/file-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxF;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,MAAM,EACV,QAAQ,GAAE,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAM,GAC1C,IAAI,CAMN"}
@@ -0,0 +1,19 @@
1
+ import fs from "node:fs";
2
+ export async function syncFileBestEffort(handle) {
3
+ try {
4
+ await handle.sync();
5
+ }
6
+ catch (error) {
7
+ if (error?.code !== "EPERM")
8
+ throw error;
9
+ }
10
+ }
11
+ export function syncFileBestEffortSync(fd, fsModule = fs) {
12
+ try {
13
+ fsModule.fsyncSync(fd);
14
+ }
15
+ catch (error) {
16
+ if (error?.code !== "EPERM")
17
+ throw error;
18
+ }
19
+ }