@openclaw/fs-safe 0.8.2 → 0.8.4

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 (200) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1 -1
  3. package/dist/absolute-path.d.ts.map +1 -1
  4. package/dist/absolute-path.js +8 -7
  5. package/dist/advanced.d.ts +2 -2
  6. package/dist/advanced.d.ts.map +1 -1
  7. package/dist/advanced.js +2 -2
  8. package/dist/archive-kind.d.ts +1 -0
  9. package/dist/archive-kind.d.ts.map +1 -1
  10. package/dist/archive-kind.js +6 -0
  11. package/dist/archive-limits.d.ts +0 -1
  12. package/dist/archive-limits.d.ts.map +1 -1
  13. package/dist/archive-limits.js +0 -3
  14. package/dist/archive-native.d.ts +1 -0
  15. package/dist/archive-native.d.ts.map +1 -1
  16. package/dist/archive-native.js +20 -76
  17. package/dist/archive-parser-errors.d.ts +2 -0
  18. package/dist/archive-parser-errors.d.ts.map +1 -0
  19. package/dist/archive-parser-errors.js +12 -0
  20. package/dist/archive-plan.d.ts +20 -0
  21. package/dist/archive-plan.d.ts.map +1 -0
  22. package/dist/archive-plan.js +55 -0
  23. package/dist/archive-read.d.ts.map +1 -1
  24. package/dist/archive-read.js +7 -13
  25. package/dist/archive-tar-inspect.d.ts +7 -0
  26. package/dist/archive-tar-inspect.d.ts.map +1 -0
  27. package/dist/archive-tar-inspect.js +59 -0
  28. package/dist/archive-tar-wasm.d.ts +1 -1
  29. package/dist/archive-tar-wasm.d.ts.map +1 -1
  30. package/dist/archive-tar-wasm.js +5 -8
  31. package/dist/archive-tar.d.ts +3 -1
  32. package/dist/archive-tar.d.ts.map +1 -1
  33. package/dist/archive-tar.js +12 -49
  34. package/dist/archive.d.ts +1 -0
  35. package/dist/archive.d.ts.map +1 -1
  36. package/dist/archive.js +54 -33
  37. package/dist/atomic.d.ts +1 -1
  38. package/dist/atomic.d.ts.map +1 -1
  39. package/dist/atomic.js +1 -1
  40. package/dist/bounded-read-stream.d.ts +1 -5
  41. package/dist/bounded-read-stream.d.ts.map +1 -1
  42. package/dist/bounded-read-stream.js +5 -13
  43. package/dist/bounded-read.d.ts.map +1 -1
  44. package/dist/bounded-read.js +31 -4
  45. package/dist/byte-budget.d.ts +0 -1
  46. package/dist/byte-budget.d.ts.map +1 -1
  47. package/dist/byte-budget.js +1 -1
  48. package/dist/directory-durability.js +5 -5
  49. package/dist/directory-guard.d.ts.map +1 -1
  50. package/dist/directory-guard.js +5 -6
  51. package/dist/directory-mode-owner.d.ts.map +1 -1
  52. package/dist/directory-mode-owner.js +2 -0
  53. package/dist/error-detail.d.ts +1 -0
  54. package/dist/error-detail.d.ts.map +1 -1
  55. package/dist/error-detail.js +6 -0
  56. package/dist/file-store-boundary.d.ts +1 -1
  57. package/dist/file-store-boundary.d.ts.map +1 -1
  58. package/dist/file-store-boundary.js +3 -14
  59. package/dist/file-store-prune.js +17 -8
  60. package/dist/file-store.js +3 -3
  61. package/dist/file-sync.d.ts +5 -0
  62. package/dist/file-sync.d.ts.map +1 -0
  63. package/dist/file-sync.js +19 -0
  64. package/dist/guarded-mkdir.d.ts.map +1 -1
  65. package/dist/guarded-mkdir.js +5 -4
  66. package/dist/guarded-mutation.d.ts +3 -0
  67. package/dist/guarded-mutation.d.ts.map +1 -1
  68. package/dist/guarded-mutation.js +5 -1
  69. package/dist/home-dir.d.ts +0 -10
  70. package/dist/home-dir.d.ts.map +1 -1
  71. package/dist/home-dir.js +0 -28
  72. package/dist/json-durable-queue-ownership.d.ts +1 -0
  73. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  74. package/dist/json-durable-queue-ownership.js +139 -18
  75. package/dist/json-durable-queue.d.ts.map +1 -1
  76. package/dist/json-durable-queue.js +5 -10
  77. package/dist/json-store.d.ts.map +1 -1
  78. package/dist/json-store.js +1 -1
  79. package/dist/local-roots.d.ts.map +1 -1
  80. package/dist/local-roots.js +2 -3
  81. package/dist/move-path-cleanup.d.ts +1 -1
  82. package/dist/move-path-cleanup.d.ts.map +1 -1
  83. package/dist/move-path-cleanup.js +4 -2
  84. package/dist/move-path.d.ts +9 -0
  85. package/dist/move-path.d.ts.map +1 -1
  86. package/dist/move-path.js +53 -16
  87. package/dist/native-binding.d.ts +2 -1
  88. package/dist/native-binding.d.ts.map +1 -1
  89. package/dist/native-operations.d.ts +0 -1
  90. package/dist/native-operations.d.ts.map +1 -1
  91. package/dist/native-operations.js +0 -10
  92. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  93. package/dist/native-pinned-write-windows.js +8 -4
  94. package/dist/native-pinned-write.js +3 -3
  95. package/dist/native-staged-file.d.ts +10 -3
  96. package/dist/native-staged-file.d.ts.map +1 -1
  97. package/dist/native-staged-file.js +38 -13
  98. package/dist/opened-realpath.d.ts +12 -1
  99. package/dist/opened-realpath.d.ts.map +1 -1
  100. package/dist/opened-realpath.js +16 -14
  101. package/dist/output.d.ts.map +1 -1
  102. package/dist/output.js +26 -2
  103. package/dist/path-policy.js +3 -3
  104. package/dist/permissions.d.ts +1 -0
  105. package/dist/permissions.d.ts.map +1 -1
  106. package/dist/permissions.js +3 -0
  107. package/dist/pinned-write.d.ts +2 -2
  108. package/dist/pinned-write.d.ts.map +1 -1
  109. package/dist/pinned-write.js +17 -22
  110. package/dist/private-temp-workspace.js +1 -1
  111. package/dist/publish-file.js +2 -2
  112. package/dist/regular-file.js +8 -8
  113. package/dist/replace-file-copy-source.d.ts.map +1 -1
  114. package/dist/replace-file-copy-source.js +2 -7
  115. package/dist/replace-file-descriptor.d.ts.map +1 -1
  116. package/dist/replace-file-descriptor.js +11 -20
  117. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  118. package/dist/replace-file-temp-owner.js +15 -14
  119. package/dist/replace-file.d.ts.map +1 -1
  120. package/dist/replace-file.js +21 -8
  121. package/dist/root-context.js +5 -5
  122. package/dist/root-errors.d.ts +1 -0
  123. package/dist/root-errors.d.ts.map +1 -1
  124. package/dist/root-errors.js +9 -0
  125. package/dist/root-file.d.ts.map +1 -1
  126. package/dist/root-file.js +17 -28
  127. package/dist/root-impl.d.ts +2 -1
  128. package/dist/root-impl.d.ts.map +1 -1
  129. package/dist/root-impl.js +46 -58
  130. package/dist/root-path-existing.d.ts.map +1 -1
  131. package/dist/root-path-existing.js +2 -3
  132. package/dist/root-path-symlink.d.ts.map +1 -1
  133. package/dist/root-path-symlink.js +2 -3
  134. package/dist/root-path.d.ts.map +1 -1
  135. package/dist/root-path.js +51 -97
  136. package/dist/root-paths.d.ts.map +1 -1
  137. package/dist/root-paths.js +16 -15
  138. package/dist/root-write-mode.d.ts +6 -0
  139. package/dist/root-write-mode.d.ts.map +1 -0
  140. package/dist/root-write-mode.js +61 -0
  141. package/dist/root-write-verification.js +8 -8
  142. package/dist/safe-path-segment.d.ts +1 -0
  143. package/dist/safe-path-segment.d.ts.map +1 -1
  144. package/dist/safe-path-segment.js +1 -1
  145. package/dist/secret-file.d.ts.map +1 -1
  146. package/dist/secret-file.js +4 -11
  147. package/dist/secret-read-async.d.ts.map +1 -1
  148. package/dist/secret-read-async.js +2 -10
  149. package/dist/secret-read-policy.d.ts +4 -0
  150. package/dist/secret-read-policy.d.ts.map +1 -1
  151. package/dist/secret-read-policy.js +12 -0
  152. package/dist/sibling-staged-file.d.ts.map +1 -1
  153. package/dist/sibling-staged-file.js +2 -7
  154. package/dist/sidecar-lock-policy.d.ts +0 -1
  155. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  156. package/dist/sidecar-lock-policy.js +0 -4
  157. package/dist/strict-file-identity.d.ts +1 -1
  158. package/dist/strict-file-identity.d.ts.map +1 -1
  159. package/dist/string-coerce.d.ts +0 -9
  160. package/dist/string-coerce.d.ts.map +1 -1
  161. package/dist/string-coerce.js +0 -56
  162. package/dist/symlink-parents.d.ts.map +1 -1
  163. package/dist/symlink-parents.js +1 -2
  164. package/dist/temp-target.d.ts.map +1 -1
  165. package/dist/temp-target.js +1 -12
  166. package/docs/archive.md +57 -0
  167. package/docs/atomic.md +53 -19
  168. package/docs/reading.md +2 -0
  169. package/docs/root.md +10 -1
  170. package/docs/staged-file.md +6 -2
  171. package/docs/types.md +2 -1
  172. package/docs/writing.md +30 -2
  173. package/package.json +8 -8
  174. package/dist/archive-tar-extract.d.ts +0 -11
  175. package/dist/archive-tar-extract.d.ts.map +0 -1
  176. package/dist/archive-tar-extract.js +0 -49
  177. package/dist/fsync.d.ts +0 -2
  178. package/dist/fsync.d.ts.map +0 -1
  179. package/dist/fsync.js +0 -1
  180. package/dist/json-durable-queue-retirement.d.ts +0 -9
  181. package/dist/json-durable-queue-retirement.d.ts.map +0 -1
  182. package/dist/json-durable-queue-retirement.js +0 -126
  183. package/dist/json-durable-queue-transfer-lock.d.ts +0 -2
  184. package/dist/json-durable-queue-transfer-lock.d.ts.map +0 -1
  185. package/dist/json-durable-queue-transfer-lock.js +0 -19
  186. package/dist/mode.d.ts +0 -2
  187. package/dist/mode.d.ts.map +0 -1
  188. package/dist/mode.js +0 -3
  189. package/dist/output-sibling.d.ts +0 -8
  190. package/dist/output-sibling.d.ts.map +0 -1
  191. package/dist/output-sibling.js +0 -30
  192. package/dist/read-error.d.ts +0 -2
  193. package/dist/read-error.d.ts.map +0 -1
  194. package/dist/read-error.js +0 -11
  195. package/dist/short-path.d.ts +0 -2
  196. package/dist/short-path.d.ts.map +0 -1
  197. package/dist/short-path.js +0 -7
  198. package/dist/staged-file.d.ts +0 -10
  199. package/dist/staged-file.d.ts.map +0 -1
  200. package/dist/staged-file.js +0 -15
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.8.4 - 2026-09-07
6
+
7
+ - Add bounded `inspectTarArchive` with complete native/WASM admission and the same canonical extraction planner, preserving effective member identities without materializing an output tree.
8
+ - Preserve inherited and getter-backed extraction options on the WASM TAR path, matching native and ZIP handling for destinations, filters, stripping, and modes.
9
+ - Apply the existing filesystem-test worker cap locally as well as in CI, and drain archive publication fixtures before resetting hooks or cleaning restricted directories after timeouts.
10
+ - Add `durable` to Root write/create/writeJson/createJson/append options and Root defaults; `durable: false` skips file and parent fsync for reconstructible data (default unchanged: durable).
11
+ - Run metadata checks inside async operations synchronously (microseconds each), cutting event-loop round-trips per read/write while data I/O remains asynchronous and native canonical path spelling is preserved, including Windows short paths.
12
+
13
+ ## 0.8.3 - 2026-09-06
14
+
15
+ - 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.
16
+
17
+ - Simplify internals without changing public behavior: remove unused string/home helpers, `resolveUserPath`, `createBoundedReadStream`, `sidecarLockPayloadIsStale`, and `tarManifestEntryCost`; share filesystem utilities and merge private modules.
18
+ - Reduce filesystem calls per read and write while retaining boundary, hardlink, hook, retry, and post-mutation identity checks.
19
+ - 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.
20
+ - Add 1 MiB read/write and existing-mode inheritance benchmarks, with native-mode metadata and per-case iteration counts.
21
+
3
22
  ## 0.8.2 - 2026-09-05
4
23
 
5
24
  **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 +1 @@
1
- {"version":3,"file":"absolute-path.d.ts","sourceRoot":"","sources":["../src/absolute-path.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAIhE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAU5D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,GAAG;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAmL7D,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWhE;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnF;AAuBD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,6BAA6B,CAAC,CA+ExC;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAazF;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,oBAAoB,CAAC,CAgB/B;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,4BAA4B,CAAC,CA2BvC"}
1
+ {"version":3,"file":"absolute-path.d.ts","sourceRoot":"","sources":["../src/absolute-path.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAIhE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAU5D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,GAAG;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAmL7D,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWhE;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnF;AAuBD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,6BAA6B,CAAC,CA+ExC;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAazF;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,oBAAoB,CAAC,CAgB/B;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,yBAAyB,CAAA;CAAO,GACrD,OAAO,CAAC,4BAA4B,CAAC,CA2BvC"}
@@ -1,3 +1,4 @@
1
+ import fsSync from "node:fs";
1
2
  import fs from "node:fs/promises";
2
3
  import path from "node:path";
3
4
  import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, } from "./directory-guard.js";
@@ -66,7 +67,7 @@ async function directoryGuardFailure(err, dir, scopeLabel) {
66
67
  return { ok: false, code: err.code, error: err };
67
68
  }
68
69
  try {
69
- const stat = await fs.lstat(dir);
70
+ const stat = fsSync.lstatSync(dir);
70
71
  const failure = classifyExistingDirectorySegment(stat, scopeLabel);
71
72
  if (failure) {
72
73
  return failure;
@@ -86,7 +87,7 @@ async function resolveTrustedDirectoryPrefix(targetPath, scopeLabel) {
86
87
  let current = root;
87
88
  let currentStat;
88
89
  try {
89
- currentStat = await fs.lstat(current);
90
+ currentStat = fsSync.lstatSync(current);
90
91
  }
91
92
  catch (err) {
92
93
  const failure = classifyDirectoryLookupError(err, scopeLabel);
@@ -110,7 +111,7 @@ async function resolveTrustedDirectoryPrefix(targetPath, scopeLabel) {
110
111
  }
111
112
  const next = path.join(current, segment);
112
113
  try {
113
- const nextStat = await fs.lstat(next);
114
+ const nextStat = fsSync.lstatSync(next);
114
115
  const segmentFailure = classifyExistingDirectorySegment(nextStat, scopeLabel);
115
116
  if (segmentFailure) {
116
117
  return segmentFailure;
@@ -155,7 +156,7 @@ async function findExistingAncestorWithStat(filePath) {
155
156
  let current = path.resolve(filePath);
156
157
  while (true) {
157
158
  try {
158
- return { path: current, stat: await fs.lstat(current) };
159
+ return { path: current, stat: fsSync.lstatSync(current) };
159
160
  }
160
161
  catch (err) {
161
162
  if (err.code !== "ENOENT") {
@@ -199,7 +200,7 @@ export async function ensureAbsoluteDirectory(dirPath, options = {}) {
199
200
  return guardResult;
200
201
  }
201
202
  try {
202
- const stat = await fs.lstat(current);
203
+ const stat = fsSync.lstatSync(current);
203
204
  if (stat.isSymbolicLink()) {
204
205
  return ensureDirectoryFailure("symlink", `directory path traverses a symlink within ${scopeLabel}`);
205
206
  }
@@ -250,7 +251,7 @@ export async function canonicalPathFromExistingAncestor(filePath) {
250
251
  }
251
252
  let canonicalAncestor = ancestor;
252
253
  try {
253
- canonicalAncestor = await fs.realpath(ancestor);
254
+ canonicalAncestor = fsSync.realpathSync.native(ancestor);
254
255
  }
255
256
  catch {
256
257
  // Keep lexical path when the existing ancestor cannot be canonicalized.
@@ -263,7 +264,7 @@ export async function resolveAbsolutePathForRead(filePath, options = {}) {
263
264
  const normalized = assertAbsolutePathInput(filePath);
264
265
  let canonicalPath;
265
266
  try {
266
- canonicalPath = await fs.realpath(normalized);
267
+ canonicalPath = fsSync.realpathSync.native(normalized);
267
268
  }
268
269
  catch (err) {
269
270
  if (err.code === "ENOENT") {
@@ -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);
@@ -3,6 +3,7 @@ import type { ArchiveKind } from "./archive-kind.js";
3
3
  import { type ResolvedArchiveExtractLimits, type TarMeterLimits } from "./archive-limits.js";
4
4
  import type { ExtractArchiveOptions } from "./archive-options.js";
5
5
  import type { NativeBinding } from "./native.js";
6
+ export declare function throwMappedNativeArchiveError(error: unknown): never;
6
7
  export declare function extractNativeArchive(params: {
7
8
  binding: NativeBinding;
8
9
  archivePath: string;
@@ -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":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAEL,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAOlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAYnE;AAED,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,CA6FhB"}
@@ -1,30 +1,19 @@
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 { formatErrorDetail } from "./error-detail.js";
5
- import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
4
+ import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
5
+ import { stripArchivePath, validateArchiveEntryPath } from "./archive-entry.js";
6
+ import { createArchiveEntryPlanner } from "./archive-plan.js";
6
7
  import { stageArchiveFileForExtraction } from "./archive-input.js";
7
- import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, } from "./archive-limits.js";
8
- import { resolveArchiveEntryMode, shouldExtractArchiveEntry } from "./archive-policy.js";
8
+ import { assertArchiveEntryCountWithinLimit, } from "./archive-limits.js";
9
9
  import { prepareArchiveDestinationDir, withStagedArchiveDestination, } from "./archive-staging.js";
10
10
  import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
11
11
  import { admitZipFile } from "./archive-zip-admission.js";
12
- function policyKind(kind) {
13
- if (kind === "file" || kind === "directory")
14
- return kind;
15
- if (kind === "symlink" || kind === "hardlink")
16
- return "symlink";
17
- return "other";
18
- }
19
- function throwMappedNativeError(error) {
12
+ export function throwMappedNativeArchiveError(error) {
20
13
  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
- }
14
+ const mapped = classifyArchiveParserError(error.message, { cause: error });
15
+ if (mapped)
16
+ throw mapped;
28
17
  if (isArchiveFormatErrorMessage(error.message)) {
29
18
  throw new ArchiveFormatError(error.message, { cause: error });
30
19
  }
@@ -53,7 +42,7 @@ export async function extractNativeArchive(params) {
53
42
  params.deadline.check();
54
43
  const manifest = await params.binding
55
44
  .inspectArchiveNative(stagedArchive.path, params.kind, tarLimits, params.deadline.signal)
56
- .catch(throwMappedNativeError);
45
+ .catch(throwMappedNativeArchiveError);
57
46
  params.deadline.check();
58
47
  assertArchiveEntryCountWithinLimit(manifest.length, limits);
59
48
  if (physicalCount !== undefined && manifest.length !== physicalCount) {
@@ -78,69 +67,24 @@ export async function extractNativeArchive(params) {
78
67
  for (const entry of manifest)
79
68
  validateArchiveEntryPath(entry.path);
80
69
  }
81
- const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
82
- const budget = createByteBudgetTracker(limits);
83
- const trackOutputPath = createArchiveOutputPathTracker();
70
+ const planEntry = createArchiveEntryPlanner({ ...params, rootDir: stagingDir }, params.kind);
84
71
  const plan = [];
85
72
  for (const entry of manifest) {
86
73
  params.deadline.check();
87
- validateArchiveEntryPath(entry.path);
88
- const canonicalPath = stripArchivePath(entry.path, 0);
89
- if (!canonicalPath)
90
- continue;
91
- const relPath = stripArchivePath(canonicalPath, strip);
92
- if (!relPath)
93
- continue;
94
- validateArchiveEntryPath(relPath);
95
- assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
96
- trackOutputPath(relPath, entry.path);
97
- resolveArchiveOutputPath({ rootDir: stagingDir, relPath, originalPath: entry.path });
98
- const kind = policyKind(entry.kind);
99
- if (!shouldExtractArchiveEntry({
100
- filter: params.entryFilter,
101
- onFiltered: params.onFiltered,
102
- entry: { path: canonicalPath, kind, size: entry.size },
103
- })) {
104
- continue;
105
- }
106
- if (entry.kind === "sparse") {
107
- throw new ArchiveFormatError(`GNU sparse archive entry is not supported: ${formatErrorDetail(entry.path)}`);
108
- }
109
- if (kind === "symlink" || entry.kind === "blocked") {
110
- const label = params.kind === "zip" ? "zip" : "tar";
111
- throw new ArchiveSecurityError("entry-link", `${label} entry is a link: ${formatErrorDetail(entry.path)}`);
112
- }
113
- if (!Number.isSafeInteger(entry.size) || entry.size < 0) {
114
- throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
115
- }
116
- // GNUDumpDir can carry a body; charge accepted TAR sizes as JS does.
117
- if (kind === "file" || (kind === "directory" && params.kind !== "zip")) {
118
- budget.startEntry();
119
- budget.addEntrySize(entry.size);
120
- }
121
- if (kind === "file" || kind === "directory") {
122
- plan.push({
123
- index: entry.index,
124
- path: relPath,
125
- kind,
126
- size: entry.size,
127
- mode: resolveArchiveEntryMode({
128
- kind,
129
- archivedMode: params.kind === "zip"
130
- ? zipEntries[entry.index].creatorSystem === 3
131
- ? zipEntries[entry.index].externalAttributes >>> 16
132
- : undefined
133
- : entry.mode,
134
- policy: params.entryModes,
135
- }),
136
- });
137
- }
74
+ const mode = params.kind === "zip"
75
+ ? zipEntries[entry.index].creatorSystem === 3
76
+ ? zipEntries[entry.index].externalAttributes >>> 16
77
+ : undefined
78
+ : entry.mode;
79
+ const accepted = planEntry({ ...entry, mode });
80
+ if (accepted)
81
+ plan.push({ ...accepted, index: entry.index });
138
82
  }
139
83
  const directory = await fs.open(stagingDir, fsConstants.O_RDONLY |
140
84
  (typeof fsConstants.O_DIRECTORY === "number" ? fsConstants.O_DIRECTORY : 0));
141
85
  try {
142
86
  params.deadline.check();
143
- await params.binding.extractArchiveNative(stagedArchive.path, params.kind, directory.fd, plan.map((entry) => ({ ...entry, mode: entry.kind === "directory" ? 0o700 : 0o600 })), tarLimits, params.deadline.signal).catch(throwMappedNativeError);
87
+ await params.binding.extractArchiveNative(stagedArchive.path, params.kind, directory.fd, plan.map((entry) => ({ ...entry, mode: entry.kind === "directory" ? 0o700 : 0o600 })), tarLimits, params.deadline.signal).catch(throwMappedNativeArchiveError);
144
88
  }
145
89
  finally {
146
90
  await directory.close().catch(() => undefined);
@@ -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
+ }
@@ -0,0 +1,20 @@
1
+ import type { ArchiveKind } from "./archive-kind.js";
2
+ import type { ExtractArchiveOptions } from "./archive-options.js";
3
+ export type ArchiveMemberKind = "file" | "directory" | "symlink" | "hardlink" | "blocked" | "sparse" | "other";
4
+ export type ArchivePlanEntry = {
5
+ path: string;
6
+ kind: "file" | "directory";
7
+ size: number;
8
+ mode: number;
9
+ };
10
+ export type ArchivePlanOptions = Pick<ExtractArchiveOptions, "stripComponents" | "limits" | "entryModes" | "entryFilter" | "onFiltered"> & {
11
+ rootDir?: string;
12
+ escapeLabel?: string;
13
+ };
14
+ export declare function createArchiveEntryPlanner(params: ArchivePlanOptions, archiveKind: ArchiveKind): (entry: {
15
+ path: string;
16
+ kind: ArchiveMemberKind;
17
+ size: number;
18
+ mode?: number;
19
+ }) => ArchivePlanEntry | null;
20
+ //# sourceMappingURL=archive-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive-plan.d.ts","sourceRoot":"","sources":["../src/archive-plan.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC/G,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACxG,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,EACzD,iBAAiB,GAAG,QAAQ,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC,GAAG;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAIF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,GAAG,CAAC,KAAK,EAAE;IACvG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACpE,KAAK,gBAAgB,GAAG,IAAI,CA2C5B"}
@@ -0,0 +1,55 @@
1
+ import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
2
+ import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
3
+ import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, resolveExtractLimits, } from "./archive-limits.js";
4
+ import { resolveArchiveEntryMode, shouldExtractArchiveEntry } from "./archive-policy.js";
5
+ import { formatErrorDetail } from "./error-detail.js";
6
+ // Inspection and both executors decide over the same admitted identities.
7
+ // A destination is optional only for inspection; writers still prove containment.
8
+ export function createArchiveEntryPlanner(params, archiveKind) {
9
+ const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
10
+ const limits = resolveExtractLimits(params.limits);
11
+ const budget = createByteBudgetTracker(limits);
12
+ const trackOutputPath = createArchiveOutputPathTracker();
13
+ let entryCount = 0;
14
+ return (entry) => {
15
+ assertArchiveEntryCountWithinLimit(++entryCount, limits);
16
+ validateArchiveEntryPath(entry.path, { escapeLabel: params.escapeLabel });
17
+ const canonicalPath = stripArchivePath(entry.path, 0);
18
+ if (!canonicalPath)
19
+ return null;
20
+ const relPath = stripArchivePath(canonicalPath, strip);
21
+ if (!relPath)
22
+ return null;
23
+ validateArchiveEntryPath(relPath, { escapeLabel: params.escapeLabel });
24
+ assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
25
+ trackOutputPath(relPath, entry.path);
26
+ if (params.rootDir !== undefined) {
27
+ resolveArchiveOutputPath({ rootDir: params.rootDir, relPath, originalPath: entry.path,
28
+ escapeLabel: params.escapeLabel });
29
+ }
30
+ const kind = entry.kind === "file" || entry.kind === "directory" ? entry.kind
31
+ : entry.kind === "symlink" || entry.kind === "hardlink" ? "symlink" : "other";
32
+ if (!shouldExtractArchiveEntry({ filter: params.entryFilter, onFiltered: params.onFiltered,
33
+ entry: { path: canonicalPath, kind, size: entry.size } }))
34
+ return null;
35
+ if (entry.kind === "sparse") {
36
+ throw new ArchiveFormatError(`GNU sparse archive entry is not supported: ${formatErrorDetail(entry.path)}`);
37
+ }
38
+ if (kind === "symlink" || entry.kind === "blocked") {
39
+ const label = archiveKind === "zip" ? "zip" : "tar";
40
+ throw new ArchiveSecurityError("entry-link", `${label} entry is a link: ${formatErrorDetail(entry.path)}`);
41
+ }
42
+ if (!Number.isSafeInteger(entry.size) || entry.size < 0) {
43
+ throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
44
+ }
45
+ // Unsupported records remain visible to policy but cannot create outputs.
46
+ if (kind === "other")
47
+ return null;
48
+ if (kind === "file" || archiveKind !== "zip") {
49
+ budget.startEntry();
50
+ budget.addEntrySize(entry.size);
51
+ }
52
+ return { path: relPath, kind, size: entry.size,
53
+ mode: resolveArchiveEntryMode({ kind, archivedMode: entry.mode, policy: params.entryModes }) };
54
+ };
55
+ }
@@ -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);
@@ -0,0 +1,7 @@
1
+ import type { ExtractArchiveOptions } from "./archive-options.js";
2
+ import { type ArchivePlanEntry } from "./archive-plan.js";
3
+ export type InspectTarArchiveOptions = Pick<ExtractArchiveOptions, "archivePath" | "timeoutMs" | "limits" | "entryFilter" | "onFiltered">;
4
+ export type InspectedTarEntry = Readonly<Pick<ArchivePlanEntry, "path" | "kind" | "size">>;
5
+ /** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
6
+ export declare function inspectTarArchive(params: InspectTarArchiveOptions): Promise<readonly InspectedTarEntry[]>;
7
+ //# sourceMappingURL=archive-tar-inspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive-tar-inspect.d.ts","sourceRoot":"","sources":["../src/archive-tar-inspect.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAOrF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,qBAAqB,EAC/D,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AAE3F,2FAA2F;AAC3F,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA2C/G"}
@@ -0,0 +1,59 @@
1
+ import { withExtractionDeadline } from "./archive-deadline.js";
2
+ import { validateArchiveEntryPath } from "./archive-entry.js";
3
+ import { stageArchiveFileForExtraction } from "./archive-input.js";
4
+ import { resolveExtractLimits, resolveTarMeterLimits } from "./archive-limits.js";
5
+ import { throwMappedNativeArchiveError } from "./archive-native.js";
6
+ import { createArchiveEntryPlanner } from "./archive-plan.js";
7
+ import { resolveArchiveFilteredEntryPolicy } from "./archive-policy.js";
8
+ import { createTarEntryPlanner } from "./archive-tar.js";
9
+ import { inspectTar } from "./archive-tar-stream.js";
10
+ import { getNativeBinding } from "./native.js";
11
+ /** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
12
+ export async function inspectTarArchive(params) {
13
+ const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
14
+ const limits = resolveExtractLimits(params.limits);
15
+ const tarLimits = resolveTarMeterLimits(limits);
16
+ const native = getNativeBinding();
17
+ return await withExtractionDeadline(params.timeoutMs, "inspect tar", async (deadline) => {
18
+ const staged = await stageArchiveFileForExtraction({ archivePath: params.archivePath, limits, deadline });
19
+ try {
20
+ // Staging closes descriptors asynchronously; expiry there must not start a decoder.
21
+ deadline.check();
22
+ const entries = [];
23
+ const append = (entry) => {
24
+ if (entry)
25
+ entries.push(Object.freeze({ path: entry.path, kind: entry.kind, size: entry.size }));
26
+ };
27
+ const policy = { limits, entryFilter: params.entryFilter, onFiltered };
28
+ if (native) {
29
+ const manifest = await native.inspectArchiveNative(staged.path, "tar", tarLimits, deadline.signal)
30
+ .catch(throwMappedNativeArchiveError);
31
+ deadline.check();
32
+ // Match extraction's whole-manifest validation before caller policy runs.
33
+ for (const entry of manifest)
34
+ validateArchiveEntryPath(entry.path);
35
+ const planEntry = createArchiveEntryPlanner(policy, "tar");
36
+ for (const entry of manifest) {
37
+ deadline.check();
38
+ append(planEntry(entry));
39
+ }
40
+ }
41
+ else {
42
+ const manifest = [];
43
+ await inspectTar({ archivePath: staged.path, limits: tarLimits, signal: deadline.signal,
44
+ onMember: (entry) => { manifest.push(entry); } });
45
+ const planEntry = createTarEntryPlanner(policy);
46
+ for (const entry of manifest) {
47
+ deadline.check();
48
+ append(planEntry(entry));
49
+ }
50
+ }
51
+ deadline.check();
52
+ // This is bounded evidence about the staged bytes, not a reusable write plan.
53
+ return Object.freeze(entries);
54
+ }
55
+ finally {
56
+ await staged.cleanup();
57
+ }
58
+ });
59
+ }
@@ -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"}