@openclaw/fs-safe 0.15.0 → 0.16.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 (184) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +28 -6
  3. package/dist/advanced.d.ts +1 -0
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +1 -0
  6. package/dist/archive-kind.d.ts +0 -1
  7. package/dist/archive-kind.d.ts.map +1 -1
  8. package/dist/archive-kind.js +5 -17
  9. package/dist/archive-parser.wasm +0 -0
  10. package/dist/archive-read.d.ts.map +1 -1
  11. package/dist/archive-read.js +6 -7
  12. package/dist/archive-tar-stream.d.ts +3 -0
  13. package/dist/archive-tar-stream.d.ts.map +1 -1
  14. package/dist/archive-tar-stream.js +56 -37
  15. package/dist/archive-tar-wasm.d.ts +16 -4
  16. package/dist/archive-tar-wasm.d.ts.map +1 -1
  17. package/dist/archive-tar-wasm.js +134 -34
  18. package/dist/archive.d.ts.map +1 -1
  19. package/dist/archive.js +5 -6
  20. package/dist/clone-metadata.d.ts +1 -0
  21. package/dist/clone-metadata.d.ts.map +1 -1
  22. package/dist/clone-metadata.js +6 -2
  23. package/dist/create-directory.d.ts +20 -0
  24. package/dist/create-directory.d.ts.map +1 -0
  25. package/dist/create-directory.js +130 -0
  26. package/dist/create-file-async.d.ts +7 -0
  27. package/dist/create-file-async.d.ts.map +1 -0
  28. package/dist/create-file-async.js +121 -0
  29. package/dist/create-file.d.ts +8 -0
  30. package/dist/create-file.d.ts.map +1 -0
  31. package/dist/create-file.js +190 -0
  32. package/dist/create-owned-file.d.ts +8 -0
  33. package/dist/create-owned-file.d.ts.map +1 -0
  34. package/dist/create-owned-file.js +16 -0
  35. package/dist/create.d.ts +4 -0
  36. package/dist/create.d.ts.map +1 -0
  37. package/dist/create.js +2 -0
  38. package/dist/creation-darwin.d.ts +7 -0
  39. package/dist/creation-darwin.d.ts.map +1 -0
  40. package/dist/creation-darwin.js +79 -0
  41. package/dist/creation-file-state.d.ts +19 -0
  42. package/dist/creation-file-state.d.ts.map +1 -0
  43. package/dist/creation-file-state.js +118 -0
  44. package/dist/creation-path.d.ts +21 -0
  45. package/dist/creation-path.d.ts.map +1 -0
  46. package/dist/creation-path.js +71 -0
  47. package/dist/creation-permissions.d.ts +19 -0
  48. package/dist/creation-permissions.d.ts.map +1 -0
  49. package/dist/creation-permissions.js +125 -0
  50. package/dist/directory-durability.d.ts +1 -1
  51. package/dist/directory-durability.d.ts.map +1 -1
  52. package/dist/directory-durability.js +22 -80
  53. package/dist/directory-guard.d.ts +3 -0
  54. package/dist/directory-guard.d.ts.map +1 -1
  55. package/dist/directory-mode-node.d.ts +2 -0
  56. package/dist/directory-mode-node.d.ts.map +1 -1
  57. package/dist/directory-mode-node.js +8 -0
  58. package/dist/directory-receipt.d.ts +24 -0
  59. package/dist/directory-receipt.d.ts.map +1 -0
  60. package/dist/directory-receipt.js +127 -0
  61. package/dist/file-cleanup.d.ts +19 -0
  62. package/dist/file-cleanup.d.ts.map +1 -0
  63. package/dist/file-cleanup.js +78 -0
  64. package/dist/file-observation.d.ts +1 -1
  65. package/dist/file-observation.d.ts.map +1 -1
  66. package/dist/file-store-boundary.d.ts +2 -6
  67. package/dist/file-store-boundary.d.ts.map +1 -1
  68. package/dist/file-store-boundary.js +3 -9
  69. package/dist/file-store-sync-write.d.ts.map +1 -1
  70. package/dist/file-store-sync-write.js +2 -5
  71. package/dist/guarded-mkdir.d.ts +1 -0
  72. package/dist/guarded-mkdir.d.ts.map +1 -1
  73. package/dist/guarded-mkdir.js +36 -7
  74. package/dist/move-path.js +1 -1
  75. package/dist/native-binding.d.ts +11 -1
  76. package/dist/native-binding.d.ts.map +1 -1
  77. package/dist/native-fallback-warning.d.ts +4 -0
  78. package/dist/native-fallback-warning.d.ts.map +1 -0
  79. package/dist/native-fallback-warning.js +11 -0
  80. package/dist/native-operations.d.ts +0 -2
  81. package/dist/native-operations.d.ts.map +1 -1
  82. package/dist/native-operations.js +0 -24
  83. package/dist/native-parent-admission.d.ts +2 -0
  84. package/dist/native-parent-admission.d.ts.map +1 -1
  85. package/dist/native-parent-admission.js +3 -2
  86. package/dist/native-pinned-write-windows.d.ts +1 -1
  87. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  88. package/dist/native-pinned-write-windows.js +173 -28
  89. package/dist/native-pinned-write.d.ts.map +1 -1
  90. package/dist/native-pinned-write.js +19 -3
  91. package/dist/native-policy-parent-windows.d.ts.map +1 -1
  92. package/dist/native-policy-parent-windows.js +15 -6
  93. package/dist/native-staged-file.d.ts +3 -2
  94. package/dist/native-staged-file.d.ts.map +1 -1
  95. package/dist/native-staged-file.js +86 -39
  96. package/dist/owner-dacl.d.ts.map +1 -1
  97. package/dist/owner-dacl.js +10 -4
  98. package/dist/pinned-write-input.d.ts +4 -0
  99. package/dist/pinned-write-input.d.ts.map +1 -0
  100. package/dist/pinned-write-input.js +25 -0
  101. package/dist/pinned-write-mode.d.ts +5 -0
  102. package/dist/pinned-write-mode.d.ts.map +1 -0
  103. package/dist/pinned-write-mode.js +24 -0
  104. package/dist/pinned-write-staged.d.ts +6 -0
  105. package/dist/pinned-write-staged.d.ts.map +1 -0
  106. package/dist/pinned-write-staged.js +187 -0
  107. package/dist/pinned-write-types.d.ts +3 -0
  108. package/dist/pinned-write-types.d.ts.map +1 -1
  109. package/dist/pinned-write.d.ts.map +1 -1
  110. package/dist/pinned-write.js +35 -145
  111. package/dist/private-directory.d.ts.map +1 -1
  112. package/dist/private-directory.js +18 -4
  113. package/dist/private-producer-handoff-sync.d.ts +14 -0
  114. package/dist/private-producer-handoff-sync.d.ts.map +1 -0
  115. package/dist/private-producer-handoff-sync.js +114 -0
  116. package/dist/private-producer-handoff.d.ts +22 -4
  117. package/dist/private-producer-handoff.d.ts.map +1 -1
  118. package/dist/private-producer-handoff.js +140 -77
  119. package/dist/publish-copy-stage.d.ts +2 -1
  120. package/dist/publish-copy-stage.d.ts.map +1 -1
  121. package/dist/publish-copy-stage.js +16 -7
  122. package/dist/publish-file.d.ts.map +1 -1
  123. package/dist/publish-file.js +2 -2
  124. package/dist/replace-file-temp-owner.d.ts +0 -7
  125. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  126. package/dist/replace-file-temp-owner.js +3 -54
  127. package/dist/root-create-input.d.ts +2 -1
  128. package/dist/root-create-input.d.ts.map +1 -1
  129. package/dist/root-create-input.js +13 -4
  130. package/dist/root-directory-creation.d.ts +3 -3
  131. package/dist/root-directory-creation.d.ts.map +1 -1
  132. package/dist/root-directory-creation.js +15 -3
  133. package/dist/root-impl.d.ts.map +1 -1
  134. package/dist/root-impl.js +28 -7
  135. package/dist/root-move-noreplace.d.ts.map +1 -1
  136. package/dist/root-move-noreplace.js +22 -13
  137. package/dist/root-options.d.ts +12 -4
  138. package/dist/root-options.d.ts.map +1 -1
  139. package/dist/root-path-stat.d.ts.map +1 -1
  140. package/dist/root-path-stat.js +59 -7
  141. package/dist/root-write-publication.js +1 -1
  142. package/dist/secret-file.d.ts.map +1 -1
  143. package/dist/secret-file.js +1 -0
  144. package/dist/secure-file-windows.d.ts +6 -0
  145. package/dist/secure-file-windows.d.ts.map +1 -1
  146. package/dist/secure-file-windows.js +34 -117
  147. package/dist/secure-file.js +2 -2
  148. package/dist/sidecar-lock-root.d.ts.map +1 -1
  149. package/dist/sidecar-lock-root.js +2 -1
  150. package/dist/staged-directory.d.ts.map +1 -1
  151. package/dist/staged-directory.js +6 -6
  152. package/dist/staged-file-settlement.d.ts +17 -0
  153. package/dist/staged-file-settlement.d.ts.map +1 -0
  154. package/dist/staged-file-settlement.js +57 -0
  155. package/dist/windows-owner.d.ts.map +1 -1
  156. package/dist/windows-owner.js +2 -1
  157. package/dist/windows-security-bridge.cs +336 -0
  158. package/dist/windows-security-bridge.ps1 +15 -0
  159. package/dist/windows-security-command.d.ts +26 -0
  160. package/dist/windows-security-command.d.ts.map +1 -0
  161. package/dist/windows-security-command.js +363 -0
  162. package/dist/windows-security-facts.d.ts +6 -0
  163. package/dist/windows-security-facts.d.ts.map +1 -0
  164. package/dist/windows-security-facts.js +108 -0
  165. package/docs/advanced.md +3 -1
  166. package/docs/archive.md +61 -37
  167. package/docs/config.md +6 -2
  168. package/docs/contributing.md +44 -4
  169. package/docs/copy.md +2 -0
  170. package/docs/creation.md +128 -0
  171. package/docs/durability.md +24 -0
  172. package/docs/install.md +31 -7
  173. package/docs/migrating-to-0.5.md +15 -6
  174. package/docs/migrating-to-0.6.md +9 -4
  175. package/docs/native-helper.md +22 -9
  176. package/docs/native.md +38 -7
  177. package/docs/permissions.md +37 -14
  178. package/docs/root.md +34 -0
  179. package/docs/secret-file.md +11 -2
  180. package/docs/secure-file.md +9 -4
  181. package/docs/sidecar-lock.md +5 -4
  182. package/docs/staged-file.md +5 -0
  183. package/docs/writing.md +71 -5
  184. package/package.json +18 -15
@@ -1,6 +1,7 @@
1
1
  import { classifyArchiveParserError } from "./archive-parser-errors.js";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { Transform } from "node:stream";
4
+ import { setImmediate } from "node:timers/promises";
4
5
  import { ArchiveFormatError } from "./archive-errors.js";
5
6
  // Node exposes WebAssembly without DOM globals; keep the private ABI types local.
6
7
  const wasm = globalThis.WebAssembly;
@@ -24,17 +25,14 @@ function parserError(message) {
24
25
  return mapped;
25
26
  return new ArchiveFormatError(message.replace(/^archive-header-invalid:/, "invalid TAR header:"));
26
27
  }
27
- /** Backpressure-aware transport only; all TAR semantics live in the Rust core. */
28
- export class TarParserStream extends Transform {
29
- onMember;
28
+ /** One bounded memory domain owns both admission and decompression. Its owner
29
+ * must join the decoder and parser before disposing this shared instance. */
30
+ export class TarWasmSession {
30
31
  abi;
31
- constructor(limits, onMember) {
32
- super();
33
- this.onMember = onMember;
32
+ constructor(limits) {
34
33
  this.abi = instance();
35
34
  if (this.abi.init(limits.maxEntries, limits.maxMetaEntryBytes, limits.maxDecodedBytes, limits.maxManifestBytes, Number(process.platform === "win32")) !== 0) {
36
- this.abi.dispose();
37
- this.abi = undefined;
35
+ this.dispose();
38
36
  throw new RangeError("invalid TAR parser limits");
39
37
  }
40
38
  }
@@ -50,28 +48,133 @@ export class TarParserStream extends Transform {
50
48
  const abi = this.abi;
51
49
  return utf8Decoder.decode(this.bytes(abi.text_ptr(), abi.text_len()));
52
50
  }
53
- _transform(chunk, _encoding, callback) {
54
- try {
55
- const abi = this.abi;
56
- for (let offset = 0; offset < chunk.length; offset += 65536) {
57
- const length = Math.min(65536, chunk.length - offset);
58
- this.bytes(abi.input_ptr(), length).set(chunk.subarray(offset, offset + length));
59
- // A member event can stop before the end of this already-copied inbox.
60
- for (let consumed = 0; consumed < length;) {
61
- const used = abi.push(consumed, length - consumed);
62
- if (used < 0)
63
- throw parserError(this.text());
64
- if (used === 0 || used > length - consumed)
65
- throw new ArchiveFormatError("TAR WASM made no progress");
66
- consumed += used;
67
- const type = abi.member_type();
68
- if (type >= 0)
69
- this.onMember?.({
70
- path: this.text(), type: types.get(type) ?? "Unsupported", size: abi.member_size(),
71
- mode: abi.member_mode(), offset: abi.member_offset(),
72
- });
51
+ parse(chunk, onMember) {
52
+ const abi = this.abi;
53
+ for (let offset = 0; offset < chunk.length; offset += 65536) {
54
+ const length = Math.min(65536, chunk.length - offset);
55
+ this.bytes(abi.input_ptr(), length).set(chunk.subarray(offset, offset + length));
56
+ // A member event can stop before the end of this already-copied inbox.
57
+ for (let consumed = 0; consumed < length;) {
58
+ const used = abi.push(consumed, length - consumed);
59
+ if (used < 0)
60
+ throw parserError(this.text());
61
+ if (used === 0 || used > length - consumed)
62
+ throw new ArchiveFormatError("TAR WASM made no progress");
63
+ consumed += used;
64
+ const type = abi.member_type();
65
+ if (type >= 0)
66
+ onMember?.({
67
+ path: this.text(), type: types.get(type) ?? "Unsupported", size: abi.member_size(),
68
+ mode: abi.member_mode(), offset: abi.member_offset(),
69
+ });
70
+ }
71
+ }
72
+ }
73
+ finish() {
74
+ if (this.abi.finish() !== 0)
75
+ throw parserError(this.text());
76
+ }
77
+ codecError() {
78
+ const abi = this.abi;
79
+ return new ArchiveFormatError(utf8Decoder.decode(this.bytes(abi.codec_error_ptr(), abi.codec_error_len())));
80
+ }
81
+ async *decode(source, kind, signal) {
82
+ const abi = this.abi;
83
+ signal?.throwIfAborted();
84
+ if (abi.codec_init(kind === "tar-bzip2" ? 1 : 2) !== 0)
85
+ throw this.codecError();
86
+ let work = 0;
87
+ let calls = 0;
88
+ let unconsumedBoundary = false;
89
+ const step = (input) => {
90
+ signal?.throwIfAborted();
91
+ this.bytes(abi.codec_input_ptr(), input.length).set(input);
92
+ const result = abi.codec_push(input.length);
93
+ if (result < 0)
94
+ throw this.codecError();
95
+ const consumed = abi.codec_consumed(), produced = abi.codec_produced();
96
+ if (!Number.isInteger(consumed) || consumed < 0 || consumed > input.length ||
97
+ !Number.isInteger(produced) || produced < 0 || produced > 65536 ||
98
+ (result !== 0 && result !== 1) ||
99
+ (input.length > 0 && consumed === 0 && produced === 0 && (result !== 1 || unconsumedBoundary))) {
100
+ throw new ArchiveFormatError("compressed TAR WASM made no progress");
101
+ }
102
+ if (input.length > 0)
103
+ unconsumedBoundary = consumed === 0 && produced === 0;
104
+ work += consumed + produced;
105
+ calls++;
106
+ // The parser can grow this same memory, and the next codec call reuses it.
107
+ return { consumed, output: produced ? Buffer.from(this.bytes(abi.codec_output_ptr(), produced)) : undefined };
108
+ };
109
+ const empty = Buffer.alloc(0);
110
+ // Yield even for output-free skippable frames and arbitrarily small members.
111
+ const yieldIfNeeded = async () => {
112
+ if (work >= 256 * 1024 || calls >= 32) {
113
+ work = 0;
114
+ calls = 0;
115
+ await setImmediate(undefined, { signal });
116
+ }
117
+ signal?.throwIfAborted();
118
+ };
119
+ for await (const chunk of source) {
120
+ for (let offset = 0; offset < chunk.length;) {
121
+ const { consumed, output } = step(chunk.subarray(offset, offset + 65536));
122
+ offset += consumed;
123
+ if (output)
124
+ yield output;
125
+ await yieldIfNeeded();
126
+ // A full output window can leave more decoded bytes with no new input.
127
+ if (output) {
128
+ for (;;) {
129
+ const drained = step(empty);
130
+ if (drained.output)
131
+ yield drained.output;
132
+ await yieldIfNeeded();
133
+ if (!drained.output)
134
+ break;
135
+ }
73
136
  }
74
137
  }
138
+ }
139
+ // Physical EOF, rather than a selected range or TAR EOF, ends the decoder.
140
+ for (;;) {
141
+ const drained = step(empty);
142
+ if (drained.output)
143
+ yield drained.output;
144
+ await yieldIfNeeded();
145
+ if (!drained.output)
146
+ break;
147
+ }
148
+ if (abi.codec_finish() !== 0)
149
+ throw this.codecError();
150
+ }
151
+ dispose() {
152
+ const abi = this.abi;
153
+ this.abi = undefined;
154
+ if (abi) {
155
+ try {
156
+ abi.codec_dispose();
157
+ }
158
+ finally {
159
+ abi.dispose();
160
+ }
161
+ }
162
+ }
163
+ }
164
+ /** Backpressure-aware transport only; all TAR semantics live in the Rust core. */
165
+ export class TarParserStream extends Transform {
166
+ onMember;
167
+ session;
168
+ ownsSession;
169
+ constructor(limits, onMember, session) {
170
+ super();
171
+ this.onMember = onMember;
172
+ this.session = session ?? new TarWasmSession(limits);
173
+ this.ownsSession = session === undefined;
174
+ }
175
+ _transform(chunk, _encoding, callback) {
176
+ try {
177
+ this.session.parse(chunk, this.onMember);
75
178
  callback(null, chunk);
76
179
  }
77
180
  catch (error) {
@@ -80,8 +183,7 @@ export class TarParserStream extends Transform {
80
183
  }
81
184
  _flush(callback) {
82
185
  try {
83
- if (this.abi.finish() !== 0)
84
- throw parserError(this.text());
186
+ this.session.finish();
85
187
  callback();
86
188
  }
87
189
  catch (error) {
@@ -90,14 +192,12 @@ export class TarParserStream extends Transform {
90
192
  }
91
193
  _destroy(error, callback) {
92
194
  try {
93
- this.abi?.dispose();
195
+ if (this.ownsSession)
196
+ this.session.dispose();
94
197
  }
95
198
  catch (cause) {
96
199
  error ??= cause instanceof Error ? cause : new Error(String(cause));
97
200
  }
98
- finally {
99
- this.abi = undefined;
100
- }
101
201
  callback(error);
102
202
  }
103
203
  }
@@ -1 +1 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDjF"}
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDjF"}
package/dist/archive.js CHANGED
@@ -9,7 +9,7 @@ import { pipeline } from "node:stream/promises";
9
9
  import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
10
10
  import { createPipelineTimeoutError, waitForDeadline, withExtractionDeadline, } from "./archive-deadline.js";
11
11
  import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, createExtractBudgetTransform, resolveExtractLimits, resolveTarMeterLimits, } from "./archive-limits.js";
12
- import { assertPortableArchiveKind, resolveArchiveKind } from "./archive-kind.js";
12
+ import { resolveArchiveKind } from "./archive-kind.js";
13
13
  import { prepareArchiveDestinationGuard, preparePrivateArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
14
14
  import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
15
15
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
@@ -230,8 +230,7 @@ export async function extractArchive(params) {
230
230
  await withExtractionDeadline(params.timeoutMs, label, async (deadline) => extractNativeArchive({ ...options, binding: native, kind, tarLimits, deadline }));
231
231
  return;
232
232
  }
233
- assertPortableArchiveKind(kind);
234
- if (kind === "tar") {
233
+ if (kind !== "zip") {
235
234
  await withExtractionDeadline(params.timeoutMs, label, async (deadline) => {
236
235
  const stagedArchive = await stageArchiveFileForExtraction({
237
236
  archivePath: options.archivePath,
@@ -239,7 +238,7 @@ export async function extractArchive(params) {
239
238
  deadline,
240
239
  });
241
240
  try {
242
- await extractWasmTar({ archivePath: stagedArchive.path, options, limits, tarLimits, deadline });
241
+ await extractWasmTar({ archivePath: stagedArchive.path, kind, options, limits, tarLimits, deadline });
243
242
  }
244
243
  finally {
245
244
  await stagedArchive.cleanup();
@@ -252,7 +251,7 @@ export async function extractArchive(params) {
252
251
  async function extractWasmTar(params) {
253
252
  const { options, deadline, tarLimits } = params;
254
253
  const manifest = [];
255
- await inspectTar({ archivePath: params.archivePath, limits: tarLimits, signal: deadline.signal,
254
+ await inspectTar({ archivePath: params.archivePath, kind: params.kind, limits: tarLimits, signal: deadline.signal,
256
255
  onMember: (entry) => { manifest.push(entry); } });
257
256
  deadline.check();
258
257
  const destinationGuard = await prepareArchiveDestinationGuard(options.destDir);
@@ -265,7 +264,7 @@ async function extractWasmTar(params) {
265
264
  const planned = planEntry(entry);
266
265
  return planned ? [{ ...entry, ...planned }] : [];
267
266
  });
268
- await replayTar({ archivePath: params.archivePath, limits: tarLimits, signal: deadline.signal, members: accepted,
267
+ await replayTar({ archivePath: params.archivePath, kind: params.kind, limits: tarLimits, signal: deadline.signal, members: accepted,
269
268
  async consume(member, payload) {
270
269
  deadline.check();
271
270
  await preparePrivateArchiveOutputPath({ destinationDir: stagingDir, destinationRealDir: stagingDir,
@@ -13,6 +13,7 @@ export type CloneFileMetadata = {
13
13
  cloneId: bigint;
14
14
  };
15
15
  /** Batched APFS metadata snapshots; absent on unsupported files or filesystems.
16
+ * On macOS, these snapshots require the native helper.
16
17
  * These are point-in-time facts, not authorization or a guarantee against later edits.
17
18
  */
18
19
  export declare function readCloneFileMetadata(files: readonly string[]): Promise<(CloneFileMetadata | undefined)[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"clone-metadata.d.ts","sourceRoot":"","sources":["../src/clone-metadata.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,OAAO,CAAC,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAAC,CA4B5C"}
1
+ {"version":3,"file":"clone-metadata.d.ts","sourceRoot":"","sources":["../src/clone-metadata.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,OAAO,CAAC,CAAC,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAAC,CA8B5C"}
@@ -1,11 +1,15 @@
1
1
  import { assertAbsolutePathInput } from "./absolute-path.js";
2
- import { requireNativeBinding } from "./native.js";
2
+ import { getNativeBinding, requireNativeBinding } from "./native.js";
3
3
  /** Batched APFS metadata snapshots; absent on unsupported files or filesystems.
4
+ * On macOS, these snapshots require the native helper.
4
5
  * These are point-in-time facts, not authorization or a guarantee against later edits.
5
6
  */
6
7
  export async function readCloneFileMetadata(files) {
7
8
  const paths = files.map(assertAbsolutePathInput);
8
- const results = await requireNativeBinding().readCloneFileMetadata(paths);
9
+ const native = process.platform === "darwin" ? requireNativeBinding() : getNativeBinding();
10
+ if (!native)
11
+ return paths.map(() => undefined);
12
+ const results = await native.readCloneFileMetadata(paths);
9
13
  return results.map((result) => {
10
14
  if (!result ||
11
15
  result.length !== 100 ||
@@ -0,0 +1,20 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ import { type CreationPermissions } from "./creation-permissions.js";
3
+ import { type CreationParentIdentity } from "./creation-path.js";
4
+ export type CreateDirectoryOptions = CreationPermissions & {
5
+ assertBeforeMutation?: () => void;
6
+ };
7
+ export type CreationAdmission = {
8
+ expectedParentIdentity?: CreationParentIdentity;
9
+ };
10
+ export type CreationDirectoryReceipt = {
11
+ stat: BigIntStats;
12
+ windowsIdentity?: string;
13
+ };
14
+ export declare function createDirectory(targetPath: string, options?: CreateDirectoryOptions): Promise<void>;
15
+ export declare function createDirectoryWithAdmission(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): Promise<void>;
16
+ export declare function createDirectoryWithReceipt(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): Promise<CreationDirectoryReceipt>;
17
+ export declare function createDirectorySync(targetPath: string, options?: CreateDirectoryOptions): void;
18
+ export declare function createDirectorySyncWithAdmission(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): void;
19
+ export declare function createDirectoryWithReceiptSync(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): CreationDirectoryReceipt;
20
+ //# sourceMappingURL=create-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-directory.d.ts","sourceRoot":"","sources":["../src/create-directory.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAMnD,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAA0C,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AASzG,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,GAAG;IAAE,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC;AACjG,MAAM,MAAM,iBAAiB,GAAG;IAAE,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;CAAE,CAAC;AACpF,MAAM,MAAM,wBAAwB,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAwBvF,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,EACpC,SAAS,GAAE,iBAAsB,GAChC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,EACpC,SAAS,GAAE,iBAAsB,GAChC,OAAO,CAAC,wBAAwB,CAAC,CAmCnC;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAEN;AAED,wBAAgB,gCAAgC,CAC9C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,EACpC,SAAS,GAAE,iBAAsB,GAChC,IAAI,CAEN;AAED,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,EACpC,SAAS,GAAE,iBAAsB,GAChC,wBAAwB,CAmC1B"}
@@ -0,0 +1,130 @@
1
+ import fsSync, {} from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { getNativeBinding } from "./native.js";
5
+ import { getFsSafeNativeConfig } from "./native-config.js";
6
+ import { assertSynchronousCallbackResult } from "./mutation-authority.js";
7
+ import { assertPrivateDirectory, assertPrivateDirectorySync, inspectCreationDirectory, inspectCreationDirectorySync, resolveCreationPermissions, } from "./creation-permissions.js";
8
+ import { creationCollision, prepareCreationPath } from "./creation-path.js";
9
+ import { createPrivateWindowsDirectoryCommand, createPrivateWindowsDirectoryCommandSync, } from "./windows-security-command.js";
10
+ import { warnNativeFallback } from "./native-fallback-warning.js";
11
+ import { inspectDirectoryIdentitySync } from "./directory-guard.js";
12
+ import { assertDarwinCreationDirectoryAcl, assertDarwinPrivateDirectoryMode } from "./creation-darwin.js";
13
+ function directoryAdoptionFailure(error, target, windowsIdentity) {
14
+ return new FsSafeError("helper-failed", "created directory admission failed", {
15
+ cause: error,
16
+ details: {
17
+ publication: { status: "published" }, path: target, cleanup: "preserved",
18
+ ...(windowsIdentity === undefined ? {} : { windowsIdentity }),
19
+ },
20
+ });
21
+ }
22
+ function privateDirectoryBackend() {
23
+ const binding = getNativeBinding();
24
+ if (typeof binding?.createPrivateDirectoryWithParentIdentity === "function") {
25
+ return binding.createPrivateDirectoryWithParentIdentity.bind(binding);
26
+ }
27
+ if (getFsSafeNativeConfig().mode === "require") {
28
+ throw new FsSafeError("helper-unavailable", "private directory creation requires an up-to-date native parent identity capability");
29
+ }
30
+ warnNativeFallback("windows-private-directory", "Private Windows directory creation uses a slower built-in system command.");
31
+ return undefined;
32
+ }
33
+ export async function createDirectory(targetPath, options = {}) {
34
+ await createDirectoryWithAdmission(targetPath, options, {});
35
+ }
36
+ export async function createDirectoryWithAdmission(targetPath, options = {}, admission = {}) {
37
+ await createDirectoryWithReceipt(targetPath, options, admission);
38
+ }
39
+ export async function createDirectoryWithReceipt(targetPath, options = {}, admission = {}) {
40
+ const permissions = resolveCreationPermissions(options, true);
41
+ const assertion = options.assertBeforeMutation;
42
+ const selected = prepareCreationPath(targetPath, admission.expectedParentIdentity);
43
+ const backend = permissions.private && process.platform === "win32" ? privateDirectoryBackend() : undefined;
44
+ const parentIdentity = permissions.private && process.platform === "win32"
45
+ ? await inspectCreationDirectory(selected.parent.dir, false) : undefined;
46
+ if (permissions.private)
47
+ assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, "directory");
48
+ assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
49
+ selected.assertParent();
50
+ if (permissions.private)
51
+ assertDarwinPrivateDirectoryMode(permissions.mode);
52
+ if (permissions.private)
53
+ assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, "directory");
54
+ let created = false;
55
+ let windowsIdentity;
56
+ try {
57
+ if (parentIdentity !== undefined) {
58
+ const receipt = backend
59
+ ? backend(selected.target, parentIdentity)
60
+ : await createPrivateWindowsDirectoryCommand(selected.target, parentIdentity);
61
+ created = true;
62
+ windowsIdentity = receipt.identity;
63
+ if ((await inspectCreationDirectory(selected.target, true)) !== receipt.identity) {
64
+ throw new FsSafeError("path-mismatch", "created private directory changed before adoption");
65
+ }
66
+ }
67
+ else {
68
+ await fs.mkdir(selected.target, { mode: permissions.mode });
69
+ created = true;
70
+ if (permissions.private)
71
+ await assertPrivateDirectory(selected.target);
72
+ }
73
+ selected.assertParent();
74
+ return { stat: inspectDirectoryIdentitySync(selected.target), windowsIdentity };
75
+ }
76
+ catch (error) {
77
+ if (created)
78
+ throw directoryAdoptionFailure(error, selected.target, windowsIdentity);
79
+ throw creationCollision(error);
80
+ }
81
+ }
82
+ export function createDirectorySync(targetPath, options = {}) {
83
+ createDirectoryWithReceiptSync(targetPath, options, {});
84
+ }
85
+ export function createDirectorySyncWithAdmission(targetPath, options = {}, admission = {}) {
86
+ createDirectoryWithReceiptSync(targetPath, options, admission);
87
+ }
88
+ export function createDirectoryWithReceiptSync(targetPath, options = {}, admission = {}) {
89
+ const permissions = resolveCreationPermissions(options, true);
90
+ const assertion = options.assertBeforeMutation;
91
+ const selected = prepareCreationPath(targetPath, admission.expectedParentIdentity);
92
+ const backend = permissions.private && process.platform === "win32" ? privateDirectoryBackend() : undefined;
93
+ const parentIdentity = permissions.private && process.platform === "win32"
94
+ ? inspectCreationDirectorySync(selected.parent.dir, false) : undefined;
95
+ if (permissions.private)
96
+ assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, "directory");
97
+ assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
98
+ selected.assertParent();
99
+ if (permissions.private)
100
+ assertDarwinPrivateDirectoryMode(permissions.mode);
101
+ if (permissions.private)
102
+ assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, "directory");
103
+ let created = false;
104
+ let windowsIdentity;
105
+ try {
106
+ if (parentIdentity !== undefined) {
107
+ const receipt = backend
108
+ ? backend(selected.target, parentIdentity)
109
+ : createPrivateWindowsDirectoryCommandSync(selected.target, parentIdentity);
110
+ created = true;
111
+ windowsIdentity = receipt.identity;
112
+ if (inspectCreationDirectorySync(selected.target, true) !== receipt.identity) {
113
+ throw new FsSafeError("path-mismatch", "created private directory changed before adoption");
114
+ }
115
+ }
116
+ else {
117
+ fsSync.mkdirSync(selected.target, { mode: permissions.mode });
118
+ created = true;
119
+ if (permissions.private)
120
+ assertPrivateDirectorySync(selected.target);
121
+ }
122
+ selected.assertParent();
123
+ return { stat: inspectDirectoryIdentitySync(selected.target), windowsIdentity };
124
+ }
125
+ catch (error) {
126
+ if (created)
127
+ throw directoryAdoptionFailure(error, selected.target, windowsIdentity);
128
+ throw creationCollision(error);
129
+ }
130
+ }
@@ -0,0 +1,7 @@
1
+ import { type FileHandle } from "node:fs/promises";
2
+ import { type CreationPath } from "./creation-path.js";
3
+ export declare function createPrivateWindowsFileHandle(selected: CreationPath, options: {
4
+ mode: number;
5
+ assertBeforeMutation?: () => void;
6
+ }): Promise<FileHandle>;
7
+ //# sourceMappingURL=create-file-async.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-file-async.d.ts","sourceRoot":"","sources":["../src/create-file-async.ts"],"names":[],"mappings":"AAEA,OAAW,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAevD,OAAO,EAAmD,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKxG,wBAAsB,8BAA8B,CAClD,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,GAC3D,OAAO,CAAC,UAAU,CAAC,CA0FrB"}
@@ -0,0 +1,121 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import fsSync, {} from "node:fs";
3
+ import fs, {} from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { createDirectoryWithReceipt } from "./create-directory.js";
6
+ import { assertDirectoryIdentitySync } from "./directory-guard.js";
7
+ import { FsSafeError } from "./errors.js";
8
+ import { assertSynchronousCallbackResult } from "./mutation-authority.js";
9
+ import { assertCreationFile, creationPublicationAfterFailure, privateFileSettlementFailure, rethrowPrivateStageCreationFailure, removeRecordedCreationFile, } from "./creation-file-state.js";
10
+ import { inspectCreationDirectory, protectCreatedFile, verifyCreatedFile } from "./creation-permissions.js";
11
+ import { creationCollision, removeCreationDirectoryAsync } from "./creation-path.js";
12
+ import { handoffCreatedFile } from "./private-producer-handoff.js";
13
+ import { resolveReadOpenFlags } from "./read-open-flags.js";
14
+ import { hasUnsettledWindowsSecurityCommand } from "./windows-security-command.js";
15
+ export async function createPrivateWindowsFileHandle(selected, options) {
16
+ const assertion = options.assertBeforeMutation;
17
+ const existing = await fs.lstat(selected.target).catch((error) => {
18
+ if (error.code !== "ENOENT")
19
+ throw error;
20
+ return undefined;
21
+ });
22
+ selected.assertParent();
23
+ if (existing)
24
+ throw new FsSafeError("already-exists", "creation target already exists");
25
+ const parentIdentity = await inspectCreationDirectory(selected.parent.dir, false);
26
+ selected.assertParent();
27
+ const stageDirectory = path.join(selected.parent.dir, `.fs-safe-create-${randomUUID()}`);
28
+ let stage;
29
+ try {
30
+ stage = await createDirectoryWithReceipt(stageDirectory, { private: true, assertBeforeMutation: assertion }, {
31
+ expectedParentIdentity: selected.parent.stat,
32
+ });
33
+ }
34
+ catch (error) {
35
+ rethrowPrivateStageCreationFailure(error, selected.target, stageDirectory);
36
+ }
37
+ const stageIdentity = stage.windowsIdentity;
38
+ const stagePath = path.join(stageDirectory, "file");
39
+ const assertStage = () => {
40
+ selected.assertParent();
41
+ assertDirectoryIdentitySync(stageDirectory, stage.stat);
42
+ };
43
+ let file;
44
+ let identity;
45
+ let publication = "not-published";
46
+ try {
47
+ if (await inspectCreationDirectory(stageDirectory, true) !== stageIdentity) {
48
+ throw new FsSafeError("path-mismatch", "private staging directory changed before file creation");
49
+ }
50
+ assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
51
+ assertStage();
52
+ file = await fs.open(stagePath, fsSync.constants.O_RDWR | fsSync.constants.O_CREAT |
53
+ fsSync.constants.O_EXCL | resolveReadOpenFlags(), 0o600);
54
+ assertStage();
55
+ identity = assertCreationFile(file.fd, stagePath);
56
+ assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
57
+ assertStage();
58
+ assertCreationFile(file.fd, stagePath, identity);
59
+ const windowsIdentity = await protectCreatedFile(file.fd, stagePath, stageIdentity);
60
+ assertStage();
61
+ assertCreationFile(file.fd, stagePath, identity);
62
+ const source = file;
63
+ file = undefined;
64
+ // The handoff consumes the source pin on both success and failure.
65
+ file = await handoffCreatedFile({
66
+ source, sourcePath: stagePath, targetPath: selected.target, identity,
67
+ assertSourceParent: assertStage, assertTargetParent: selected.assertParent,
68
+ assertBeforeMutation: assertion,
69
+ verifyDescriptor: (fd, pathname, links) => verifyCreatedFile(fd, pathname, windowsIdentity, pathname === stagePath ? stageIdentity : parentIdentity, links),
70
+ onPublished: () => { publication = "published"; },
71
+ });
72
+ assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
73
+ selected.assertParent();
74
+ assertCreationFile(file.fd, selected.target, identity);
75
+ await verifyCreatedFile(file.fd, selected.target, windowsIdentity, parentIdentity);
76
+ selected.assertParent();
77
+ assertCreationFile(file.fd, selected.target, identity);
78
+ await file.chmod(options.mode);
79
+ await verifyCreatedFile(file.fd, selected.target, windowsIdentity, parentIdentity);
80
+ selected.assertParent();
81
+ assertCreationFile(file.fd, selected.target, identity);
82
+ await removeCreationDirectoryAsync(stageDirectory, stage.stat, selected.assertParent);
83
+ selected.assertParent();
84
+ assertCreationFile(file.fd, selected.target, identity);
85
+ return file;
86
+ }
87
+ catch (primary) {
88
+ publication = creationPublicationAfterFailure(primary, publication);
89
+ const preserved = publication === "indeterminate" || hasUnsettledWindowsSecurityCommand(primary);
90
+ const cleanup = [];
91
+ if (file) {
92
+ const owned = file;
93
+ file = undefined;
94
+ try {
95
+ await owned.close();
96
+ }
97
+ catch (error) {
98
+ cleanup.push(error);
99
+ }
100
+ }
101
+ if (!preserved) {
102
+ if (identity) {
103
+ try {
104
+ await removeRecordedCreationFile(stagePath, identity, assertStage);
105
+ }
106
+ catch (error) {
107
+ cleanup.push(error);
108
+ }
109
+ }
110
+ try {
111
+ await removeCreationDirectoryAsync(stageDirectory, stage.stat, selected.assertParent);
112
+ }
113
+ catch (error) {
114
+ cleanup.push(error);
115
+ }
116
+ }
117
+ if (publication === "not-published" && cleanup.length === 0 && !preserved)
118
+ throw creationCollision(primary);
119
+ throw privateFileSettlementFailure({ primary, cleanup, publication, path: selected.target, stageDirectory, preserved });
120
+ }
121
+ }
@@ -0,0 +1,8 @@
1
+ import { type FileHandle } from "node:fs/promises";
2
+ import { type CreateDirectoryOptions, type CreationAdmission } from "./create-directory.js";
3
+ import { type OwnedFileDescriptorSync } from "./create-owned-file.js";
4
+ export type CreateFileOptions = CreateDirectoryOptions;
5
+ export declare function createFileSync(targetPath: string, options?: CreateFileOptions): OwnedFileDescriptorSync;
6
+ /** Internal async writer; Windows preparation uses the nonblocking stage owner. */
7
+ export declare function createFileHandle(targetPath: string, options?: CreateFileOptions, admission?: CreationAdmission): Promise<FileHandle>;
8
+ //# sourceMappingURL=create-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-file.d.ts","sourceRoot":"","sources":["../src/create-file.ts"],"names":[],"mappings":"AAEA,OAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAkC,KAAK,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC5H,OAAO,EAAyB,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AA2B7F,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AA8GvD,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,uBAAuB,CAE3G;AAED,mFAAmF;AACnF,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,EAC/B,SAAS,GAAE,iBAAsB,GAChC,OAAO,CAAC,UAAU,CAAC,CA+BrB"}