@openclaw/fs-safe 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/README.md +10 -4
  3. package/dist/advanced.d.ts +1 -1
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +1 -1
  6. package/dist/archive-merge.d.ts.map +1 -1
  7. package/dist/archive-merge.js +113 -46
  8. package/dist/archive-read.d.ts.map +1 -1
  9. package/dist/archive-read.js +4 -4
  10. package/dist/archive-zip-directory.d.ts +4 -0
  11. package/dist/archive-zip-directory.d.ts.map +1 -1
  12. package/dist/archive-zip-directory.js +2 -0
  13. package/dist/archive-zip-entry.d.ts +6 -2
  14. package/dist/archive-zip-entry.d.ts.map +1 -1
  15. package/dist/archive-zip-entry.js +23 -8
  16. package/dist/archive-zip-integrity.d.ts.map +1 -1
  17. package/dist/archive-zip-integrity.js +3 -4
  18. package/dist/archive-zip-loader.d.ts.map +1 -1
  19. package/dist/archive-zip-loader.js +107 -31
  20. package/dist/archive-zip-names.d.ts +1 -0
  21. package/dist/archive-zip-names.d.ts.map +1 -1
  22. package/dist/archive-zip-names.js +6 -0
  23. package/dist/archive.js +6 -5
  24. package/dist/bounded-read-stream.d.ts +0 -1
  25. package/dist/bounded-read-stream.d.ts.map +1 -1
  26. package/dist/bounded-read-stream.js +0 -6
  27. package/dist/copy-publication.d.ts +6 -0
  28. package/dist/copy-publication.d.ts.map +1 -1
  29. package/dist/copy-publication.js +3 -0
  30. package/dist/copy-tree-portable.d.ts.map +1 -1
  31. package/dist/copy-tree-portable.js +44 -24
  32. package/dist/copy.d.ts.map +1 -1
  33. package/dist/copy.js +29 -11
  34. package/dist/directory-mode-owner.js +5 -5
  35. package/dist/file-handle-transfer.d.ts +2 -0
  36. package/dist/file-handle-transfer.d.ts.map +1 -1
  37. package/dist/file-handle-transfer.js +57 -2
  38. package/dist/file-identity.d.ts.map +1 -1
  39. package/dist/file-identity.js +18 -4
  40. package/dist/file-lock-sync-admission.d.ts +19 -0
  41. package/dist/file-lock-sync-admission.d.ts.map +1 -0
  42. package/dist/file-lock-sync-admission.js +93 -0
  43. package/dist/file-lock-sync-root-acquire.d.ts +4 -0
  44. package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
  45. package/dist/file-lock-sync-root-acquire.js +370 -0
  46. package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
  47. package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
  48. package/dist/file-lock-sync-root-arbitration.js +66 -0
  49. package/dist/file-lock-sync-root-held.d.ts +34 -0
  50. package/dist/file-lock-sync-root-held.d.ts.map +1 -0
  51. package/dist/file-lock-sync-root-held.js +393 -0
  52. package/dist/file-lock-sync-root-io.d.ts +44 -0
  53. package/dist/file-lock-sync-root-io.d.ts.map +1 -0
  54. package/dist/file-lock-sync-root-io.js +209 -0
  55. package/dist/file-lock-sync-root-mutation.d.ts +17 -0
  56. package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
  57. package/dist/file-lock-sync-root-mutation.js +277 -0
  58. package/dist/file-lock-sync-root-options.d.ts +20 -0
  59. package/dist/file-lock-sync-root-options.d.ts.map +1 -0
  60. package/dist/file-lock-sync-root-options.js +58 -0
  61. package/dist/file-lock-sync-root-registration.d.ts +2 -0
  62. package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
  63. package/dist/file-lock-sync-root-registration.js +90 -0
  64. package/dist/file-lock-sync-root.d.ts +36 -0
  65. package/dist/file-lock-sync-root.d.ts.map +1 -0
  66. package/dist/file-lock-sync-root.js +361 -0
  67. package/dist/file-lock-sync-stale-admission.d.ts +24 -0
  68. package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
  69. package/dist/file-lock-sync-stale-admission.js +205 -0
  70. package/dist/file-lock-sync.d.ts.map +1 -1
  71. package/dist/file-lock-sync.js +245 -205
  72. package/dist/file-store-prune.d.ts.map +1 -1
  73. package/dist/file-store-prune.js +5 -1
  74. package/dist/file-store-sync-directory.d.ts.map +1 -1
  75. package/dist/file-store-sync-directory.js +14 -7
  76. package/dist/file-store-sync-write.js +3 -3
  77. package/dist/file-store.d.ts.map +1 -1
  78. package/dist/file-store.js +47 -12
  79. package/dist/guest-dispatch-python.js +1 -1
  80. package/dist/json-document-store.d.ts.map +1 -1
  81. package/dist/json-document-store.js +22 -15
  82. package/dist/json-durable-queue-ownership.d.ts +0 -1
  83. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  84. package/dist/json-durable-queue-ownership.js +0 -6
  85. package/dist/native-binding.d.ts +8 -0
  86. package/dist/native-binding.d.ts.map +1 -1
  87. package/dist/native-parent-admission.d.ts +3 -2
  88. package/dist/native-parent-admission.d.ts.map +1 -1
  89. package/dist/native-parent-admission.js +24 -5
  90. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  91. package/dist/native-pinned-write-windows.js +7 -7
  92. package/dist/native-pinned-write.d.ts.map +1 -1
  93. package/dist/native-pinned-write.js +202 -172
  94. package/dist/native-policy-directory-observation.d.ts +12 -0
  95. package/dist/native-policy-directory-observation.d.ts.map +1 -0
  96. package/dist/native-policy-directory-observation.js +61 -0
  97. package/dist/native-policy-parent-windows.d.ts +14 -0
  98. package/dist/native-policy-parent-windows.d.ts.map +1 -0
  99. package/dist/native-policy-parent-windows.js +200 -0
  100. package/dist/native-rename-outcome.d.ts +4 -0
  101. package/dist/native-rename-outcome.d.ts.map +1 -0
  102. package/dist/native-rename-outcome.js +8 -0
  103. package/dist/native-staged-file.d.ts +2 -2
  104. package/dist/native-staged-file.d.ts.map +1 -1
  105. package/dist/native-staged-file.js +42 -40
  106. package/dist/output.d.ts.map +1 -1
  107. package/dist/output.js +12 -8
  108. package/dist/path-prefix.d.ts.map +1 -1
  109. package/dist/path-prefix.js +30 -8
  110. package/dist/path-suffix-aliases.d.ts +2 -0
  111. package/dist/path-suffix-aliases.d.ts.map +1 -1
  112. package/dist/path-suffix-aliases.js +25 -17
  113. package/dist/permission-exec.d.ts +2 -0
  114. package/dist/permission-exec.d.ts.map +1 -1
  115. package/dist/permission-exec.js +150 -21
  116. package/dist/permissions-windows.js +1 -1
  117. package/dist/pinned-mutation-admission.d.ts.map +1 -1
  118. package/dist/pinned-mutation-admission.js +23 -29
  119. package/dist/pinned-mutation-observation.d.ts +7 -2
  120. package/dist/pinned-mutation-observation.d.ts.map +1 -1
  121. package/dist/pinned-mutation-observation.js +81 -18
  122. package/dist/pinned-mutation-shared-route.d.ts +1 -0
  123. package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
  124. package/dist/pinned-mutation-shared-route.js +1 -1
  125. package/dist/pinned-write-types.d.ts +2 -0
  126. package/dist/pinned-write-types.d.ts.map +1 -1
  127. package/dist/private-temp-workspace.d.ts.map +1 -1
  128. package/dist/private-temp-workspace.js +75 -121
  129. package/dist/regular-file.d.ts.map +1 -1
  130. package/dist/regular-file.js +1 -15
  131. package/dist/replace-directory.d.ts.map +1 -1
  132. package/dist/replace-directory.js +256 -18
  133. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  134. package/dist/replace-file-copy-fallback.js +62 -70
  135. package/dist/replace-file-copy-source.d.ts.map +1 -1
  136. package/dist/replace-file-copy-source.js +10 -12
  137. package/dist/replace-file-temp-owner.d.ts +5 -2
  138. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  139. package/dist/replace-file-temp-owner.js +65 -30
  140. package/dist/replace-file.js +6 -6
  141. package/dist/retained-directory-replacement.d.ts +26 -0
  142. package/dist/retained-directory-replacement.d.ts.map +1 -0
  143. package/dist/retained-directory-replacement.js +193 -0
  144. package/dist/root-boundary.d.ts +1 -0
  145. package/dist/root-boundary.d.ts.map +1 -1
  146. package/dist/root-boundary.js +4 -0
  147. package/dist/root-context.d.ts +0 -8
  148. package/dist/root-context.d.ts.map +1 -1
  149. package/dist/root-context.js +0 -3
  150. package/dist/root-create-input.d.ts +6 -0
  151. package/dist/root-create-input.d.ts.map +1 -1
  152. package/dist/root-create-input.js +5 -1
  153. package/dist/root-directory-creation.d.ts.map +1 -1
  154. package/dist/root-directory-creation.js +5 -4
  155. package/dist/root-directory-list.d.ts +1 -0
  156. package/dist/root-directory-list.d.ts.map +1 -1
  157. package/dist/root-directory-list.js +1 -0
  158. package/dist/root-impl.d.ts.map +1 -1
  159. package/dist/root-impl.js +20 -11
  160. package/dist/root-move-noreplace.d.ts.map +1 -1
  161. package/dist/root-move-noreplace.js +2 -2
  162. package/dist/root-path-errors.d.ts +1 -0
  163. package/dist/root-path-errors.d.ts.map +1 -1
  164. package/dist/root-path-errors.js +11 -2
  165. package/dist/root-path-existing.d.ts.map +1 -1
  166. package/dist/root-path-existing.js +12 -37
  167. package/dist/root-path.js +1 -13
  168. package/dist/root-remove.d.ts +1 -0
  169. package/dist/root-remove.d.ts.map +1 -1
  170. package/dist/root-remove.js +4 -0
  171. package/dist/root-walk.d.ts +1 -1
  172. package/dist/root-walk.d.ts.map +1 -1
  173. package/dist/root-walk.js +17 -2
  174. package/dist/root-write-admission.d.ts +0 -2
  175. package/dist/root-write-admission.d.ts.map +1 -1
  176. package/dist/root-write-admission.js +1 -15
  177. package/dist/root-write-compatibility.d.ts +1 -2
  178. package/dist/root-write-compatibility.d.ts.map +1 -1
  179. package/dist/root-write-compatibility.js +17 -68
  180. package/dist/root-write-complete-parent.d.ts.map +1 -1
  181. package/dist/root-write-complete-parent.js +10 -24
  182. package/dist/root-write-lock-binding.d.ts +15 -0
  183. package/dist/root-write-lock-binding.d.ts.map +1 -0
  184. package/dist/root-write-lock-binding.js +162 -0
  185. package/dist/root-write-verification.d.ts.map +1 -1
  186. package/dist/root-write-verification.js +29 -42
  187. package/dist/secret-file.d.ts.map +1 -1
  188. package/dist/secret-file.js +2 -24
  189. package/dist/secret-read-async.d.ts.map +1 -1
  190. package/dist/secret-read-async.js +3 -24
  191. package/dist/secret-read-policy.d.ts +6 -2
  192. package/dist/secret-read-policy.d.ts.map +1 -1
  193. package/dist/secret-read-policy.js +26 -2
  194. package/dist/sibling-temp.d.ts.map +1 -1
  195. package/dist/sibling-temp.js +23 -12
  196. package/dist/sidecar-lock-acquire.d.ts +2 -28
  197. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  198. package/dist/sidecar-lock-acquire.js +288 -199
  199. package/dist/sidecar-lock-admission-context.d.ts +19 -0
  200. package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
  201. package/dist/sidecar-lock-admission-context.js +60 -0
  202. package/dist/sidecar-lock-admission-parser.d.ts +43 -0
  203. package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
  204. package/dist/sidecar-lock-admission-parser.js +113 -0
  205. package/dist/sidecar-lock-admission.d.ts +35 -0
  206. package/dist/sidecar-lock-admission.d.ts.map +1 -0
  207. package/dist/sidecar-lock-admission.js +7 -0
  208. package/dist/sidecar-lock-reclaim.d.ts +9 -4
  209. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  210. package/dist/sidecar-lock-reclaim.js +80 -25
  211. package/dist/sidecar-lock-stale-admission.d.ts +39 -0
  212. package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
  213. package/dist/sidecar-lock-stale-admission.js +232 -0
  214. package/dist/sidecar-lock-target.d.ts +8 -0
  215. package/dist/sidecar-lock-target.d.ts.map +1 -0
  216. package/dist/sidecar-lock-target.js +55 -0
  217. package/dist/sidecar-lock.d.ts.map +1 -1
  218. package/dist/sidecar-lock.js +100 -16
  219. package/dist/staged-directory.d.ts +5 -2
  220. package/dist/staged-directory.d.ts.map +1 -1
  221. package/dist/staged-directory.js +37 -1
  222. package/dist/temp-workspace-admission.d.ts.map +1 -1
  223. package/dist/temp-workspace-admission.js +28 -4
  224. package/dist/temp-workspace-descriptor.d.ts.map +1 -1
  225. package/dist/temp-workspace-descriptor.js +9 -27
  226. package/dist/temp-workspace-owner.d.ts.map +1 -1
  227. package/dist/temp-workspace-owner.js +8 -8
  228. package/dist/walk.d.ts +5 -1
  229. package/dist/walk.d.ts.map +1 -1
  230. package/dist/walk.js +19 -6
  231. package/dist/windows-owner.d.ts.map +1 -1
  232. package/dist/windows-owner.js +2 -2
  233. package/docs/advanced.md +1 -1
  234. package/docs/archive.md +36 -9
  235. package/docs/atomic.md +85 -8
  236. package/docs/copy.md +35 -0
  237. package/docs/file-store.md +19 -0
  238. package/docs/guest.md +1 -1
  239. package/docs/json-store.md +5 -0
  240. package/docs/native-helper.md +10 -3
  241. package/docs/native.md +8 -0
  242. package/docs/output.md +6 -0
  243. package/docs/path-prefix.md +10 -0
  244. package/docs/path-suffix-aliases.md +51 -6
  245. package/docs/permissions.md +13 -0
  246. package/docs/public-api.md +3 -2
  247. package/docs/root.md +22 -3
  248. package/docs/sidecar-lock.md +109 -4
  249. package/docs/staged-file.md +7 -3
  250. package/docs/temp.md +20 -3
  251. package/docs/walk.md +67 -1
  252. package/docs/writing.md +11 -1
  253. package/package.json +10 -10
  254. package/dist/darwin-acl.d.ts +0 -4
  255. package/dist/darwin-acl.d.ts.map +0 -1
  256. package/dist/darwin-acl.js +0 -24
package/CHANGELOG.md CHANGED
@@ -1,6 +1,69 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.15.0 - 2026-09-18
4
+
5
+ ### Highlights
6
+
7
+ - Enforce Root authority throughout synchronous lock admission, stale recovery, and cleanup; keep asynchronous reclaim guards inside the Root capability.
8
+ - Preserve directory and archive-source identity across publication, callbacks, and recovery, and reject corrupt portable ZIP payloads and metadata.
9
+ - Support asynchronous walk filters, configurable-depth suffix probing, and bounded synchronous descriptor copying.
10
+
11
+ ### Compatibility and upgrade notes
12
+
13
+ - Directory replacement now requires compatible native identity-fenced, no-replace support, including when the destination is absent. Disabled, missing, or older helpers reject before target-parent creation.
14
+ - Treat ordinary post-dispatch native rename errors as indeterminate. Preserve staged files and directory backups when a rename may have committed before its reply was lost; automatic retry and rollback require explicit evidence that dispatch never occurred.
15
+ - Synchronous Root locks require a genuine registered Root handle and enforce its policies. Interrupted asynchronous Root reclaim guards can remain after process exit or reset; recover them only after an application-owned liveness check proves the attempt ended.
16
+ - Root lock authority checks add measurable overhead. Removing redundant observations reduces some cost, but these paths remain slower than the earlier implementation that did not enforce the same boundary.
17
+
18
+ ### Security and correctness
19
+
20
+ - Serialize sidecar admission across raw and Root-based synchronous callers, retain separate cleanup authority, and recheck asynchronous authorization at the final stale-removal boundary.
21
+ - Keep asynchronous Root reclaim-guard creation, token/byte ownership checks, and removal inside the capability. Preserve unsettled guards instead of following replaced parents through raw cleanup.
22
+ - Create native Root lock records through retained-parent exclusive opens so racing contenders retry normally. Ordinary Root writes retain private staging. Treat raw reclaim guards disappearing or changing during collision inspection as contention.
23
+ - Verify reclaim-guard ownership after the final sidecar snapshot and parser, preserving mutation authorization and parser failures. Evaluate stale age after snapshot parsing and reject cleanup when exact identity is unknown.
24
+ - Retain source-root and child-directory authority during archive merges and bind file identity and mode to the admitted copy descriptor, including exact large Windows identities.
25
+ - Preserve ZIP payloads and physical entry metadata during portable loading so extraction rejects corrupt empty content and honors admitted directory, symlink, and special-file kinds without reordering callbacks.
26
+ - Snapshot FileStore policies before asynchronous work, streams, source reads, and JSON serialization; snapshot JsonStore durability/newline options before queued mutations and updater callbacks.
27
+ - Snapshot external-output and sibling-temp options before asynchronous setup while preserving callback receivers.
28
+ - Enforce Windows native parent mutation policies before creating each missing component and retain originating native descriptor ownership through cleanup.
29
+ - Preserve files and symlinks replacing an observed empty FileStore directory, and leave directories that become nonempty untouched.
30
+ - Recheck child entries before walk descent so symlink replacements during filters honor the selected policy.
31
+ - Keep Windows owner/ACL diagnostics bounded and fail-closed for hostile thrown values. Preserve ordinary diagnostics and the original cause without invoking getters, proxy traps, coercion, or altered buffer properties.
32
+
33
+ ### APIs and failure handling
34
+
35
+ - Allow asynchronous `include`/`descend` callbacks in `walkDirectory()` and `entryFilter` in `Root.walk()`, retaining serial selection, callback receivers, budgets, cancellation, and post-callback identity checks.
36
+ - Add `maxDepth` to `probePathSuffixAliasesSync()` with proportional budgets, a 32,768-forward-observation ceiling, unchanged defaults and string limits, and complete owned cleanup.
37
+ - Add `copyFileDescriptorSync()` for bounded positional transfers between caller-owned regular-file descriptors, preserving cursors, destination suffixes, descriptor lifetime, and caller-owned durability/publication.
38
+ - Preserve the first tree-copy operation failure, including falsy values and existing codes, while closing each owned resource once. Successful operations retain output-before-input and source-before-parent close-failure precedence.
39
+ - Preserve falsy atomic operation, cleanup, close, restoration, temporary-workspace removal, and Windows native cleanup failures; consume retained handles before close to prevent repeated release.
40
+ - Propagate portable ZIP fallback-close failures before publication and copy-fallback writer close failures after successful unsynchronized writes, including restoration with an absent destination.
41
+ - Preserve synchronous destination-admission failures when best-effort close also fails, and preserve directory-mode authority/deadline failures before and after dispatch.
42
+
43
+ ### Performance and maintenance
44
+
45
+ - Bound path-prefix queue consumption on long paths while preserving short-path behavior and resolution checks.
46
+ - Reuse eligible Windows parent-creation receipts, equivalent Root identity observations, and guarded regular-file unlink paths while retaining final identity and policy checks.
47
+ - Consolidate native copy ownership, regular/secret read admission, copy-fallback admission, and temporary-workspace setup and settlement; remove redundant wrappers, write-admission storage, and permission-diagnostic work.
48
+ - Bind public copy, ZIP extraction, atomic settlement, and permission benchmarks to verified workload receipts; keep setup, content checks, and cleanup verification outside timing.
49
+ - Update @napi-rs/cli to 3.10.0 and the locked Node type definitions to 26.6.0, and align Vitest/coverage on 5.0.1.
50
+ - Expand security-owner coverage to native/archive code, platform packages, executable benchmarks, and build/test/release configuration.
51
+ - Raise publication and release-proof job ceilings to 90 minutes, retaining bounded registry retries and fail-closed artifact, signature, and provenance checks.
52
+
53
+ ## 0.14.0 - 2026-09-17
54
+
55
+ ### Highlights
56
+
57
+ - **Safer guest directory selection:** descriptor-relative listings now report regular files as well as directories without following symlinks, so consumers can select files without reopening path-type checks.
58
+
59
+ ### Filesystem boundaries and performance
60
+
61
+ - Report both directory and regular-file kinds from descriptor-relative guest listings. Symlinks and special entries report neither kind.
62
+ - Reduce repeated filesystem observations during policy-bound native parent creation, Windows compatibility writes and metadata reads, synchronous store-directory permission repair, and missing temporary-root creation. Fresh identity, canonical-path, lock-destination, callback, and publication checks remain at mutation boundaries.
63
+
64
+ ### Validation
65
+
66
+ - Register the path-prefix and synchronous `lockRoot` performance workflows on the default branch. Their dispatch stubs fail closed without checking out or executing repository code.
4
67
 
5
68
  ## 0.13.1 - 2026-09-16
6
69
 
package/README.md CHANGED
@@ -260,7 +260,7 @@ contract. Low-level helpers that OpenClaw needs to compose higher-level APIs are
260
260
  | `@openclaw/fs-safe/walk` | budget-bounded directory walking with symlink policy, filters, and truncation accounting; not root-bounded |
261
261
  | `@openclaw/fs-safe/copy` | directory copying with `clone: "auto"`, `"always"`, or `"never"`; native APFS, Btrfs, ReFS, XFS, and ZFS cloning, portable byte copying, and clone metadata; see [directory copying](docs/copy.md) |
262
262
  | `@openclaw/fs-safe/archive` | policy-driven ZIP/TAR extraction, clamp/filter policy, metadata/path-depth limits, native gzip/zstd/bzip2, and bounded entry reads |
263
- | `@openclaw/fs-safe/advanced` | lower-level composition helpers such as path scopes, root-file open, bounded descriptor reads, [borrowed-handle copying](docs/copy.md#borrowed-filehandle-transfers), [exact directory identity](docs/directory-identity.md), [case probing](docs/path-case.md), [suffix-alias probing](docs/path-suffix-aliases.md), [in-place writes](docs/in-place-write.md), [versioned install-ID encoding](docs/install-path.md#safepathsegmenthashedv2), filename sanitizing, temp-file targets, sibling-temp writes, local-root readers, regular-file helpers, `pathExists`, and `withTimeout`; less stable than focused public subpaths |
263
+ | `@openclaw/fs-safe/advanced` | lower-level composition helpers such as path scopes, root-file open, bounded descriptor reads, [borrowed-handle and descriptor copying](docs/copy.md#borrowed-filehandle-transfers), [exact directory identity](docs/directory-identity.md), [case probing](docs/path-case.md), [suffix-alias probing](docs/path-suffix-aliases.md), [in-place writes](docs/in-place-write.md), [versioned install-ID encoding](docs/install-path.md#safepathsegmenthashedv2), filename sanitizing, temp-file targets, sibling-temp writes, local-root readers, regular-file helpers, `pathExists`, and `withTimeout`; less stable than focused public subpaths |
264
264
  | `@openclaw/fs-safe/errors` | `FsSafeError`, closed codes/categories, causes, and operation-specific details receipts |
265
265
  | `@openclaw/fs-safe/types` | shared types: `DirEntry`, `PathStat`, … |
266
266
  | `@openclaw/fs-safe/test-hooks` | hooks the test suite uses to inject races; registration requires `NODE_ENV=test` or `VITEST=true` |
@@ -517,12 +517,18 @@ for (const file of scan.entries) {
517
517
 
518
518
  Check `scan.truncated` before treating the result as complete, and `scan.failedDirs` to tell an incomplete scan (a directory that could not be read) from an empty one before pruning state from the listing.
519
519
 
520
+ `walkDirectory()` accepts asynchronous `include` and `descend` callbacks through `AsyncWalkDirectoryOptions`, so a marker lookup can prune a directory before its children are read. Decisions remain serial and retain the options object as their `this` receiver; `walkDirectorySync()` and its options remain synchronous. See [Directory walking](docs/walk.md) for callback timing, JavaScript result compatibility, and error handling.
521
+
520
522
  For caller-controlled paths, `Root.walk()` is the root-bounded async iterator.
521
523
  It supports entry/depth budgets, in-root symlink following, cancellation, and a
522
524
  truncation marker (or typed error) when a budget is reached. Its `entryFilter`
523
- can return `"skip-subtree"` to prune a directory, and
524
- `onDirectoryError: "skip-and-report"` yields typed `"directory-error"` markers
525
- while preserving entries from readable subtrees.
525
+ accepts `"include"`, `"skip"`, or `"skip-subtree"`, directly or through a Promise.
526
+ After an awaited decision resolves, the walk rechecks cancellation and the
527
+ current listing directory and Root identities before using it. Pending callbacks
528
+ settle before cancellation or iterator disposal completes. Callback failures
529
+ reject the walk. `onDirectoryError: "skip-and-report"` yields typed `"directory-error"` markers
530
+ for directory read or identity-check failures while preserving entries from
531
+ readable subtrees.
526
532
 
527
533
  ## Archive extraction
528
534
 
@@ -1,5 +1,5 @@
1
1
  export { createAsyncLock } from "./async-lock.js";
2
- export { copyFileHandle, type CopyFileHandleOptions } from "./file-handle-transfer.js";
2
+ export { copyFileHandle, copyFileDescriptorSync, type CopyFileHandleOptions } from "./file-handle-transfer.js";
3
3
  export { overwriteFileHandle, type OverwriteFileHandleOptions } from "./overwrite-file-handle.js";
4
4
  export { probePathCaseInsensitiveSync, type ProbePathCaseOptions } from "./path-case.js";
5
5
  export { resolvePathPrefixSync, type ResolvedPathPrefix } from "./path-prefix.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 { copyFileHandle } from "./file-handle-transfer.js";
5
+ export { copyFileHandle, copyFileDescriptorSync } from "./file-handle-transfer.js";
6
6
  export { overwriteFileHandle } from "./overwrite-file-handle.js";
7
7
  export { probePathCaseInsensitiveSync } from "./path-case.js";
8
8
  export { resolvePathPrefixSync } from "./path-prefix.js";
@@ -1 +1 @@
1
- {"version":3,"file":"archive-merge.d.ts","sourceRoot":"","sources":["../src/archive-merge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAc9B,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACjG,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,CAAC;AACF,KAAK,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG;IACtE,gBAAgB,EAAE,qBAAqB,CAAC;CACzC,CAAC;AAEF,wBAAsB,kCAAkC,CACtD,MAAM,EAAE,kBAAkB,GAAG;IAAE,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9F,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAS1F"}
1
+ {"version":3,"file":"archive-merge.d.ts","sourceRoot":"","sources":["../src/archive-merge.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAiB9B,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACjG,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,CAAC;AACF,KAAK,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG;IACtE,gBAAgB,EAAE,qBAAqB,CAAC;CACzC,CAAC;AAEF,wBAAsB,kCAAkC,CACtD,MAAM,EAAE,kBAAkB,GAAG;IAAE,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9F,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAS1F"}
@@ -5,16 +5,19 @@ import { ownExtractionDestinationMutation } from "./archive-deadline.js";
5
5
  import { assertDirectoryIdentityGuard, assertResolvedInsideDestination, createDirectoryIdentityGuard, createArchiveSymlinkTraversalError, preparePrivateArchiveOutputPath, } from "./archive-staging.js";
6
6
  import {} from "./directory-mode-owner.js";
7
7
  import { pinNodeDirectoryForMode } from "./directory-mode-node.js";
8
+ import { assertSyncDirectoryGuard, inspectDirectoryIdentitySync } from "./directory-guard.js";
8
9
  import { FsSafeError } from "./errors.js";
9
10
  import { formatErrorDetail } from "./error-detail.js";
10
- import { isPathInside } from "./path.js";
11
+ import { isNotFoundPathError, isPathInside } from "./path.js";
11
12
  import { rootFromDirectoryGuard } from "./root-impl.js";
13
+ import { admitPathInsideRoot } from "./root-boundary.js";
12
14
  import { realpathSync } from "./realpath.js";
13
15
  import { getFsSafeTestHooks } from "./test-hooks.js";
14
- import { onCopyPublication } from "./copy-publication.js";
16
+ import { onCopyPublication, onCopySourceAdmission } from "./copy-publication.js";
15
17
  import { syncFileBestEffortSync } from "./file-sync.js";
16
18
  import { finalizeArchivePublication } from "./archive-durability.js";
17
19
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
20
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
18
21
  export async function mergePlannedArchiveIntoDestination(params) {
19
22
  assertMergePathInputs(params);
20
23
  await mergeTree(params, params.entries, params.durable === true);
@@ -58,6 +61,33 @@ async function mergeTree(params, publication, durable = true) {
58
61
  plan.set(stagedPath, entry);
59
62
  }
60
63
  const ancestors = [];
64
+ const sourceAncestors = [];
65
+ const assertSourceDirectory = (guard, canonical = false) => {
66
+ try {
67
+ // Child receipts already retain canonical ancestry. An exact no-follow
68
+ // observation checks each named association without re-running realpath.
69
+ if (canonical)
70
+ assertSyncDirectoryGuard(guard);
71
+ else
72
+ inspectDirectoryIdentitySync(guard.dir, guard.stat);
73
+ }
74
+ catch (error) {
75
+ if (error instanceof FsSafeError || isNotFoundPathError(error)) {
76
+ throw createArchiveSymlinkTraversalError(path.relative(params.sourceDir, guard.dir));
77
+ }
78
+ throw error;
79
+ }
80
+ check();
81
+ };
82
+ const assertSourceFrontier = () => {
83
+ const current = sourceAncestors.at(-1);
84
+ if (current)
85
+ assertSourceDirectory(current);
86
+ };
87
+ const assertSourceAncestors = () => {
88
+ for (const ancestor of sourceAncestors)
89
+ assertSourceDirectory(ancestor);
90
+ };
61
91
  const assertGuards = async () => {
62
92
  await assertDirectoryIdentityGuard(destinationGuard);
63
93
  check();
@@ -72,15 +102,21 @@ async function mergeTree(params, publication, durable = true) {
72
102
  };
73
103
  const walk = async (sourceDir) => {
74
104
  await assertGuards();
105
+ assertSourceFrontier();
75
106
  const entries = await fs.readdir(sourceDir, { withFileTypes: true });
76
107
  check();
108
+ if (entries.length === 0) {
109
+ await assertGuards();
110
+ assertSourceFrontier();
111
+ }
77
112
  for (const entry of entries) {
78
113
  await assertGuards();
114
+ assertSourceFrontier();
79
115
  const sourcePath = path.join(sourceDir, entry.name);
80
116
  const relPath = path.relative(params.sourceDir, sourcePath);
81
117
  const originalPath = relPath.split(path.sep).join("/");
82
118
  const destinationPath = path.join(destinationDir, relPath);
83
- const sourceStat = fsSync.lstatSync(sourcePath);
119
+ const sourceStat = inspectFileIdentitySync(() => fsSync.lstatSync(sourcePath, { bigint: true }));
84
120
  check();
85
121
  if (sourceStat.isSymbolicLink())
86
122
  throw createArchiveSymlinkTraversalError(originalPath);
@@ -96,61 +132,73 @@ async function mergeTree(params, publication, durable = true) {
96
132
  if (plan && ((planned && planned.kind !== kind) || (!planned && kind === "file"))) {
97
133
  throw new FsSafeError("path-mismatch", "archive staging disagrees with the admitted publication plan");
98
134
  }
99
- const mode = plan ? planned?.mode ?? 0o755 : sourceStat.mode & 0o777;
135
+ let mode = plan ? planned?.mode ?? 0o755 : Number(sourceStat.mode & 511n);
100
136
  await preparePrivateArchiveOutputPath({
101
137
  destinationDir, destinationRealDir, deadline: params.deadline,
102
138
  relPath, outPath: destinationPath, originalPath, isDirectory: kind === "directory",
103
139
  }, assertGuards, destinationGuard);
104
140
  check();
105
141
  if (kind === "directory") {
106
- // Ownership spans open, descendants, finalization and close, including timeout.
107
- await ownExtractionDestinationMutation(params.deadline, async () => {
108
- await assertGuards();
109
- const owner = await pinNodeDirectoryForMode(destinationPath).catch((error) => {
110
- if (error instanceof FsSafeError && (error.code === "not-file" || error.code === "path-mismatch")) {
111
- throw createArchiveSymlinkTraversalError(originalPath);
112
- }
113
- throw error;
114
- });
115
- try {
116
- check();
117
- const guard = await createDirectoryIdentityGuard(destinationPath);
118
- check();
119
- await owner.verify(check);
120
- ancestors.push({ guard, owner });
121
- try {
122
- await walk(sourcePath);
123
- if (publication) {
124
- await assertGuards();
125
- publishedDirectories.push({ guard, mode, parents: ancestors.slice(0, -1).map((ancestor) => ancestor.guard) });
126
- return;
142
+ // Retain the observation made before output preparation: recapturing
143
+ // here could silently authorize a replacement source directory.
144
+ sourceAncestors.push({ dir: sourcePath, realPath: sourceReal, stat: sourceStat });
145
+ try {
146
+ // Ownership spans open, descendants, finalization and close, including timeout.
147
+ await ownExtractionDestinationMutation(params.deadline, async () => {
148
+ await assertGuards();
149
+ assertSourceFrontier();
150
+ const owner = await pinNodeDirectoryForMode(destinationPath).catch((error) => {
151
+ if (error instanceof FsSafeError && (error.code === "not-file" || error.code === "path-mismatch")) {
152
+ throw createArchiveSymlinkTraversalError(originalPath);
127
153
  }
128
- await getFsSafeTestHooks()?.beforeArchiveOutputMutation?.("chmod", destinationPath);
154
+ throw error;
155
+ });
156
+ try {
129
157
  check();
130
- await assertGuards();
131
- // Do not recursively verify this owner from inside its serialized apply.
132
- ancestors.pop();
133
- await owner.apply(mode, { check, beforeChmod: async () => {
134
- await assertGuards();
135
- await assertDirectoryIdentityGuard(guard);
136
- check();
137
- await assertResolvedInsideDestination({
138
- destinationRealDir, targetPath: destinationPath, originalPath,
139
- });
140
- check();
141
- } });
158
+ const guard = await createDirectoryIdentityGuard(destinationPath);
142
159
  check();
143
- await assertGuards();
160
+ await owner.verify(check);
161
+ ancestors.push({ guard, owner });
162
+ try {
163
+ await walk(sourcePath);
164
+ if (publication) {
165
+ await assertGuards();
166
+ assertSourceAncestors();
167
+ publishedDirectories.push({ guard, mode, parents: ancestors.slice(0, -1).map((ancestor) => ancestor.guard) });
168
+ return;
169
+ }
170
+ await getFsSafeTestHooks()?.beforeArchiveOutputMutation?.("chmod", destinationPath);
171
+ check();
172
+ await assertGuards();
173
+ // Do not recursively verify this owner from inside its serialized apply.
174
+ ancestors.pop();
175
+ await owner.apply(mode, { check, beforeChmod: async () => {
176
+ await assertGuards();
177
+ assertSourceAncestors();
178
+ await assertDirectoryIdentityGuard(guard);
179
+ check();
180
+ await assertResolvedInsideDestination({
181
+ destinationRealDir, targetPath: destinationPath, originalPath,
182
+ });
183
+ check();
184
+ } });
185
+ check();
186
+ await assertGuards();
187
+ assertSourceAncestors();
188
+ }
189
+ finally {
190
+ if (ancestors.at(-1)?.owner === owner)
191
+ ancestors.pop();
192
+ }
144
193
  }
145
194
  finally {
146
- if (ancestors.at(-1)?.owner === owner)
147
- ancestors.pop();
195
+ await owner.close();
148
196
  }
149
- }
150
- finally {
151
- await owner.close();
152
- }
153
- });
197
+ });
198
+ }
199
+ finally {
200
+ sourceAncestors.pop();
201
+ }
154
202
  }
155
203
  else {
156
204
  await ownExtractionDestinationMutation(params.deadline, async () => {
@@ -158,6 +206,25 @@ async function mergeTree(params, publication, durable = true) {
158
206
  try {
159
207
  const options = {
160
208
  mkdir: true, mode, durable: publication ? false : true,
209
+ [onCopySourceAdmission]: (identity, realPath) => {
210
+ assertSourceDirectory(sourceGuard, true);
211
+ assertSourceFrontier();
212
+ inspectFileIdentitySync(() => identity, sourceStat);
213
+ if (!admitPathInsideRoot({
214
+ rootPath: sourceGuard.realPath, candidatePath: realPath, rootIdentity: sourceGuard.stat,
215
+ }))
216
+ throw createArchiveSymlinkTraversalError(originalPath);
217
+ // Public merge permissions come from the admitted descriptor;
218
+ // extraction keeps the separately admitted publication plan.
219
+ if (!plan)
220
+ mode = Number(identity.mode & 511n);
221
+ return { mode, verify() {
222
+ // Only copy publication boundaries scan the active chain.
223
+ // Destination mkdir callbacks must not multiply this work.
224
+ assertSourceDirectory(sourceGuard, true);
225
+ assertSourceAncestors();
226
+ } };
227
+ },
161
228
  };
162
229
  if (publication && durable) {
163
230
  options[onCopyPublication] = async (fd, identity) => {
@@ -1 +1 @@
1
- {"version":3,"file":"archive-read.d.ts","sourceRoot":"","sources":["../src/archive-read.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAiD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA0OpG,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,CAkBjB"}
1
+ {"version":3,"file":"archive-read.d.ts","sourceRoot":"","sources":["../src/archive-read.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAiD,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAqOpG,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,CAkBjB"}
@@ -13,6 +13,7 @@ import { isGzipBuffer } from "./archive-gzip-tail.js";
13
13
  import { inspectTar, replayTar } from "./archive-tar-stream.js";
14
14
  import { loadAdmittedZipArchive } from "./archive-zip-loader.js";
15
15
  import { createZipIntegrityTransform, normalizeZipIntegrityError, } from "./archive-zip-integrity.js";
16
+ import { isZipSymlinkEntry, zipEntryKind } from "./archive-zip-entry.js";
16
17
  import { FsSafeError } from "./errors.js";
17
18
  import { inspectFileIdentity } from "./strict-file-identity.js";
18
19
  import { resolveReadOpenFlags } from "./read-open-flags.js";
@@ -22,8 +23,6 @@ import { admitZipBuffer } from "./archive-zip-admission.js";
22
23
  import { validateNativeZipManifest } from "./archive-zip-manifest.js";
23
24
  import { resolveExtractLimits, resolveTarMeterLimits } from "./archive-limits.js";
24
25
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
25
- const ZIP_UNIX_FILE_TYPE_MASK = 0o170000;
26
- const ZIP_UNIX_SYMLINK_TYPE = 0o120000;
27
26
  function canonicalEntryPath(entryPath) {
28
27
  validateArchiveEntryPath(entryPath, { escapeLabel: "archive root" });
29
28
  return stripArchivePath(entryPath, 0) ?? "";
@@ -109,10 +108,11 @@ async function readZipEntry(buffer, entryPath, maxBytes, admitted) {
109
108
  if (!entry || entry.dir) {
110
109
  throw new Error(`archive entry not found: ${formatErrorDetail(entryPath)}`);
111
110
  }
112
- if (typeof entry.unixPermissions === "number" &&
113
- (entry.unixPermissions & ZIP_UNIX_FILE_TYPE_MASK) === ZIP_UNIX_SYMLINK_TYPE) {
111
+ if (isZipSymlinkEntry(entry)) {
114
112
  throw new Error(`archive entry is a link: ${formatErrorDetail(entryPath)}`);
115
113
  }
114
+ if (zipEntryKind(entry) !== "file")
115
+ throw new Error(`archive entry is not a file: ${formatErrorDetail(entryPath)}`);
116
116
  const integrity = createZipIntegrityTransform(entry);
117
117
  const stream = typeof entry.nodeStream === "function"
118
118
  ? entry.nodeStream()
@@ -10,8 +10,12 @@ export type ZipDirectoryEntry = {
10
10
  creatorSystem: number;
11
11
  externalAttributes: number;
12
12
  size: number;
13
+ compressedSize: number;
14
+ compressionMethod: number;
15
+ crc32: number;
13
16
  path?: string;
14
17
  portableKey: string;
18
+ portableDirectory: boolean;
15
19
  kind: ArchiveEntryKind;
16
20
  };
17
21
  export declare function scanZipDirectory(size: number, limits: ResolvedArchiveExtractLimits, onEntry?: (entry: ZipDirectoryEntry) => void): ZipScan;
@@ -1 +1 @@
1
- {"version":3,"file":"archive-zip-directory.d.ts","sourceRoot":"","sources":["../src/archive-zip-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AA4IF,wBAAiB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC/F,OAAO,CAmET"}
1
+ {"version":3,"file":"archive-zip-directory.d.ts","sourceRoot":"","sources":["../src/archive-zip-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AA4IF,wBAAiB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC/F,OAAO,CAqET"}
@@ -224,6 +224,8 @@ export function* scanZipDirectory(size, limits, onEntry) {
224
224
  onEntry?.({
225
225
  index: count - 1, creatorSystem: central[5], externalAttributes: central.readUInt32LE(38),
226
226
  size: values.uncompressed, path: admittedName.path, portableKey: admittedName.portableKey,
227
+ compressedSize: values.compressed, compressionMethod: central.readUInt16LE(10), crc32: crc,
228
+ portableDirectory: admittedName.portableDirectory,
227
229
  kind: entryKind(central.readUInt32LE(38), admittedName.directory),
228
230
  });
229
231
  at = next;
@@ -1,8 +1,10 @@
1
- import { type ArchiveEntryModePolicy } from "./archive-policy.js";
1
+ import { type ArchiveEntryModePolicy, type ArchiveEntryKind } from "./archive-policy.js";
2
+ import type { ZipDirectoryEntry } from "./archive-zip-directory.js";
2
3
  export type ZipEntry = {
3
4
  name: string;
4
5
  dir: boolean;
5
6
  unixPermissions?: number | null;
7
+ dosPermissions?: number | null;
6
8
  _data?: {
7
9
  crc32?: number;
8
10
  uncompressedSize?: number;
@@ -10,12 +12,14 @@ export type ZipEntry = {
10
12
  nodeStream?: () => NodeJS.ReadableStream;
11
13
  async: (type: "nodebuffer") => Promise<Buffer>;
12
14
  };
15
+ /** Internal: register only after the complete decoder/admission association. */
16
+ export declare function registerAdmittedZipEntry(entry: ZipEntry, physical: ZipDirectoryEntry): void;
13
17
  export declare function zipEntryIntegrityMetadata(entry: ZipEntry): {
14
18
  crc32?: number;
15
19
  uncompressedSize?: number;
16
20
  } | undefined;
17
- export declare function hasDeferredEmptyZipData(entry: ZipEntry): boolean;
18
21
  export declare function isZipSymlinkEntry(entry: ZipEntry): boolean;
22
+ export declare function zipEntryKind(entry: ZipEntry): ArchiveEntryKind;
19
23
  export declare function zipEntryMode(entry: ZipEntry, policy: ArchiveEntryModePolicy | undefined): number;
20
24
  export declare function zipEntryDeclaredSize(entry: ZipEntry): number;
21
25
  //# sourceMappingURL=archive-zip-entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"archive-zip-entry.d.ts","sourceRoot":"","sources":["../src/archive-zip-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7E,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,cAAc,CAAC;IACzC,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAEF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,QAAQ,GACd;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI3D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAGhE;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAK1D;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACzC,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE5D"}
1
+ {"version":3,"file":"archive-zip-entry.d.ts","sourceRoot":"","sources":["../src/archive-zip-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7E,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,cAAc,CAAC;IACzC,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAIF,gFAAgF;AAChF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAQ3F;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,QAAQ,GACd;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI3D;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAE1D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,gBAAgB,CAM9D;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACzC,MAAM,CAOR;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE5D"}
@@ -1,27 +1,42 @@
1
1
  import { resolveArchiveEntryMode, } from "./archive-policy.js";
2
+ const admittedMetadata = new WeakMap();
3
+ /** Internal: register only after the complete decoder/admission association. */
4
+ export function registerAdmittedZipEntry(entry, physical) {
5
+ const mode = physical.creatorSystem === 3 ? physical.externalAttributes >>> 16 : undefined;
6
+ admittedMetadata.set(entry, { kind: physical.kind, mode, size: physical.size });
7
+ entry.dir = physical.kind === "directory";
8
+ // Previously unsupported non-UNIX symlinks must also be recognizable to
9
+ // public preflight consumers using JSZip's conventional type inspection.
10
+ entry.unixPermissions = mode ?? (physical.kind === "symlink" ? 0o120000 : null);
11
+ entry.dosPermissions = physical.creatorSystem === 0 ? physical.externalAttributes & 0x3f : null;
12
+ }
2
13
  export function zipEntryIntegrityMetadata(entry) {
3
14
  const data = entry._data;
4
15
  if (!data || "then" in data)
5
16
  return undefined;
6
17
  return data;
7
18
  }
8
- export function hasDeferredEmptyZipData(entry) {
9
- const data = entry._data;
10
- return Boolean(data && "then" in data);
11
- }
12
19
  const ZIP_UNIX_FILE_TYPE_MASK = 0o170000;
13
20
  const ZIP_UNIX_SYMLINK_TYPE = 0o120000;
14
21
  export function isZipSymlinkEntry(entry) {
15
- return (typeof entry.unixPermissions === "number" &&
16
- (entry.unixPermissions & ZIP_UNIX_FILE_TYPE_MASK) === ZIP_UNIX_SYMLINK_TYPE);
22
+ return zipEntryKind(entry) === "symlink";
23
+ }
24
+ export function zipEntryKind(entry) {
25
+ const metadata = admittedMetadata.get(entry);
26
+ if (metadata)
27
+ return metadata.kind;
28
+ return typeof entry.unixPermissions === "number" &&
29
+ (entry.unixPermissions & ZIP_UNIX_FILE_TYPE_MASK) === ZIP_UNIX_SYMLINK_TYPE
30
+ ? "symlink" : entry.dir ? "directory" : "file";
17
31
  }
18
32
  export function zipEntryMode(entry, policy) {
33
+ const metadata = admittedMetadata.get(entry);
19
34
  return resolveArchiveEntryMode({
20
35
  kind: entry.dir ? "directory" : "file",
21
- archivedMode: entry.unixPermissions,
36
+ archivedMode: metadata ? metadata.mode : entry.unixPermissions,
22
37
  policy,
23
38
  });
24
39
  }
25
40
  export function zipEntryDeclaredSize(entry) {
26
- return Math.max(0, Math.floor(zipEntryIntegrityMetadata(entry)?.uncompressedSize ?? 0));
41
+ return admittedMetadata.get(entry)?.size ?? Math.max(0, Math.floor(zipEntryIntegrityMetadata(entry)?.uncompressedSize ?? 0));
27
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"archive-zip-integrity.d.ts","sourceRoot":"","sources":["../src/archive-zip-integrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAGL,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAIhC,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQhE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAgCtE"}
1
+ {"version":3,"file":"archive-zip-integrity.d.ts","sourceRoot":"","sources":["../src/archive-zip-integrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAIhC,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQhE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CA+BtE"}
@@ -1,6 +1,6 @@
1
1
  import { Transform } from "node:stream";
2
2
  import { ArchiveFormatError } from "./archive-errors.js";
3
- import { hasDeferredEmptyZipData, zipEntryIntegrityMetadata, } from "./archive-zip-entry.js";
3
+ import { zipEntryIntegrityMetadata, } from "./archive-zip-entry.js";
4
4
  import { updateCrc32 } from "./archive-crc32.js";
5
5
  export function normalizeZipIntegrityError(error) {
6
6
  if (error instanceof Error &&
@@ -11,9 +11,8 @@ export function normalizeZipIntegrityError(error) {
11
11
  }
12
12
  export function createZipIntegrityTransform(entry) {
13
13
  const metadata = zipEntryIntegrityMetadata(entry);
14
- const deferredEmpty = hasDeferredEmptyZipData(entry);
15
- const expectedCrc32 = deferredEmpty ? 0 : metadata?.crc32;
16
- const expectedSize = deferredEmpty ? 0 : metadata?.uncompressedSize;
14
+ const expectedCrc32 = metadata?.crc32;
15
+ const expectedSize = metadata?.uncompressedSize;
17
16
  if (typeof expectedCrc32 !== "number" ||
18
17
  !Number.isInteger(expectedCrc32) ||
19
18
  typeof expectedSize !== "number" ||
@@ -1 +1 @@
1
- {"version":3,"file":"archive-zip-loader.d.ts","sourceRoot":"","sources":["../src/archive-zip-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAMF,kFAAkF;AAClF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA6C9B"}
1
+ {"version":3,"file":"archive-zip-loader.d.ts","sourceRoot":"","sources":["../src/archive-zip-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIpE,MAAM,MAAM,mBAAmB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AA2BrE,kFAAkF;AAClF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}