@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
@@ -50,19 +50,22 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
+ import { assertDarwinCreationAcl, privateFileMutationAssertion } from "./creation-darwin.js";
54
+ import { assertPrivateCreationFile } from "./creation-file-state.js";
53
55
  import { requireNativeBinding } from "./native.js";
54
56
  import { syncFileBestEffortSync } from "./file-sync.js";
55
57
  import { randomUUID } from "node:crypto";
56
58
  import fs from "node:fs";
57
59
  import path from "node:path";
58
60
  import { FsSafeError } from "./errors.js";
59
- import { MutationAuthorityError } from "./mutation-authority.js";
61
+ import { assertSynchronousCallbackResult, MutationAuthorityError } from "./mutation-authority.js";
60
62
  import { captureNativeFdClose } from "./native-binding.js";
61
- import { writeNativeInput } from "./native-operations.js";
63
+ import { writePinnedInput } from "./pinned-write-input.js";
62
64
  import { assertNativeCopyCompleted, createNativeCopyFile } from "./copy-file-input.js";
63
65
  import { assertStagedDirectoryCurrent, openStagedDirectory } from "./staged-directory.js";
64
66
  import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
65
67
  import { classifyNativeRenameFailure } from "./native-rename-outcome.js";
68
+ import { createStagedFileReceipt, stagedFileFailure as failure } from "./staged-file-settlement.js";
66
69
  export function assertNativeStaging(binding) {
67
70
  if ([
68
71
  binding.closeOwnedFd,
@@ -82,12 +85,6 @@ function assertBasename(name, portable) {
82
85
  }
83
86
  }
84
87
  const NOT_PUBLISHED = Object.freeze({ status: "not-published" });
85
- function failure(error, details) {
86
- const code = error instanceof FsSafeError
87
- ? error.code
88
- : error?.code === "EEXIST" ? "already-exists" : "helper-failed";
89
- return new FsSafeError(code, `staged file ${details.phase} failed`, { cause: error, details });
90
- }
91
88
  class NativeStagedFile {
92
89
  #binding;
93
90
  #closeFd;
@@ -97,12 +94,15 @@ class NativeStagedFile {
97
94
  #portableNames;
98
95
  #publishedMode;
99
96
  #sync;
97
+ #strictFileSync;
98
+ #private;
99
+ #verifyMode;
100
100
  #assertBeforeMutation;
101
101
  #name;
102
102
  #state = { status: "open", publication: NOT_PUBLISHED };
103
103
  #receipt;
104
104
  #rejectFinalSymlink = false;
105
- constructor(binding, parentFd, closeParentFd, directory, portableNames, publishedMode, sync, assertBeforeMutation, name = `.fs-safe-${randomUUID()}.tmp`) {
105
+ constructor(binding, parentFd, closeParentFd, directory, portableNames, publishedMode, sync, assertBeforeMutation, name = `.fs-safe-${randomUUID()}.tmp`, strictFileSync = false, permissionPolicy) {
106
106
  assertBasename(name, portableNames);
107
107
  this.#name = name;
108
108
  this.#closeFd = captureNativeFdClose(binding);
@@ -113,16 +113,19 @@ class NativeStagedFile {
113
113
  this.#portableNames = portableNames;
114
114
  this.#publishedMode = publishedMode;
115
115
  this.#sync = sync;
116
+ this.#strictFileSync = strictFileSync;
117
+ this.#private = permissionPolicy === "private-creation";
118
+ this.#verifyMode = permissionPolicy !== undefined;
116
119
  this.#assertBeforeMutation = assertBeforeMutation;
117
120
  }
118
121
  // Public staging supplies a Node parent; pinned writes supply a native parent.
119
122
  // The supplied closer owns parentFd even on construction or preparation failure.
120
123
  static async create(binding, parentFd, closeParentFd, directory, input, mode, maxBytes,
121
124
  // Public staging uses portable names; existing POSIX writes accept literal names.
122
- portableNames = true, sync = true, assertBeforeMutation, exclusiveBasename) {
125
+ portableNames = true, sync = true, assertBeforeMutation, exclusiveBasename, strictFileSync = false, permissionPolicy) {
123
126
  let staged;
124
127
  try {
125
- staged = new NativeStagedFile(binding, parentFd, closeParentFd, directory, portableNames, mode, sync, assertBeforeMutation, exclusiveBasename);
128
+ staged = new NativeStagedFile(binding, parentFd, closeParentFd, directory, portableNames, mode, sync, assertBeforeMutation, exclusiveBasename, strictFileSync, permissionPolicy);
126
129
  }
127
130
  catch (error) {
128
131
  try {
@@ -142,19 +145,29 @@ class NativeStagedFile {
142
145
  const exclusive = params.overwrite === false && params.input.kind === "buffer" && params.input.stageBeforePublish === false;
143
146
  // This owner never escapes. Only the internal verifier borrows its fd;
144
147
  // public descriptor methods remain await-free and cannot race disposal.
145
- const staged = __addDisposableResource(env_1, await NativeStagedFile.create(binding, parentFd, closeParentFd, directory, params.input, params.mode, params.maxBytes, false, params.sync, params.assertBeforeMutation, exclusive ? params.basename : undefined), true);
148
+ const staged = __addDisposableResource(env_1, await NativeStagedFile.create(binding, parentFd, closeParentFd, directory, params.input, params.mode, params.maxBytes, false, params.sync, params.assertBeforeMutation, exclusive ? params.basename : undefined, params.strictFileSync, params.private ? "private-creation" : params.verifyPosixMode === true ? "mode-only" : undefined), true);
146
149
  staged.#rejectFinalSymlink = params.rejectFinalSymlink === true;
147
150
  if (params.input.kind === "file")
148
151
  await params.input.verifySource();
149
152
  if (exclusive) {
150
153
  staged.#assertCurrent();
151
154
  params.assertBeforeMutation?.();
155
+ if (staged.#verifyMode)
156
+ staged.#assertCurrent();
152
157
  }
153
158
  const published = exclusive
154
159
  ? staged.#completePublication(params.basename, false, params.onPublished)
155
160
  : await staged.publish(params.basename, { overwrite: params.overwrite !== false }, params.onPublished);
156
161
  const identity = published.staged.identity;
157
- await params.verifyPublished?.(staged.#file(), identity, parentGuard);
162
+ try {
163
+ await params.verifyPublished?.(staged.#file(), identity, parentGuard);
164
+ }
165
+ catch (error) {
166
+ if (params.overwrite === false && params.input.kind !== "file" && params.input.stageBeforePublish === true) {
167
+ throw failure(error, { phase: "publish", publication: published });
168
+ }
169
+ throw error;
170
+ }
158
171
  return { dev: identity.dev, ino: identity.ino };
159
172
  }
160
173
  catch (e_1) {
@@ -186,13 +199,14 @@ class NativeStagedFile {
186
199
  }
187
200
  return state.fileFd;
188
201
  }
189
- #assertNamed(name) {
202
+ #assertNamed(name, expectedMode) {
190
203
  const fd = this.#file();
191
204
  const stat = this.#binding.stagedFileMatches(this.#parentFd, name, fd)
192
205
  ? fs.fstatSync(fd, { bigint: true }) : undefined;
193
206
  if (!stat || stat.nlink !== 1n) {
194
207
  throw new FsSafeError("path-mismatch", "staged entry no longer names the exclusive created file");
195
208
  }
209
+ this.#assertPermissions(fd, stat, expectedMode);
196
210
  return Number(stat.mode & 4095n);
197
211
  }
198
212
  #assertCurrent() {
@@ -200,7 +214,17 @@ class NativeStagedFile {
200
214
  throw new FsSafeError("helper-failed", "staged file publication has already been attempted");
201
215
  }
202
216
  assertStagedDirectoryCurrent(this.#directory);
203
- this.#assertNamed(this.#name);
217
+ this.#assertNamed(this.#name, 0o600);
218
+ }
219
+ #assertPermissions(fd, stat, expectedMode) {
220
+ if (this.#private)
221
+ assertPrivateCreationFile(stat, fd);
222
+ if (this.#verifyMode && Number(stat.mode & 4095n) !== expectedMode) {
223
+ throw new FsSafeError("insecure-permissions", "filesystem did not enforce the staged file mode");
224
+ }
225
+ }
226
+ #assertStagePermissions(fd) {
227
+ this.#assertPermissions(fd, fs.fstatSync(fd, { bigint: true }), 0o600);
204
228
  }
205
229
  async #prepare(input, maxBytes) {
206
230
  try {
@@ -208,44 +232,60 @@ class NativeStagedFile {
208
232
  if (!Number.isInteger(mode) || mode < 0 || mode > 0o7777) {
209
233
  throw new FsSafeError("invalid-path", "invalid staged file mode");
210
234
  }
235
+ const fileSignal = input.kind === "file" && this.#verifyMode ? input.signal : undefined;
236
+ if (input.kind === "file" && this.#verifyMode) {
237
+ fileSignal?.throwIfAborted();
238
+ if (input.clone === "always") {
239
+ throw new FsSafeError("helper-unavailable", "required cloning cannot verify staged permissions before copying");
240
+ }
241
+ }
211
242
  assertStagedDirectoryCurrent(this.#directory);
212
243
  // The exclusive open performs no fallible post-open checks. Store its fd
213
244
  // before every subsequent operation, including the first metadata read.
214
245
  const state = this.#open();
246
+ if (this.#private)
247
+ assertDarwinCreationAcl(this.#parentFd, "file");
215
248
  this.#assertBeforeMutation?.();
216
- const copied = input.kind === "file"
249
+ if (this.#private)
250
+ assertDarwinCreationAcl(this.#parentFd, "file");
251
+ // Native copying populates its new file before returning its descriptor.
252
+ const copied = input.kind === "file" && !this.#verifyMode
217
253
  ? await createNativeCopyFile(this.#binding, input, this.#parentFd, this.#name, maxBytes, false)
218
254
  : undefined;
219
255
  state.fileFd = copied?.fd;
220
256
  if (state.fileFd === undefined) {
221
257
  this.#assertBeforeMutation?.();
258
+ if (this.#private)
259
+ assertDarwinCreationAcl(this.#parentFd, "file");
222
260
  state.fileFd = this.#binding.createStagedFile(this.#parentFd, this.#name);
223
261
  }
224
262
  const fd = state.fileFd;
225
263
  if (input.kind === "file")
226
264
  assertNativeCopyCompleted(input, copied);
227
- this.#assertBeforeMutation?.();
265
+ const assertBeforeChmod = this.#private
266
+ ? privateFileMutationAssertion(fd, this.#assertBeforeMutation) : this.#assertBeforeMutation;
267
+ assertBeforeChmod?.();
228
268
  fs.fchmodSync(fd, 0o600);
269
+ // Some filesystems report successful chmod without enforcing its mode.
270
+ if (this.#verifyMode)
271
+ this.#assertStagePermissions(fd);
272
+ const assertBeforeMutation = this.#verifyMode ? () => {
273
+ assertSynchronousCallbackResult(this.#assertBeforeMutation?.(), "assertBeforeMutation");
274
+ fileSignal?.throwIfAborted();
275
+ this.#assertStagePermissions(fd);
276
+ } : this.#assertBeforeMutation;
229
277
  if (!copied)
230
- await writeNativeInput(fd, input, maxBytes, this.#assertBeforeMutation);
231
- if (this.#sync)
232
- syncFileBestEffortSync(fd);
278
+ await writePinnedInput(fd, input, maxBytes, assertBeforeMutation);
279
+ if (this.#verifyMode)
280
+ this.#assertStagePermissions(fd);
281
+ if (this.#sync) {
282
+ if (this.#strictFileSync)
283
+ fs.fsyncSync(fd);
284
+ else
285
+ syncFileBestEffortSync(fd);
286
+ }
233
287
  const stat = fs.fstatSync(fd, { bigint: true });
234
- this.#receipt = Object.freeze({
235
- directory: this.#directory,
236
- temporaryBasename: this.#name,
237
- identity: Object.freeze({
238
- dev: stat.dev,
239
- ino: stat.ino,
240
- mode: Number(stat.mode & 4095n),
241
- nlink: stat.nlink,
242
- size: stat.size,
243
- uid: Number(stat.uid),
244
- gid: Number(stat.gid),
245
- mtimeNs: stat.mtimeNs,
246
- ctimeNs: stat.ctimeNs,
247
- }),
248
- });
288
+ this.#receipt = createStagedFileReceipt(this.#directory, this.#name, stat);
249
289
  this.#assertCurrent();
250
290
  }
251
291
  catch (error) {
@@ -275,6 +315,10 @@ class NativeStagedFile {
275
315
  this.#assertCurrent();
276
316
  assertFinalSymlinkRejected(path.join(this.#directory.realPath, basename), this.#rejectFinalSymlink);
277
317
  this.#assertBeforeMutation?.();
318
+ if (this.#assertBeforeMutation) {
319
+ this.#assertCurrent();
320
+ assertFinalSymlinkRejected(path.join(this.#directory.realPath, basename), this.#rejectFinalSymlink);
321
+ }
278
322
  try {
279
323
  if (overwrite) {
280
324
  this.#binding.renameReplace(this.#parentFd, this.#name, this.#parentFd, basename);
@@ -307,7 +351,7 @@ class NativeStagedFile {
307
351
  const receipt = Object.freeze({ status: "published", staged: this.receipt, basename, overwrite });
308
352
  this.#open().publication = receipt;
309
353
  onPublished?.(receipt.staged.identity);
310
- const stagedMode = this.#assertNamed(basename);
354
+ const stagedMode = this.#assertNamed(basename, 0o600);
311
355
  assertStagedDirectoryCurrent(this.#directory);
312
356
  // Keep contents private until the name passes its identity fence. Mode
313
357
  // changes use the owned fd, including for final mode 000.
@@ -317,11 +361,14 @@ class NativeStagedFile {
317
361
  // Content was synced during preparation. A lost chmod leaves 0600, no wider
318
362
  // than modes retaining owner rw; restrictive modes still need a durable correction.
319
363
  if (this.#sync && ((this.#publishedMode & 0o600) !== 0o600 || (stagedMode & ~this.#publishedMode) !== 0)) {
320
- syncFileBestEffortSync(fd);
364
+ if (this.#strictFileSync)
365
+ fs.fsyncSync(fd);
366
+ else
367
+ syncFileBestEffortSync(fd);
321
368
  }
322
369
  if (this.#sync)
323
370
  syncFileBestEffortSync(this.#parentFd);
324
- this.#assertNamed(basename);
371
+ this.#assertNamed(basename, this.#publishedMode);
325
372
  assertStagedDirectoryCurrent(this.#directory);
326
373
  return receipt;
327
374
  }
@@ -397,5 +444,5 @@ export async function stageFileInDirectory(options) {
397
444
  const input = { kind: "buffer", data: Buffer.from(options.content) };
398
445
  const mode = options.mode ?? 0o600;
399
446
  const parent = openStagedDirectory(options.directory);
400
- return await createNativeStage(binding, parent.fd, fs.closeSync, parent.receipt, input, mode);
447
+ return await createNativeStage(binding, parent.fd, fs.closeSync, parent.receipt, input, mode, undefined, true, true, undefined, undefined, false, "mode-only");
401
448
  }
@@ -1 +1 @@
1
- {"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,GACD;IACE,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAmCvE"}
1
+ {"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,GACD;IACE,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAmCvE"}
@@ -1,17 +1,23 @@
1
1
  import { FsSafeError } from "./errors.js";
2
2
  import { getNativeBinding } from "./native.js";
3
+ import { getFsSafeNativeConfig } from "./native-config.js";
4
+ import { warnNativeFallback } from "./native-fallback-warning.js";
3
5
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
6
+ import { readWindowsSecurityFactsCommand } from "./windows-security-command.js";
4
7
  export function readOwnerAndDacl(targetPath) {
5
8
  if (process.platform !== "win32") {
6
9
  return { status: "unsupported-platform", platform: process.platform };
7
10
  }
8
11
  assertNoWindowsPathAlias(targetPath, "filesystem", "owner and DACL path uses a Windows filesystem namespace alias");
9
12
  const native = getNativeBinding();
10
- if (!native) {
11
- throw new FsSafeError("helper-unavailable", "Windows owner and DACL facts require the matching optional native platform package; " +
12
- "install @openclaw/fs-safe with optional dependencies enabled on a supported platform and use FS_SAFE_NATIVE_MODE=auto or require");
13
+ const inspect = native?.readOwnerAndDacl;
14
+ if (typeof inspect !== "function") {
15
+ if (getFsSafeNativeConfig().mode === "require") {
16
+ throw new FsSafeError("helper-unavailable", "Windows owner and DACL facts require an up-to-date native helper");
17
+ }
18
+ warnNativeFallback("windows-owner-dacl", "Windows owner and DACL inspection uses a slower built-in system command.");
13
19
  }
14
- const facts = native.readOwnerAndDacl(targetPath);
20
+ const facts = typeof inspect === "function" ? inspect.call(native, targetPath) : readWindowsSecurityFactsCommand(targetPath);
15
21
  return {
16
22
  status: "supported",
17
23
  ownerSid: facts.ownerSid,
@@ -0,0 +1,4 @@
1
+ import type { FileHandle } from "node:fs/promises";
2
+ import type { PinnedWriteInput } from "./pinned-write-types.js";
3
+ export declare function writePinnedInput(target: FileHandle | number, input: PinnedWriteInput, maxBytes?: number, assertBeforeMutation?: () => void): Promise<void>;
4
+ //# sourceMappingURL=pinned-write-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-write-input.d.ts","sourceRoot":"","sources":["../src/pinned-write-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,KAAK,EAAE,gBAAgB,EACvB,QAAQ,CAAC,EAAE,MAAM,EACjB,oBAAoB,CAAC,EAAE,MAAM,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAoBf"}
@@ -0,0 +1,25 @@
1
+ import { writeCopyFileToFd } from "./copy-file-input.js";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { writeAllToFile } from "./write-file-handle.js";
4
+ export async function writePinnedInput(target, input, maxBytes, assertBeforeMutation) {
5
+ if (input.kind === "file") {
6
+ await writeCopyFileToFd(typeof target === "number" ? target : target.fd, input, maxBytes, assertBeforeMutation);
7
+ return;
8
+ }
9
+ let bytes = 0;
10
+ const write = async (data) => {
11
+ bytes += data.byteLength;
12
+ if (maxBytes !== undefined && bytes > maxBytes) {
13
+ throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
14
+ }
15
+ await writeAllToFile(target, data, { assertBeforeMutation });
16
+ };
17
+ if (input.kind === "buffer") {
18
+ await write(typeof input.data === "string" ? Buffer.from(input.data, input.encoding ?? "utf8") : input.data);
19
+ }
20
+ else {
21
+ for await (const chunk of input.stream) {
22
+ await write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ import type { FileHandle } from "node:fs/promises";
2
+ export declare function assertPinnedWriteMode(fd: number, mode: number): void;
3
+ export declare function preparePinnedWriteMode(handle: FileHandle, createdMode: number, assertBeforeMutation?: () => void): Promise<(() => void) | undefined>;
4
+ export declare function pinnedWriteModeAssertion(fd: number, mode: number, assertBeforeMutation?: () => void): () => void;
5
+ //# sourceMappingURL=pinned-write-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-write-mode.d.ts","sourceRoot":"","sources":["../src/pinned-write-mode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAKpE;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,EACnB,oBAAoB,CAAC,EAAE,MAAM,IAAI,GAChC,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,CAKnC;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,oBAAoB,CAAC,EAAE,MAAM,IAAI,GAChC,MAAM,IAAI,CAKZ"}
@@ -0,0 +1,24 @@
1
+ import fs from "node:fs";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { assertSynchronousCallbackResult } from "./mutation-authority.js";
4
+ export function assertPinnedWriteMode(fd, mode) {
5
+ if (process.platform === "win32")
6
+ return;
7
+ if (Number(fs.fstatSync(fd, { bigint: true }).mode & 4095n) !== mode) {
8
+ throw new FsSafeError("insecure-permissions", "filesystem did not enforce the secret file mode");
9
+ }
10
+ }
11
+ export async function preparePinnedWriteMode(handle, createdMode, assertBeforeMutation) {
12
+ if (process.platform === "win32")
13
+ return assertBeforeMutation;
14
+ assertSynchronousCallbackResult(assertBeforeMutation?.(), "assertBeforeMutation");
15
+ if (createdMode !== 0o600)
16
+ await handle.chmod(0o600);
17
+ return pinnedWriteModeAssertion(handle.fd, 0o600, assertBeforeMutation);
18
+ }
19
+ export function pinnedWriteModeAssertion(fd, mode, assertBeforeMutation) {
20
+ return () => {
21
+ assertSynchronousCallbackResult(assertBeforeMutation?.(), "assertBeforeMutation");
22
+ assertPinnedWriteMode(fd, mode);
23
+ };
24
+ }
@@ -0,0 +1,6 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ import type { AsyncDirectoryGuard } from "./directory-guard.js";
3
+ import { type FileIdentityStat } from "./file-identity.js";
4
+ import type { PinnedWriteParams } from "./pinned-write-types.js";
5
+ export declare function runPinnedStagedWrite(params: PinnedWriteParams, parentPath: string, parentGuard: AsyncDirectoryGuard<BigIntStats>): Promise<FileIdentityStat>;
6
+ //# sourceMappingURL=pinned-write-staged.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-write-staged.d.ts","sourceRoot":"","sources":["../src/pinned-write-staged.ts"],"names":[],"mappings":"AACA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAMnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE,OAAO,EAA+B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAMxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AASjE,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,mBAAmB,CAAC,WAAW,CAAC,GAC5C,OAAO,CAAC,gBAAgB,CAAC,CAqJ3B"}
@@ -0,0 +1,187 @@
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 { createFileHandle } from "./create.js";
6
+ import { hasPreservedCreationArtifacts } from "./creation-file-state.js";
7
+ import { creationAdmissionFromParent } from "./creation-path.js";
8
+ import { syncDirectoryBestEffort } from "./directory-durability.js";
9
+ import { FsSafeError } from "./errors.js";
10
+ import { sameFileIdentity, sha256Hex } from "./file-identity.js";
11
+ import { syncFileBestEffort } from "./file-sync.js";
12
+ import { assertDarwinCreationAcl, privateFileMutationAssertion } from "./creation-darwin.js";
13
+ import { withAsyncDirectoryGuards } from "./guarded-mutation.js";
14
+ import { writePinnedInput } from "./pinned-write-input.js";
15
+ import { assertPinnedWriteMode, pinnedWriteModeAssertion, preparePinnedWriteMode } from "./pinned-write-mode.js";
16
+ import { publishCopyStage } from "./publish-copy-stage.js";
17
+ import { resolveReadOpenFlags } from "./read-open-flags.js";
18
+ import { cleanupPinnedFilePath } from "./file-cleanup.js";
19
+ import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
20
+ import { createStagedFileReceipt, settleStagedFile } from "./staged-file-settlement.js";
21
+ import { getFsSafeTestHooks } from "./test-hooks.js";
22
+ export async function runPinnedStagedWrite(params, parentPath, parentGuard) {
23
+ const verifyPosixMode = params.verifyPosixMode === true && process.platform !== "win32";
24
+ const targetPath = path.join(parentPath, params.basename);
25
+ // Private staging must not consume the destination basename's filename budget.
26
+ const tempPath = path.join(parentPath, `.fs-safe-${randomUUID()}.tmp`);
27
+ const tempFlags = fsSync.constants.O_WRONLY |
28
+ fsSync.constants.O_CREAT |
29
+ fsSync.constants.O_EXCL |
30
+ (process.platform !== "win32" && "O_NOFOLLOW" in fsSync.constants
31
+ ? fsSync.constants.O_NOFOLLOW
32
+ : 0);
33
+ let handle;
34
+ let tempStat;
35
+ let tempIdentity;
36
+ let readHandle;
37
+ let renamed = false;
38
+ const completeCreate = params.overwrite === false && params.input.kind !== "file" && params.input.stageBeforePublish === true;
39
+ let receipt;
40
+ let publication = Object.freeze({ status: "not-published" });
41
+ let phase = "prepare";
42
+ let failure;
43
+ try {
44
+ params.assertBeforeMutation?.();
45
+ handle = params.private
46
+ ? await createFileHandle(tempPath, {
47
+ private: true, mode: 0o600, assertBeforeMutation: params.assertBeforeMutation,
48
+ }, creationAdmissionFromParent(parentGuard))
49
+ : await fs.open(tempPath, tempFlags, verifyPosixMode ? 0o600 : params.mode);
50
+ let verificationIdentity = fsSync.fstatSync(handle.fd, { bigint: true });
51
+ tempIdentity = verificationIdentity;
52
+ let assertBeforeMutation = params.private
53
+ ? privateFileMutationAssertion(handle.fd, params.assertBeforeMutation) : params.assertBeforeMutation;
54
+ const assertBeforeWrite = verifyPosixMode
55
+ ? await preparePinnedWriteMode(handle, Number(tempIdentity.mode & 4095n), assertBeforeMutation)
56
+ : assertBeforeMutation;
57
+ if (verifyPosixMode)
58
+ assertPinnedWriteMode(handle.fd, 0o600);
59
+ await writePinnedInput(handle, params.input, params.maxBytes, assertBeforeWrite);
60
+ if (verifyPosixMode)
61
+ assertPinnedWriteMode(handle.fd, 0o600);
62
+ if (params.private)
63
+ assertDarwinCreationAcl(handle.fd);
64
+ tempStat = fsSync.fstatSync(handle.fd);
65
+ const tempPathStat = fsSync.lstatSync(tempPath);
66
+ if (tempPathStat.isSymbolicLink() || !sameFileIdentity(tempPathStat, tempStat)) {
67
+ throw new FsSafeError("path-mismatch", "fallback temp path changed during write");
68
+ }
69
+ const expectedTempStat = tempStat;
70
+ await handle.chmod(params.mode);
71
+ if (verifyPosixMode) {
72
+ assertPinnedWriteMode(handle.fd, params.mode);
73
+ assertBeforeMutation = pinnedWriteModeAssertion(handle.fd, params.mode, assertBeforeMutation);
74
+ }
75
+ if (params.sync !== false) {
76
+ if (params.strictFileSync)
77
+ await handle.sync();
78
+ else
79
+ await syncFileBestEffort(handle);
80
+ }
81
+ if (params.input.kind === "file")
82
+ await params.input.verifySource();
83
+ if (completeCreate) {
84
+ receipt = createStagedFileReceipt(Object.freeze({
85
+ path: parentGuard.dir,
86
+ realPath: parentGuard.realPath,
87
+ identity: Object.freeze({ dev: parentGuard.stat.dev, ino: parentGuard.stat.ino }),
88
+ }), path.basename(tempPath), fsSync.fstatSync(handle.fd, { bigint: true }));
89
+ }
90
+ let verifiedIdentity = expectedTempStat;
91
+ phase = "publish";
92
+ await withAsyncDirectoryGuards([parentGuard], async () => {
93
+ assertFinalSymlinkRejected(targetPath, params.rejectFinalSymlink);
94
+ if (params.overwrite === false) {
95
+ publishCopyStage({
96
+ temporaryPath: tempPath, targetPath, fd: handle.fd,
97
+ identity: tempIdentity, parentGuard,
98
+ assertBeforeMutation,
99
+ onPublicationAttempt: () => {
100
+ if (receipt)
101
+ publication = Object.freeze({ status: "indeterminate", basename: params.basename, overwrite: false });
102
+ },
103
+ onPublished: (identity) => {
104
+ renamed = true;
105
+ if (receipt)
106
+ publication = Object.freeze({ status: "published", staged: receipt, basename: params.basename, overwrite: false });
107
+ params.onPublished?.(identity);
108
+ },
109
+ });
110
+ }
111
+ else {
112
+ assertBeforeMutation?.();
113
+ await fs.rename(tempPath, targetPath);
114
+ renamed = true;
115
+ params.onPublished?.(verificationIdentity);
116
+ }
117
+ await getFsSafeTestHooks()?.afterPinnedWriteFallbackRename?.(targetPath);
118
+ if (params.sync !== false)
119
+ await syncDirectoryBestEffort(parentPath);
120
+ const targetStat = fsSync.lstatSync(targetPath);
121
+ if (targetStat.isSymbolicLink()) {
122
+ throw new FsSafeError("path-mismatch", "fallback target changed during write");
123
+ }
124
+ if (!sameFileIdentity(targetStat, expectedTempStat)) {
125
+ // On filesystems like rclone FUSE, rename(2) can give the destination a
126
+ // different inode from the source temp fd even with zero concurrency. The
127
+ // caller must ensure mutual exclusion before passing "verify-content";
128
+ // fall back to a content hash for this rename-boundary check only.
129
+ if (params.onRenameIdentityMismatch !== "verify-content") {
130
+ throw new FsSafeError("path-mismatch", "fallback target changed during write");
131
+ }
132
+ if (params.input.kind !== "buffer") {
133
+ throw new FsSafeError("path-mismatch", "fallback target changed during write");
134
+ }
135
+ const expectedHash = sha256Hex(params.input.data, params.input.encoding);
136
+ readHandle = await fs.open(targetPath, resolveReadOpenFlags());
137
+ const readHandleStat = fsSync.fstatSync(readHandle.fd, { bigint: true });
138
+ const actualHash = sha256Hex(await readHandle.readFile());
139
+ if (actualHash !== expectedHash) {
140
+ throw new FsSafeError("path-mismatch", "fallback target changed during write");
141
+ }
142
+ // The content-verified destination, not the old temp inode, is now pinned.
143
+ verificationIdentity = readHandleStat;
144
+ // Preserve the helper's legacy numeric return facts, not the private proof.
145
+ verifiedIdentity = { dev: Number(readHandleStat.dev), ino: Number(readHandleStat.ino) };
146
+ }
147
+ });
148
+ await params.verifyPublished?.((readHandle ?? handle).fd, verificationIdentity, parentGuard);
149
+ return { dev: verifiedIdentity.dev, ino: verifiedIdentity.ino };
150
+ }
151
+ catch (error) {
152
+ failure = { error };
153
+ throw error;
154
+ }
155
+ finally {
156
+ const preservedPreparation = params.private && !handle && hasPreservedCreationArtifacts(failure?.error);
157
+ if (completeCreate || preservedPreparation) {
158
+ await settleStagedFile({
159
+ temporaryBasename: path.basename(tempPath), publication, phase, failure,
160
+ cleanup: async () => {
161
+ if (preservedPreparation)
162
+ return "preserved";
163
+ if (publication.status === "indeterminate")
164
+ return "preserved";
165
+ if (renamed) {
166
+ return failure?.error instanceof FsSafeError && failure.error.details?.cleanup === "failed"
167
+ ? "failed" : "not-needed";
168
+ }
169
+ return handle ? await cleanupPinnedFilePath({
170
+ pathname: tempPath, handle, identity: tempIdentity, parentGuard, throwOnCleanupError: true,
171
+ }) : "not-needed";
172
+ },
173
+ close: [async () => { await readHandle?.close(); }, async () => { await handle?.close(); }],
174
+ });
175
+ }
176
+ else
177
+ try {
178
+ if (!renamed && handle) {
179
+ await cleanupPinnedFilePath({ pathname: tempPath, handle, identity: tempIdentity, parentGuard });
180
+ }
181
+ }
182
+ finally {
183
+ await readHandle?.close().catch(() => undefined);
184
+ await handle?.close().catch(() => undefined);
185
+ }
186
+ }
187
+ }
@@ -62,7 +62,10 @@ export type PinnedWriteParams = {
62
62
  basename: string;
63
63
  mkdir: boolean;
64
64
  mode: number;
65
+ private?: boolean;
66
+ verifyPosixMode?: boolean;
65
67
  sync?: boolean;
68
+ strictFileSync?: boolean;
66
69
  overwrite?: boolean;
67
70
  assertBeforeMutation?: () => void;
68
71
  rejectFinalSymlink?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"pinned-write-types.d.ts","sourceRoot":"","sources":["../src/pinned-write-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pinned-write-types.d.ts","sourceRoot":"","sources":["../src/pinned-write-types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pinned-write.d.ts","sourceRoot":"","sources":["../src/pinned-write.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAiB3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACvF,YAAY,EACV,gBAAgB,EAAE,oBAAoB,EAAE,sBAAsB,EAC9D,8BAA8B,EAAE,gCAAgC,EAChE,6BAA6B,EAAE,+BAA+B,EAC9D,4BAA4B,EAAE,iBAAiB,GAChD,MAAM,yBAAyB,CAAC;AAgFjC,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsD/F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,iBAAiB,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC,GACA,OAAO,CAAC,gBAAgB,CAAC,CAkB3B;AAED,wBAAsB,iCAAiC,CAAC,CAAC,EACvD,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,EAC5E,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACpB,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
1
+ {"version":3,"file":"pinned-write.d.ts","sourceRoot":"","sources":["../src/pinned-write.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAgB3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACvF,YAAY,EACV,gBAAgB,EAAE,oBAAoB,EAAE,sBAAsB,EAC9D,8BAA8B,EAAE,gCAAgC,EAChE,6BAA6B,EAAE,+BAA+B,EAC9D,4BAA4B,EAAE,iBAAiB,GAChD,MAAM,yBAAyB,CAAC;AAkDjC,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsD/F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,iBAAiB,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC,GACA,OAAO,CAAC,gBAAgB,CAAC,CAkB3B;AAED,wBAAsB,iCAAiC,CAAC,CAAC,EACvD,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,EAC5E,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACpB,OAAO,CAAC,CAAC,CAAC,CAiBZ"}