@openclaw/fs-safe 0.10.0 → 0.11.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 (259) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +1 -0
  3. package/README.md +36 -5
  4. package/dist/absolute-path.d.ts.map +1 -1
  5. package/dist/absolute-path.js +3 -2
  6. package/dist/advanced.d.ts +4 -0
  7. package/dist/advanced.d.ts.map +1 -1
  8. package/dist/advanced.js +4 -0
  9. package/dist/archive-durability.d.ts +6 -6
  10. package/dist/archive-durability.d.ts.map +1 -1
  11. package/dist/archive-durability.js +1 -1
  12. package/dist/archive-entry.d.ts.map +1 -1
  13. package/dist/archive-entry.js +4 -5
  14. package/dist/archive-gzip-tail.d.ts +1 -0
  15. package/dist/archive-gzip-tail.d.ts.map +1 -1
  16. package/dist/archive-gzip-tail.js +3 -0
  17. package/dist/archive-input.d.ts.map +1 -1
  18. package/dist/archive-input.js +4 -2
  19. package/dist/archive-merge.d.ts +5 -1
  20. package/dist/archive-merge.d.ts.map +1 -1
  21. package/dist/archive-merge.js +15 -12
  22. package/dist/archive-native.js +4 -4
  23. package/dist/archive-parser.wasm +0 -0
  24. package/dist/archive-read.d.ts.map +1 -1
  25. package/dist/archive-read.js +17 -9
  26. package/dist/archive-staging.d.ts +6 -3
  27. package/dist/archive-staging.d.ts.map +1 -1
  28. package/dist/archive-staging.js +42 -22
  29. package/dist/archive-tar-stream.d.ts.map +1 -1
  30. package/dist/archive-tar-stream.js +7 -6
  31. package/dist/archive-tar-wasm.d.ts.map +1 -1
  32. package/dist/archive-tar-wasm.js +16 -13
  33. package/dist/archive-zip-admission.d.ts +1 -1
  34. package/dist/archive-zip-admission.d.ts.map +1 -1
  35. package/dist/archive-zip-admission.js +48 -12
  36. package/dist/archive-zip-loader.d.ts +6 -0
  37. package/dist/archive-zip-loader.d.ts.map +1 -0
  38. package/dist/archive-zip-loader.js +38 -0
  39. package/dist/archive-zip-names.d.ts.map +1 -1
  40. package/dist/archive-zip-names.js +13 -8
  41. package/dist/archive-zip-preflight.d.ts +2 -3
  42. package/dist/archive-zip-preflight.d.ts.map +1 -1
  43. package/dist/archive-zip-preflight.js +2 -34
  44. package/dist/archive.d.ts.map +1 -1
  45. package/dist/archive.js +12 -10
  46. package/dist/bounded-read.d.ts +5 -0
  47. package/dist/bounded-read.d.ts.map +1 -1
  48. package/dist/bounded-read.js +16 -9
  49. package/dist/copy-file-input.d.ts +0 -1
  50. package/dist/copy-file-input.d.ts.map +1 -1
  51. package/dist/copy-file-input.js +4 -26
  52. package/dist/copy-tree-portable.d.ts.map +1 -1
  53. package/dist/copy-tree-portable.js +57 -26
  54. package/dist/copy.d.ts +1 -1
  55. package/dist/copy.d.ts.map +1 -1
  56. package/dist/copy.js +3 -1
  57. package/dist/directory-durability.d.ts.map +1 -1
  58. package/dist/directory-durability.js +5 -4
  59. package/dist/directory-guard.d.ts +11 -1
  60. package/dist/directory-guard.d.ts.map +1 -1
  61. package/dist/directory-guard.js +53 -11
  62. package/dist/durability.d.ts +1 -1
  63. package/dist/durability.d.ts.map +1 -1
  64. package/dist/durability.js +1 -1
  65. package/dist/file-handle-transfer.d.ts +14 -0
  66. package/dist/file-handle-transfer.d.ts.map +1 -0
  67. package/dist/file-handle-transfer.js +64 -0
  68. package/dist/file-hash.d.ts +3 -0
  69. package/dist/file-hash.d.ts.map +1 -1
  70. package/dist/file-hash.js +91 -31
  71. package/dist/file-lock-sync.d.ts.map +1 -1
  72. package/dist/file-lock-sync.js +8 -4
  73. package/dist/file-store-boundary.d.ts.map +1 -1
  74. package/dist/file-store-boundary.js +7 -5
  75. package/dist/file-store-path.d.ts +3 -0
  76. package/dist/file-store-path.d.ts.map +1 -0
  77. package/dist/file-store-path.js +27 -0
  78. package/dist/file-store-prune.d.ts.map +1 -1
  79. package/dist/file-store-prune.js +6 -4
  80. package/dist/file-store-sync-write.d.ts.map +1 -1
  81. package/dist/file-store-sync-write.js +56 -44
  82. package/dist/file-store.d.ts.map +1 -1
  83. package/dist/file-store.js +2 -18
  84. package/dist/filename.d.ts.map +1 -1
  85. package/dist/filename.js +2 -1
  86. package/dist/guarded-mkdir.d.ts.map +1 -1
  87. package/dist/guarded-mkdir.js +3 -2
  88. package/dist/guest-dispatch-python.d.ts +2 -0
  89. package/dist/guest-dispatch-python.d.ts.map +1 -0
  90. package/dist/guest-dispatch-python.js +117 -0
  91. package/dist/guest-native-python.d.ts +4 -0
  92. package/dist/guest-native-python.d.ts.map +1 -0
  93. package/dist/guest-native-python.js +135 -0
  94. package/dist/guest.d.ts +9 -0
  95. package/dist/guest.d.ts.map +1 -0
  96. package/dist/guest.js +413 -0
  97. package/dist/index.d.ts +1 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/install-path.d.ts.map +1 -1
  100. package/dist/install-path.js +3 -2
  101. package/dist/json-durable-queue-directory.js +3 -3
  102. package/dist/json-durable-queue.d.ts.map +1 -1
  103. package/dist/json-durable-queue.js +6 -4
  104. package/dist/json.d.ts.map +1 -1
  105. package/dist/json.js +2 -1
  106. package/dist/local-roots.d.ts.map +1 -1
  107. package/dist/local-roots.js +2 -1
  108. package/dist/move-path-stage.d.ts.map +1 -1
  109. package/dist/move-path-stage.js +2 -1
  110. package/dist/move-path.d.ts.map +1 -1
  111. package/dist/move-path.js +4 -3
  112. package/dist/mutation-authority.d.ts +1 -0
  113. package/dist/mutation-authority.d.ts.map +1 -1
  114. package/dist/mutation-authority.js +4 -4
  115. package/dist/native-binding.d.ts +7 -2
  116. package/dist/native-binding.d.ts.map +1 -1
  117. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  118. package/dist/native-pinned-write-windows.js +3 -2
  119. package/dist/native-pinned-write.d.ts.map +1 -1
  120. package/dist/native-pinned-write.js +2 -1
  121. package/dist/opened-realpath.d.ts.map +1 -1
  122. package/dist/opened-realpath.js +5 -4
  123. package/dist/output.d.ts +2 -0
  124. package/dist/output.d.ts.map +1 -1
  125. package/dist/output.js +2 -0
  126. package/dist/overwrite-file-handle.d.ts +8 -0
  127. package/dist/overwrite-file-handle.d.ts.map +1 -0
  128. package/dist/overwrite-file-handle.js +42 -0
  129. package/dist/path-case.d.ts +7 -0
  130. package/dist/path-case.d.ts.map +1 -0
  131. package/dist/path-case.js +136 -0
  132. package/dist/path.d.ts.map +1 -1
  133. package/dist/path.js +2 -1
  134. package/dist/permissions-windows.d.ts +1 -1
  135. package/dist/permissions-windows.d.ts.map +1 -1
  136. package/dist/permissions-windows.js +48 -6
  137. package/dist/pinned-open.d.ts.map +1 -1
  138. package/dist/pinned-open.js +3 -1
  139. package/dist/pinned-write.d.ts +2 -2
  140. package/dist/pinned-write.d.ts.map +1 -1
  141. package/dist/pinned-write.js +2 -1
  142. package/dist/private-temp-workspace.d.ts.map +1 -1
  143. package/dist/private-temp-workspace.js +6 -4
  144. package/dist/realpath.d.ts +4 -0
  145. package/dist/realpath.d.ts.map +1 -0
  146. package/dist/realpath.js +43 -0
  147. package/dist/recursive-mkdir-path.d.ts +3 -0
  148. package/dist/recursive-mkdir-path.d.ts.map +1 -0
  149. package/dist/recursive-mkdir-path.js +8 -0
  150. package/dist/replace-directory.d.ts.map +1 -1
  151. package/dist/replace-directory.js +2 -1
  152. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  153. package/dist/replace-file-copy-fallback.js +23 -31
  154. package/dist/replace-file-copy-source.d.ts.map +1 -1
  155. package/dist/replace-file-copy-source.js +7 -12
  156. package/dist/replace-file-mode.d.ts +3 -0
  157. package/dist/replace-file-mode.d.ts.map +1 -0
  158. package/dist/replace-file-mode.js +10 -0
  159. package/dist/replace-file.d.ts +1 -0
  160. package/dist/replace-file.d.ts.map +1 -1
  161. package/dist/replace-file.js +14 -8
  162. package/dist/root-context.d.ts.map +1 -1
  163. package/dist/root-context.js +8 -8
  164. package/dist/root-create-input.d.ts +10 -0
  165. package/dist/root-create-input.d.ts.map +1 -0
  166. package/dist/root-create-input.js +80 -0
  167. package/dist/root-directory-list.d.ts +3 -1
  168. package/dist/root-directory-list.d.ts.map +1 -1
  169. package/dist/root-directory-list.js +21 -3
  170. package/dist/root-entries.d.ts +11 -0
  171. package/dist/root-entries.d.ts.map +1 -0
  172. package/dist/root-entries.js +61 -0
  173. package/dist/root-errors.d.ts +5 -5
  174. package/dist/root-errors.d.ts.map +1 -1
  175. package/dist/root-errors.js +13 -12
  176. package/dist/root-impl.d.ts +13 -3
  177. package/dist/root-impl.d.ts.map +1 -1
  178. package/dist/root-impl.js +40 -36
  179. package/dist/root-options.d.ts +12 -1
  180. package/dist/root-options.d.ts.map +1 -1
  181. package/dist/root-path-existing.d.ts.map +1 -1
  182. package/dist/root-path-existing.js +4 -3
  183. package/dist/root-path-symlink.d.ts.map +1 -1
  184. package/dist/root-path-symlink.js +3 -2
  185. package/dist/root-paths.d.ts.map +1 -1
  186. package/dist/root-paths.js +13 -9
  187. package/dist/root-remove.d.ts +5 -0
  188. package/dist/root-remove.d.ts.map +1 -0
  189. package/dist/root-remove.js +286 -0
  190. package/dist/root-symlink-policy.d.ts +2 -1
  191. package/dist/root-symlink-policy.d.ts.map +1 -1
  192. package/dist/root-symlink-policy.js +2 -2
  193. package/dist/root-write-mode.d.ts.map +1 -1
  194. package/dist/root-write-mode.js +2 -1
  195. package/dist/root.d.ts +2 -1
  196. package/dist/root.d.ts.map +1 -1
  197. package/dist/secret-file.d.ts.map +1 -1
  198. package/dist/secret-file.js +2 -1
  199. package/dist/secret-read-async.d.ts.map +1 -1
  200. package/dist/secret-read-async.js +2 -1
  201. package/dist/secure-file.d.ts.map +1 -1
  202. package/dist/secure-file.js +21 -4
  203. package/dist/secure-temp-dir.d.ts.map +1 -1
  204. package/dist/secure-temp-dir.js +2 -1
  205. package/dist/sibling-staged-file.d.ts +1 -0
  206. package/dist/sibling-staged-file.d.ts.map +1 -1
  207. package/dist/sibling-staged-file.js +42 -8
  208. package/dist/sibling-temp.d.ts +2 -0
  209. package/dist/sibling-temp.d.ts.map +1 -1
  210. package/dist/sibling-temp.js +6 -4
  211. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  212. package/dist/sidecar-lock-acquire.js +15 -4
  213. package/dist/sidecar-lock-policy.d.ts +2 -0
  214. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  215. package/dist/sidecar-lock-policy.js +17 -0
  216. package/dist/staged-directory.d.ts.map +1 -1
  217. package/dist/staged-directory.js +4 -3
  218. package/dist/temp-target.d.ts +14 -12
  219. package/dist/temp-target.d.ts.map +1 -1
  220. package/dist/temp-target.js +12 -6
  221. package/dist/trash.d.ts.map +1 -1
  222. package/dist/trash.js +7 -5
  223. package/dist/unicode-path.d.ts +3 -0
  224. package/dist/unicode-path.d.ts.map +1 -0
  225. package/dist/unicode-path.js +13 -0
  226. package/dist/walk.d.ts.map +1 -1
  227. package/dist/walk.js +3 -2
  228. package/dist/write-file-handle.d.ts +1 -0
  229. package/dist/write-file-handle.d.ts.map +1 -1
  230. package/dist/write-file-handle.js +3 -2
  231. package/docs/advanced.md +4 -0
  232. package/docs/archive.md +25 -5
  233. package/docs/atomic.md +17 -1
  234. package/docs/config.md +1 -0
  235. package/docs/contributing.md +29 -1
  236. package/docs/copy.md +75 -6
  237. package/docs/directory-identity.md +85 -0
  238. package/docs/durability.md +36 -1
  239. package/docs/entries.md +109 -0
  240. package/docs/errors.md +3 -3
  241. package/docs/file-store.md +15 -0
  242. package/docs/guest.md +141 -0
  243. package/docs/in-place-write.md +81 -0
  244. package/docs/index.md +2 -0
  245. package/docs/install.md +31 -0
  246. package/docs/native-helper.md +10 -3
  247. package/docs/native.md +4 -0
  248. package/docs/output.md +32 -6
  249. package/docs/path-case.md +64 -0
  250. package/docs/path-scope.md +1 -1
  251. package/docs/permissions.md +11 -2
  252. package/docs/public-api.md +31 -2
  253. package/docs/root.md +30 -2
  254. package/docs/secure-file.md +2 -0
  255. package/docs/sidecar-lock.md +12 -3
  256. package/docs/temp.md +35 -6
  257. package/docs/types.md +1 -1
  258. package/docs/writing.md +153 -3
  259. package/package.json +14 -8
package/dist/guest.js ADDED
@@ -0,0 +1,413 @@
1
+ // Adapted from OpenClaw's guest filesystem program. Copyright OpenClaw Foundation; MIT.
2
+ import { GUEST_FILESYSTEM_DISPATCH_PYTHON } from "./guest-dispatch-python.js";
3
+ import { GUEST_FILESYSTEM_CREATE_EXCLUSIVE_PYTHON, GUEST_FILESYSTEM_CREATE_STAGING_PYTHON, GUEST_FILESYSTEM_RENAME_NO_REPLACE_PYTHON, } from "./guest-native-python.js";
4
+ /** Exclusive create found an existing destination, including a lost publication race. */
5
+ export const GUEST_FILESYSTEM_CREATE_EXISTS_EXIT_CODE = 17;
6
+ /** Read found a missing parent or file after the admitted root was opened. */
7
+ export const GUEST_FILESYSTEM_READ_NOT_FOUND_EXIT_CODE = 2;
8
+ /** Python 3 source for the descriptor-relative guest filesystem protocol. See docs/guest.md. */
9
+ export const GUEST_FILESYSTEM_PYTHON = [
10
+ `SANDBOX_CREATE_EXISTS_EXIT_CODE = ${GUEST_FILESYSTEM_CREATE_EXISTS_EXIT_CODE}`,
11
+ `SANDBOX_READ_NOT_FOUND_EXIT_CODE = ${GUEST_FILESYSTEM_READ_NOT_FOUND_EXIT_CODE}`,
12
+ "import ctypes",
13
+ "import errno",
14
+ "import json",
15
+ "import os",
16
+ "import secrets",
17
+ "import stat",
18
+ "import sys",
19
+ "",
20
+ "operation = sys.argv[1]",
21
+ "",
22
+ "DIR_FLAGS = os.O_RDONLY",
23
+ "if hasattr(os, 'O_DIRECTORY'):",
24
+ " DIR_FLAGS |= os.O_DIRECTORY",
25
+ "if hasattr(os, 'O_NOFOLLOW'):",
26
+ " DIR_FLAGS |= os.O_NOFOLLOW",
27
+ "",
28
+ "READ_FLAGS = os.O_RDONLY",
29
+ "if hasattr(os, 'O_NOFOLLOW'):",
30
+ " READ_FLAGS |= os.O_NOFOLLOW",
31
+ "if hasattr(os, 'O_NONBLOCK'):",
32
+ " READ_FLAGS |= os.O_NONBLOCK",
33
+ "",
34
+ "WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL",
35
+ "if hasattr(os, 'O_NOFOLLOW'):",
36
+ " WRITE_FLAGS |= os.O_NOFOLLOW",
37
+ "",
38
+ "def split_relative(path_value):",
39
+ " segments = []",
40
+ " for segment in path_value.split('/'):",
41
+ " if not segment or segment == '.':",
42
+ " continue",
43
+ " if segment == '..':",
44
+ " raise OSError(errno.EPERM, 'path traversal is not allowed', segment)",
45
+ " segments.append(segment)",
46
+ " return segments",
47
+ "",
48
+ "def open_dir(path_value, dir_fd=None):",
49
+ " return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)",
50
+ "",
51
+ "def walk_dir(root_fd, rel_path, mkdir_enabled):",
52
+ " current_fd = os.dup(root_fd)",
53
+ " try:",
54
+ " for segment in split_relative(rel_path):",
55
+ " try:",
56
+ " next_fd = open_dir(segment, dir_fd=current_fd)",
57
+ " except FileNotFoundError:",
58
+ " if not mkdir_enabled:",
59
+ " raise",
60
+ " os.mkdir(segment, 0o777, dir_fd=current_fd)",
61
+ " next_fd = open_dir(segment, dir_fd=current_fd)",
62
+ " os.close(current_fd)",
63
+ " current_fd = next_fd",
64
+ " return current_fd",
65
+ " except Exception:",
66
+ " os.close(current_fd)",
67
+ " raise",
68
+ "",
69
+ "def create_temp_file(parent_fd):",
70
+ " prefix = '.openclaw-write-'",
71
+ " for _ in range(128):",
72
+ " candidate = prefix + secrets.token_hex(6)",
73
+ " try:",
74
+ " fd = os.open(candidate, WRITE_FLAGS, 0o600, dir_fd=parent_fd)",
75
+ " return candidate, fd",
76
+ " except FileExistsError:",
77
+ " continue",
78
+ " raise RuntimeError('failed to allocate sandbox temp file')",
79
+ "",
80
+ "def create_temp_dir(parent_fd, mode):",
81
+ " prefix = '.openclaw-move-'",
82
+ " for _ in range(128):",
83
+ " candidate = prefix + secrets.token_hex(6)",
84
+ " try:",
85
+ " os.mkdir(candidate, mode, dir_fd=parent_fd)",
86
+ " return candidate",
87
+ " except FileExistsError:",
88
+ " continue",
89
+ " raise RuntimeError('failed to allocate sandbox temp directory')",
90
+ "",
91
+ GUEST_FILESYSTEM_CREATE_STAGING_PYTHON,
92
+ "",
93
+ "def existing_regular_file_mode(parent_fd, basename):",
94
+ " try:",
95
+ " target_stat = os.lstat(basename, dir_fd=parent_fd)",
96
+ " except FileNotFoundError:",
97
+ " return None",
98
+ " if stat.S_ISREG(target_stat.st_mode):",
99
+ " return stat.S_IMODE(target_stat.st_mode)",
100
+ " return None",
101
+ "",
102
+ "def write_all(file_fd, data):",
103
+ " view = memoryview(data)",
104
+ " while view:",
105
+ " written = os.write(file_fd, view)",
106
+ " if written <= 0:",
107
+ " raise OSError(errno.EIO, 'failed to write copied file')",
108
+ " view = view[written:]",
109
+ "",
110
+ GUEST_FILESYSTEM_RENAME_NO_REPLACE_PYTHON,
111
+ "",
112
+ "def copy_regular_file(src_parent_fd, src_basename, dst_parent_fd, dst_basename):",
113
+ " src_fd = os.open(src_basename, READ_FLAGS, dir_fd=src_parent_fd)",
114
+ " dst_fd = None",
115
+ " destination_created = False",
116
+ " copy_completed = False",
117
+ " try:",
118
+ " src_stat = os.fstat(src_fd)",
119
+ " if not stat.S_ISREG(src_stat.st_mode):",
120
+ " raise OSError(errno.EPERM, 'only regular files are allowed', src_basename)",
121
+ " if src_stat.st_nlink > 1:",
122
+ " raise OSError(errno.EPERM, 'hardlinked file is not allowed', src_basename)",
123
+ " dst_fd = os.open(dst_basename, WRITE_FLAGS, stat.S_IMODE(src_stat.st_mode), dir_fd=dst_parent_fd)",
124
+ " destination_created = True",
125
+ " while True:",
126
+ " chunk = os.read(src_fd, 65536)",
127
+ " if not chunk:",
128
+ " break",
129
+ " write_all(dst_fd, chunk)",
130
+ " try:",
131
+ " os.fchmod(dst_fd, stat.S_IMODE(src_stat.st_mode))",
132
+ " except AttributeError:",
133
+ " pass",
134
+ " os.fsync(dst_fd)",
135
+ " copy_completed = True",
136
+ " return entry_identity(src_stat)",
137
+ " finally:",
138
+ " if dst_fd is not None:",
139
+ " os.close(dst_fd)",
140
+ " if destination_created and not copy_completed:",
141
+ " try:",
142
+ " os.unlink(dst_basename, dir_fd=dst_parent_fd)",
143
+ " except FileNotFoundError:",
144
+ " pass",
145
+ " os.close(src_fd)",
146
+ "",
147
+ "def copy_file_atomic(src_parent_fd, src_basename, dst_parent_fd, dst_basename):",
148
+ " prefix = '.openclaw-copy-'",
149
+ " temp_name = None",
150
+ " try:",
151
+ " for _ in range(128):",
152
+ " candidate = prefix + secrets.token_hex(6)",
153
+ " try:",
154
+ " copy_regular_file(src_parent_fd, src_basename, dst_parent_fd, candidate)",
155
+ " temp_name = candidate",
156
+ " break",
157
+ " except FileExistsError:",
158
+ " continue",
159
+ " if temp_name is None:",
160
+ " raise RuntimeError('failed to allocate sandbox copy temp file')",
161
+ " os.replace(temp_name, dst_basename, src_dir_fd=dst_parent_fd, dst_dir_fd=dst_parent_fd)",
162
+ " temp_name = None",
163
+ " os.fsync(dst_parent_fd)",
164
+ " finally:",
165
+ " if temp_name is not None:",
166
+ " try:",
167
+ " os.unlink(temp_name, dir_fd=dst_parent_fd)",
168
+ " except FileNotFoundError:",
169
+ " pass",
170
+ "",
171
+ "def write_atomic(parent_fd, basename, stdin_buffer):",
172
+ " target_mode = existing_regular_file_mode(parent_fd, basename)",
173
+ " temp_fd = None",
174
+ " temp_name = None",
175
+ " try:",
176
+ " temp_name, temp_fd = create_temp_file(parent_fd)",
177
+ " while True:",
178
+ " chunk = stdin_buffer.read(65536)",
179
+ " if not chunk:",
180
+ " break",
181
+ " write_all(temp_fd, chunk)",
182
+ " if target_mode is not None:",
183
+ " try:",
184
+ " os.fchmod(temp_fd, target_mode)",
185
+ " except AttributeError:",
186
+ " pass",
187
+ " os.fsync(temp_fd)",
188
+ " os.close(temp_fd)",
189
+ " temp_fd = None",
190
+ " os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)",
191
+ " temp_name = None",
192
+ " os.fsync(parent_fd)",
193
+ " finally:",
194
+ " if temp_fd is not None:",
195
+ " os.close(temp_fd)",
196
+ " if temp_name is not None:",
197
+ " try:",
198
+ " os.unlink(temp_name, dir_fd=parent_fd)",
199
+ " except FileNotFoundError:",
200
+ " pass",
201
+ "",
202
+ GUEST_FILESYSTEM_CREATE_EXCLUSIVE_PYTHON,
203
+ "",
204
+ "def read_file_impl(parent_fd, basename, max_bytes):",
205
+ " file_fd = os.open(basename, READ_FLAGS, dir_fd=parent_fd)",
206
+ " try:",
207
+ " file_stat = os.fstat(file_fd)",
208
+ " if not stat.S_ISREG(file_stat.st_mode):",
209
+ " raise OSError(errno.EPERM, 'only regular files are allowed', basename)",
210
+ " if file_stat.st_nlink > 1:",
211
+ " raise OSError(errno.EPERM, 'hardlinked file is not allowed', basename)",
212
+ " if max_bytes is not None and file_stat.st_size > max_bytes:",
213
+ " raise OSError(errno.EFBIG, 'file exceeds bounded read limit', basename)",
214
+ " bytes_read = 0",
215
+ " while True:",
216
+ " read_size = 65536 if max_bytes is None else min(65536, max_bytes - bytes_read + 1)",
217
+ " chunk = os.read(file_fd, read_size)",
218
+ " if not chunk:",
219
+ " break",
220
+ " bytes_read += len(chunk)",
221
+ " if max_bytes is not None and bytes_read > max_bytes:",
222
+ " raise OSError(errno.EFBIG, 'file exceeds bounded read limit', basename)",
223
+ " write_all(1, chunk)",
224
+ " finally:",
225
+ " os.close(file_fd)",
226
+ "",
227
+ "def read_file(parent_fd, basename):",
228
+ " read_file_impl(parent_fd, basename, None)",
229
+ "",
230
+ "def read_file_bounded(parent_fd, basename, max_bytes):",
231
+ " if max_bytes < 0:",
232
+ " raise OSError(errno.EINVAL, 'read limit must be non-negative', basename)",
233
+ " read_file_impl(parent_fd, basename, max_bytes)",
234
+ "",
235
+ "def remove_tree(parent_fd, basename):",
236
+ " entry_stat = os.lstat(basename, dir_fd=parent_fd)",
237
+ " if not stat.S_ISDIR(entry_stat.st_mode) or stat.S_ISLNK(entry_stat.st_mode):",
238
+ " os.unlink(basename, dir_fd=parent_fd)",
239
+ " return",
240
+ " dir_fd = open_dir(basename, dir_fd=parent_fd)",
241
+ " try:",
242
+ " for child in os.listdir(dir_fd):",
243
+ " remove_tree(dir_fd, child)",
244
+ " finally:",
245
+ " os.close(dir_fd)",
246
+ " os.rmdir(basename, dir_fd=parent_fd)",
247
+ "",
248
+ "def entry_identity(entry_stat):",
249
+ " return (",
250
+ " entry_stat.st_dev,",
251
+ " entry_stat.st_ino,",
252
+ " entry_stat.st_mode,",
253
+ " entry_stat.st_size,",
254
+ " getattr(entry_stat, 'st_mtime_ns', int(entry_stat.st_mtime * 1000000000)),",
255
+ " getattr(entry_stat, 'st_ctime_ns', int(entry_stat.st_ctime * 1000000000)),",
256
+ " )",
257
+ "",
258
+ "def inode_identity(entry_stat):",
259
+ " return (entry_stat.st_dev, entry_stat.st_ino)",
260
+ "",
261
+ "def same_identity(expected, entry_stat):",
262
+ " return expected == entry_identity(entry_stat)",
263
+ "",
264
+ "def source_changed_error(basename):",
265
+ " return OSError(getattr(errno, 'ESTALE', errno.EIO), 'source changed during move fallback cleanup', basename)",
266
+ "",
267
+ "def copy_entry(src_parent_fd, src_basename, dst_parent_fd, dst_basename):",
268
+ " src_stat = os.lstat(src_basename, dir_fd=src_parent_fd)",
269
+ " if stat.S_ISDIR(src_stat.st_mode) and not stat.S_ISLNK(src_stat.st_mode):",
270
+ " os.mkdir(dst_basename, stat.S_IMODE(src_stat.st_mode) or 0o755, dir_fd=dst_parent_fd)",
271
+ " copied_children = []",
272
+ " src_dir_fd = None",
273
+ " dst_dir_fd = None",
274
+ " try:",
275
+ " src_dir_fd = open_dir(src_basename, dir_fd=src_parent_fd)",
276
+ " src_stat = os.fstat(src_dir_fd)",
277
+ " dst_dir_fd = open_dir(dst_basename, dir_fd=dst_parent_fd)",
278
+ " for child in os.listdir(src_dir_fd):",
279
+ " copied_children.append((child, copy_entry(src_dir_fd, child, dst_dir_fd, child)))",
280
+ " except Exception:",
281
+ " if dst_dir_fd is not None:",
282
+ " os.close(dst_dir_fd)",
283
+ " dst_dir_fd = None",
284
+ " try:",
285
+ " remove_tree(dst_parent_fd, dst_basename)",
286
+ " except FileNotFoundError:",
287
+ " pass",
288
+ " raise",
289
+ " finally:",
290
+ " if src_dir_fd is not None:",
291
+ " os.close(src_dir_fd)",
292
+ " if dst_dir_fd is not None:",
293
+ " os.close(dst_dir_fd)",
294
+ " return ('dir', entry_identity(src_stat), copied_children)",
295
+ " if stat.S_ISLNK(src_stat.st_mode):",
296
+ " link_target = os.readlink(src_basename, dir_fd=src_parent_fd)",
297
+ " os.symlink(link_target, dst_basename, dir_fd=dst_parent_fd)",
298
+ " return ('leaf', entry_identity(src_stat), None)",
299
+ " return ('leaf', copy_regular_file(src_parent_fd, src_basename, dst_parent_fd, dst_basename), None)",
300
+ "",
301
+ "def remove_copied_entry(parent_fd, basename, manifest):",
302
+ " kind, expected_identity, children = manifest",
303
+ " current_stat = os.lstat(basename, dir_fd=parent_fd)",
304
+ " if not same_identity(expected_identity, current_stat):",
305
+ " raise source_changed_error(basename)",
306
+ " if kind != 'dir':",
307
+ " os.unlink(basename, dir_fd=parent_fd)",
308
+ " return",
309
+ " dir_fd = open_dir(basename, dir_fd=parent_fd)",
310
+ " try:",
311
+ " for child, child_manifest in children:",
312
+ " remove_copied_entry(dir_fd, child, child_manifest)",
313
+ " finally:",
314
+ " os.close(dir_fd)",
315
+ " os.rmdir(basename, dir_fd=parent_fd)",
316
+ "",
317
+ "def move_entry(src_parent_fd, src_basename, dst_parent_fd, dst_basename):",
318
+ " try:",
319
+ " os.rename(src_basename, dst_basename, src_dir_fd=src_parent_fd, dst_dir_fd=dst_parent_fd)",
320
+ " os.fsync(dst_parent_fd)",
321
+ " os.fsync(src_parent_fd)",
322
+ " return",
323
+ " except OSError as err:",
324
+ " if err.errno != errno.EXDEV:",
325
+ " raise",
326
+ " src_stat = os.lstat(src_basename, dir_fd=src_parent_fd)",
327
+ " if stat.S_ISDIR(src_stat.st_mode) and not stat.S_ISLNK(src_stat.st_mode):",
328
+ " temp_dir_name = create_temp_dir(dst_parent_fd, stat.S_IMODE(src_stat.st_mode) or 0o755)",
329
+ " copied_children = []",
330
+ " temp_dir_fd = None",
331
+ " src_dir_fd = None",
332
+ " try:",
333
+ " temp_dir_fd = open_dir(temp_dir_name, dir_fd=dst_parent_fd)",
334
+ " src_dir_fd = open_dir(src_basename, dir_fd=src_parent_fd)",
335
+ " src_stat = os.fstat(src_dir_fd)",
336
+ " for child in os.listdir(src_dir_fd):",
337
+ " copied_children.append((child, copy_entry(src_dir_fd, child, temp_dir_fd, child)))",
338
+ " os.close(src_dir_fd)",
339
+ " src_dir_fd = None",
340
+ " os.close(temp_dir_fd)",
341
+ " temp_dir_fd = None",
342
+ " os.rename(temp_dir_name, dst_basename, src_dir_fd=dst_parent_fd, dst_dir_fd=dst_parent_fd)",
343
+ " except Exception:",
344
+ " if src_dir_fd is not None:",
345
+ " os.close(src_dir_fd)",
346
+ " if temp_dir_fd is not None:",
347
+ " os.close(temp_dir_fd)",
348
+ " try:",
349
+ " remove_tree(dst_parent_fd, temp_dir_name)",
350
+ " except FileNotFoundError:",
351
+ " pass",
352
+ " raise",
353
+ " remove_copied_entry(src_parent_fd, src_basename, ('dir', entry_identity(src_stat), copied_children))",
354
+ " os.fsync(dst_parent_fd)",
355
+ " os.fsync(src_parent_fd)",
356
+ " return",
357
+ " if stat.S_ISLNK(src_stat.st_mode):",
358
+ " link_target = os.readlink(src_basename, dir_fd=src_parent_fd)",
359
+ " try:",
360
+ " os.unlink(dst_basename, dir_fd=dst_parent_fd)",
361
+ " except FileNotFoundError:",
362
+ " pass",
363
+ " os.symlink(link_target, dst_basename, dir_fd=dst_parent_fd)",
364
+ " os.unlink(src_basename, dir_fd=src_parent_fd)",
365
+ " os.fsync(dst_parent_fd)",
366
+ " os.fsync(src_parent_fd)",
367
+ " return",
368
+ " src_fd = os.open(src_basename, READ_FLAGS, dir_fd=src_parent_fd)",
369
+ " temp_fd = None",
370
+ " temp_name = None",
371
+ " try:",
372
+ " src_file_stat = os.fstat(src_fd)",
373
+ " if not stat.S_ISREG(src_file_stat.st_mode):",
374
+ " raise OSError(errno.EPERM, 'only regular files are allowed', src_basename)",
375
+ " if src_file_stat.st_nlink > 1:",
376
+ " raise OSError(errno.EPERM, 'hardlinked file is not allowed', src_basename)",
377
+ " temp_name, temp_fd = create_temp_file(dst_parent_fd)",
378
+ " while True:",
379
+ " chunk = os.read(src_fd, 65536)",
380
+ " if not chunk:",
381
+ " break",
382
+ " write_all(temp_fd, chunk)",
383
+ " try:",
384
+ " os.fchmod(temp_fd, stat.S_IMODE(src_stat.st_mode))",
385
+ " except AttributeError:",
386
+ " pass",
387
+ " os.fsync(temp_fd)",
388
+ " os.close(temp_fd)",
389
+ " temp_fd = None",
390
+ " os.replace(temp_name, dst_basename, src_dir_fd=dst_parent_fd, dst_dir_fd=dst_parent_fd)",
391
+ " temp_name = None",
392
+ " os.unlink(src_basename, dir_fd=src_parent_fd)",
393
+ " os.fsync(dst_parent_fd)",
394
+ " os.fsync(src_parent_fd)",
395
+ " finally:",
396
+ " if temp_fd is not None:",
397
+ " os.close(temp_fd)",
398
+ " if temp_name is not None:",
399
+ " try:",
400
+ " os.unlink(temp_name, dir_fd=dst_parent_fd)",
401
+ " except FileNotFoundError:",
402
+ " pass",
403
+ " os.close(src_fd)",
404
+ "",
405
+ "basename_indexes = (4, 7) if operation in ('copy', 'rename') else (4,) if operation in ('read', 'write', 'create', 'remove') else ()",
406
+ "for basename_index in basename_indexes:",
407
+ " basename = sys.argv[basename_index]",
408
+ " if not basename or basename in ('.', '..') or '/' in basename or '\\x00' in basename:",
409
+ " raise OSError(errno.EINVAL, 'invalid basename', basename)",
410
+ "",
411
+ GUEST_FILESYSTEM_DISPATCH_PYTHON,
412
+ ].join("\n");
413
+ export { GUEST_FILESYSTEM_RENAME_NO_REPLACE_PYTHON };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { FsSafeError, categorizeFsSafeError, type FsSafeErrorCategory, type FsSafeErrorCode, type FsSafeErrorDetails, } from "./errors.js";
2
- export { DEFAULT_ROOT_MAX_BYTES, root, type DenyMutationPolicy, type HardlinkPolicy, type RenameIdentityPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCopySource, type RootCopyPublicationReceipt, type CopyCloneMode, type RootCreateJsonOptions, type RootCreateOptions, type RootDefaults, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions, type RootWalkDataEntry, type RootWalkDataEntryKind, type RootWalkDirectoryErrorBehavior, type RootWalkEntry, type RootWalkEntryFilter, type RootWalkEntryFilterResult, type RootWalkEntryKind, type RootWalkLimitBehavior, type RootWalkOptions, type RootWalkSymlinkPolicy, type SymlinkPolicy, type MutationSymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root.js";
2
+ export { DEFAULT_ROOT_MAX_BYTES, root, type DenyMutationPolicy, type HardlinkPolicy, type RenameIdentityPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCopySource, type RootCopyPublicationReceipt, type CopyCloneMode, type RootCreateJsonOptions, type RootCreateOptions, type RootCreateStreamOptions, type RootDefaults, type RootEntriesOptions, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions, type RootWalkDataEntry, type RootWalkDataEntryKind, type RootWalkDirectoryErrorBehavior, type RootWalkEntry, type RootWalkEntryFilter, type RootWalkEntryFilterResult, type RootWalkEntryKind, type RootWalkLimitBehavior, type RootWalkOptions, type RootWalkSymlinkPolicy, type SymlinkPolicy, type MutationSymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root.js";
3
3
  export type { ContainmentGuarantee } from "./containment.js";
4
4
  export { configureFsSafePython, configureFsSafeNative, getFsSafeNativeConfig, type FsSafePythonConfig, type FsSafeNativeConfig, type FsSafeNativeMode, } from "./native-config.js";
5
5
  export { writeExternalFileWithinRoot, type ExternalFileWriteOptions, type ExternalFileWriteResult, } from "./output.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,IAAI,EACJ,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,EACnC,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,2BAA2B,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,IAAI,EACJ,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,EACnC,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,2BAA2B,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"install-path.d.ts","sourceRoot":"","sources":["../src/install-path.ts"],"names":[],"mappings":"AAKA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiB3D;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAe5D;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkDhB"}
1
+ {"version":3,"file":"install-path.d.ts","sourceRoot":"","sources":["../src/install-path.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiB3D;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAe5D;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkDhB"}
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import fsSync from "node:fs";
3
3
  import path from "node:path";
4
4
  import { isPathInside } from "./path.js";
5
+ import { realpathSync } from "./realpath.js";
5
6
  export function safeDirName(input) {
6
7
  const trimmed = input.trim();
7
8
  if (!trimmed) {
@@ -55,7 +56,7 @@ export async function assertCanonicalPathWithinBase(params) {
55
56
  else if (!baseLstat.isDirectory()) {
56
57
  throw new Error(`Invalid ${params.boundaryLabel}: base directory must be a directory`);
57
58
  }
58
- const baseRealPath = fsSync.realpathSync.native(baseDir);
59
+ const baseRealPath = realpathSync.native(baseDir);
59
60
  const validateDirectory = async (dirPath) => {
60
61
  const resolvedDirPath = path.resolve(dirPath);
61
62
  const dirLstat = fsSync.lstatSync(dirPath);
@@ -71,7 +72,7 @@ export async function assertCanonicalPathWithinBase(params) {
71
72
  else if (!dirLstat.isDirectory()) {
72
73
  throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
73
74
  }
74
- const dirRealPath = fsSync.realpathSync.native(dirPath);
75
+ const dirRealPath = realpathSync.native(dirPath);
75
76
  if (!isPathInside(baseRealPath, dirRealPath)) {
76
77
  throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
77
78
  }
@@ -1,10 +1,10 @@
1
- import fsSync from "node:fs";
2
1
  import path from "node:path";
3
2
  import { syncDirectory } from "./directory-durability.js";
4
3
  import { isPathRelativeEscape } from "./path.js";
4
+ import { realpathSync } from "./realpath.js";
5
5
  export async function syncQueueDirectoryCreation(dir, validationBase) {
6
- const baseReal = fsSync.realpathSync.native(validationBase);
7
- const targetReal = fsSync.realpathSync.native(dir);
6
+ const baseReal = realpathSync.native(validationBase);
7
+ const targetReal = realpathSync.native(dir);
8
8
  const relative = path.relative(baseReal, targetReal);
9
9
  if (isPathRelativeEscape(relative)) {
10
10
  throw new Error(`durable queue directory escapes validation base: ${dir}`);
@@ -1 +1 @@
1
- {"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAOF,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AAM3E,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAO5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AAiLD,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAyDD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAsBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CA+Dd;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
1
+ {"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAOF,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AAM3E,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAO5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AAiLD,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAyDD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAsBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CA+Dd;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
@@ -7,6 +7,8 @@ import { syncQueueDirectoryCreation } from "./json-durable-queue-directory.js";
7
7
  import { getErrorCode, acknowledgeDurableQueueEntry, claimDurableQueueEntry, completeDeliveredQueueEntry, moveDurableQueueEntryToFailed, } from "./json-durable-queue-ownership.js";
8
8
  import { stringifyJsonDocument } from "./json-stringify.js";
9
9
  import { resolveReadOpenFlags } from "./read-open-flags.js";
10
+ import { realpathSync } from "./realpath.js";
11
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
10
12
  import { replaceFileAtomicWithDirectorySync } from "./replace-file.js";
11
13
  import { assertSafePathSegment } from "./safe-path-segment.js";
12
14
  import { inspectFileIdentity } from "./strict-file-identity.js";
@@ -60,7 +62,7 @@ async function ensureJsonDurableQueueDir(dir, validationRoot) {
60
62
  ? validationRoot
61
63
  : queueValidationRoot(dir);
62
64
  await assertNoSymlinkDirectorySegments(root, dir, true);
63
- await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
65
+ await fs.promises.mkdir(recursiveMkdirPath(dir), { recursive: true, mode: 0o700 });
64
66
  await assertNoSymlinkDirectorySegments(root, dir, false);
65
67
  await chmodQueueDirectory(dir);
66
68
  await syncQueueDirectoryCreation(dir, root.path);
@@ -115,7 +117,7 @@ async function isDarwinSystemAlias(dir, stat) {
115
117
  return false;
116
118
  }
117
119
  try {
118
- return fs.realpathSync.native(resolved) === `/private${resolved}`;
120
+ return realpathSync.native(resolved) === `/private${resolved}`;
119
121
  }
120
122
  catch {
121
123
  return false;
@@ -131,7 +133,7 @@ async function assertNoSymlinkDirectorySegments(validationRoot, dir, allowMissin
131
133
  if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
132
134
  throw new Error(`durable queue path is not a directory: ${dir}`);
133
135
  }
134
- base = fs.realpathSync.native(base);
136
+ base = realpathSync.native(base);
135
137
  target = path.join(base, ...relative.split(path.sep).filter(Boolean));
136
138
  current = base;
137
139
  baseStat = fs.lstatSync(base);
@@ -159,7 +161,7 @@ async function assertNoSymlinkDirectorySegments(validationRoot, dir, allowMissin
159
161
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
160
162
  if (stat.isSymbolicLink() && validationRoot.allowSymlinkBase) {
161
163
  if (await isDarwinSystemAlias(current, stat)) {
162
- current = fs.realpathSync.native(current);
164
+ current = realpathSync.native(current);
163
165
  continue;
164
166
  }
165
167
  }
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
package/dist/json.js CHANGED
@@ -8,6 +8,7 @@ import { stringifyJsonDocument } from "./json-stringify.js";
8
8
  import { readRegularFile, readRegularFileSync, statRegularFile } from "./regular-file.js";
9
9
  import { openRootFileSync } from "./root-file.js";
10
10
  import { resolveReadOpenFlags } from "./read-open-flags.js";
11
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
11
12
  import { writeTextAtomic } from "./text-atomic.js";
12
13
  import { sleep } from "./timing.js";
13
14
  const READ_RETRY_MAX_ATTEMPTS = 5;
@@ -169,7 +170,7 @@ export function writeJsonSync(pathname, data) {
169
170
  // Keep literal parent segments so staging follows the same symlinks as the target.
170
171
  const tmpPath = path.format({ ...path.parse(pathname), base: `.fs-safe-${randomUUID()}.tmp` });
171
172
  const payload = `${stringifyJsonDocument(data, null, 2)}\n`;
172
- fsSync.mkdirSync(path.dirname(pathname), { recursive: true, mode: JSON_DIR_MODE });
173
+ fsSync.mkdirSync(recursiveMkdirPath(path.dirname(pathname)), { recursive: true, mode: JSON_DIR_MODE });
173
174
  try {
174
175
  const tempIdentity = writeTempJsonFile(tmpPath, payload);
175
176
  trySetSecureMode(tmpPath, tempIdentity);
@@ -1 +1 @@
1
- {"version":3,"file":"local-roots.d.ts","sourceRoot":"","sources":["../src/local-roots.ts"],"names":[],"mappings":"AAOA,OAAO,EAAQ,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG,sBAAsB,GAAG;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,sBAAsB,GAAG;IACnE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAsDF,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,oBAAoB,GAAG,IAAI,CAqC7B;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAiCtC"}
1
+ {"version":3,"file":"local-roots.d.ts","sourceRoot":"","sources":["../src/local-roots.ts"],"names":[],"mappings":"AAQA,OAAO,EAAQ,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG,sBAAsB,GAAG;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,sBAAsB,GAAG;IACnE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAsDF,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,oBAAoB,GAAG,IAAI,CAqC7B;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAiCtC"}
@@ -4,6 +4,7 @@ import { normalizeMaxBytes } from "./byte-budget.js";
4
4
  import { FsSafeError } from "./errors.js";
5
5
  import { expandHomePrefix, resolveRequiredHomeDir } from "./home-dir.js";
6
6
  import { isFileUrl, safeFileURLToPath } from "./local-file-access.js";
7
+ import { realpathSync } from "./realpath.js";
7
8
  import { ROOT_PATH_ALIAS_POLICIES, resolveRootPathSync } from "./root-path.js";
8
9
  import { root } from "./root.js";
9
10
  function resolveLocalPathInput(input, label) {
@@ -52,7 +53,7 @@ function resolveRootRealSync(rootDir) {
52
53
  if (!stat.isDirectory()) {
53
54
  return null;
54
55
  }
55
- return fsSync.realpathSync(rootDir);
56
+ return realpathSync(rootDir);
56
57
  }
57
58
  catch {
58
59
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"move-path-stage.d.ts","sourceRoot":"","sources":["../src/move-path-stage.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAQnD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM;oBAkBrC,WAAW,GAAG,IAAI;yBAPV,IAAI;iBAkBf,IAAI;eACA,OAAO,CAAC,IAAI,CAAC;GAajC"}
1
+ {"version":3,"file":"move-path-stage.d.ts","sourceRoot":"","sources":["../src/move-path-stage.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AASnD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM;oBAkBrC,WAAW,GAAG,IAAI;yBAPV,IAAI;iBAkBf,IAAI;eACA,OAAO,CAAC,IAAI,CAAC;GAajC"}
@@ -5,6 +5,7 @@ import { FsSafeError } from "./errors.js";
5
5
  import { guardedRm, guardedRmSync } from "./guarded-mutation.js";
6
6
  import { sameFileIdentityForCleanup } from "./file-identity.js";
7
7
  import { registerTempPathForExit } from "./temp-cleanup.js";
8
+ import { realpathSync } from "./realpath.js";
8
9
  export async function createMoveStageOwner(staged) {
9
10
  const parent = await createAsyncDirectoryGuard(path.dirname(staged), { bigint: true });
10
11
  let identity;
@@ -14,7 +15,7 @@ export async function createMoveStageOwner(staged) {
14
15
  const current = fsSync.lstatSync(parent.dir, { bigint: true });
15
16
  if (!current.isDirectory() || current.isSymbolicLink() ||
16
17
  !sameFileIdentityForCleanup(current, parent.stat) ||
17
- fsSync.realpathSync.native(parent.dir) !== parent.realPath)
18
+ realpathSync.native(parent.dir) !== parent.realPath)
18
19
  throw changed();
19
20
  };
20
21
  const assertCurrent = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,uFAAuF;IACvF,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,qFAAqF;IACrF,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AAiRD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAwGf"}
1
+ {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AAuBA,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,uFAAuF;IACvF,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,qFAAqF;IACrF,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AAiRD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAwGf"}