@openclaw/fs-safe 0.8.3 → 0.8.5

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 (74) hide show
  1. package/CHANGELOG.md +47 -1
  2. package/dist/absolute-path.d.ts.map +1 -1
  3. package/dist/absolute-path.js +8 -7
  4. package/dist/archive-native.d.ts +1 -0
  5. package/dist/archive-native.d.ts.map +1 -1
  6. package/dist/archive-native.js +15 -68
  7. package/dist/archive-plan.d.ts +20 -0
  8. package/dist/archive-plan.d.ts.map +1 -0
  9. package/dist/archive-plan.js +55 -0
  10. package/dist/archive-tar-inspect.d.ts +7 -0
  11. package/dist/archive-tar-inspect.d.ts.map +1 -0
  12. package/dist/archive-tar-inspect.js +59 -0
  13. package/dist/archive-tar.d.ts +3 -1
  14. package/dist/archive-tar.d.ts.map +1 -1
  15. package/dist/archive-tar.js +12 -49
  16. package/dist/archive.d.ts +1 -0
  17. package/dist/archive.d.ts.map +1 -1
  18. package/dist/archive.js +20 -36
  19. package/dist/bounded-read.d.ts.map +1 -1
  20. package/dist/bounded-read.js +31 -4
  21. package/dist/directory-durability.js +5 -5
  22. package/dist/directory-guard.d.ts.map +1 -1
  23. package/dist/directory-guard.js +5 -6
  24. package/dist/file-store-boundary.js +1 -1
  25. package/dist/file-store-prune.js +17 -8
  26. package/dist/file-store.js +2 -2
  27. package/dist/guarded-mkdir.d.ts.map +1 -1
  28. package/dist/guarded-mkdir.js +5 -4
  29. package/dist/move-path.js +1 -1
  30. package/dist/native-binding.d.ts +2 -1
  31. package/dist/native-binding.d.ts.map +1 -1
  32. package/dist/native-pinned-write-windows.js +6 -3
  33. package/dist/native-pinned-write.js +3 -3
  34. package/dist/native-staged-file.d.ts +2 -2
  35. package/dist/native-staged-file.d.ts.map +1 -1
  36. package/dist/native-staged-file.js +12 -8
  37. package/dist/opened-realpath.d.ts.map +1 -1
  38. package/dist/opened-realpath.js +10 -9
  39. package/dist/path-policy.js +2 -2
  40. package/dist/pinned-write.d.ts +1 -0
  41. package/dist/pinned-write.d.ts.map +1 -1
  42. package/dist/pinned-write.js +15 -11
  43. package/dist/regular-file.js +8 -8
  44. package/dist/replace-file-descriptor.d.ts.map +1 -1
  45. package/dist/replace-file-descriptor.js +8 -4
  46. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  47. package/dist/replace-file-temp-owner.js +13 -7
  48. package/dist/root-context.js +5 -5
  49. package/dist/root-impl.d.ts +2 -1
  50. package/dist/root-impl.d.ts.map +1 -1
  51. package/dist/root-impl.js +35 -26
  52. package/dist/root-path-existing.d.ts.map +1 -1
  53. package/dist/root-path-existing.js +2 -3
  54. package/dist/root-path-symlink.d.ts.map +1 -1
  55. package/dist/root-path-symlink.js +2 -3
  56. package/dist/root-path.d.ts.map +1 -1
  57. package/dist/root-path.js +3 -4
  58. package/dist/root-paths.d.ts.map +1 -1
  59. package/dist/root-paths.js +16 -15
  60. package/dist/root-write-mode.d.ts.map +1 -1
  61. package/dist/root-write-mode.js +5 -4
  62. package/dist/root-write-verification.js +6 -6
  63. package/dist/secret-file.js +2 -2
  64. package/dist/strict-file-identity.d.ts +1 -1
  65. package/dist/strict-file-identity.d.ts.map +1 -1
  66. package/dist/symlink-parents.d.ts.map +1 -1
  67. package/dist/symlink-parents.js +1 -2
  68. package/docs/archive.md +57 -0
  69. package/docs/atomic.md +2 -0
  70. package/docs/reading.md +2 -0
  71. package/docs/root.md +10 -1
  72. package/docs/types.md +2 -1
  73. package/docs/writing.md +26 -3
  74. package/package.json +8 -8
package/dist/root-impl.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { constants as fsConstants } from "node:fs";
2
+ import fsSync, { constants as fsConstants } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { normalizeMaxBytes } from "./byte-budget.js";
@@ -87,7 +87,7 @@ async function openVerifiedLocalFile(filePath, options) {
87
87
  // results that get sent to messaging channels). See openclaw/openclaw#31186.
88
88
  try {
89
89
  preOpenStat = await inspectFileIdentity(async () => {
90
- const stat = await fs.lstat(filePath, { bigint: true });
90
+ const stat = fsSync.lstatSync(filePath, { bigint: true });
91
91
  observedBeforeOpen = true;
92
92
  if (stat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
93
93
  throw new FsSafeError("symlink", "symlink not allowed");
@@ -127,13 +127,13 @@ async function openVerifiedLocalFile(filePath, options) {
127
127
  }
128
128
  try {
129
129
  await fsSafeTestHooks?.afterOpen?.(filePath, handle);
130
- const stat = await handle.stat();
130
+ const stat = fsSync.fstatSync(handle.fd);
131
131
  if (!stat.isFile()) {
132
132
  throw new FsSafeError("not-file", "not a file");
133
133
  }
134
134
  // Keep numeric Stats for the public receipt, never for identity verification.
135
135
  let openedIdentity;
136
- const identity = await inspectFileIdentity(async () => (openedIdentity = await handle.stat({ bigint: true })), preOpenStat && !preOpenStat.isSymbolicLink() ? preOpenStat : undefined).catch(async (error) => {
136
+ const identity = await inspectFileIdentity(async () => (openedIdentity = fsSync.fstatSync(handle.fd, { bigint: true })), preOpenStat && !preOpenStat.isSymbolicLink() ? preOpenStat : undefined).catch(async (error) => {
137
137
  if ([0, 1].includes(stat.nlink)) {
138
138
  await recordPreOpenFileChange(error, handle, filePath, preOpenStat, openedIdentity);
139
139
  }
@@ -156,8 +156,8 @@ async function openVerifiedLocalFile(filePath, options) {
156
156
  };
157
157
  await inspectPathIdentity(async () => {
158
158
  const pathStat = options?.symlinks === "follow-within-root"
159
- ? await fs.stat(filePath, { bigint: true })
160
- : await fs.lstat(filePath, { bigint: true });
159
+ ? fsSync.statSync(filePath, { bigint: true })
160
+ : fsSync.lstatSync(filePath, { bigint: true });
161
161
  if (pathStat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
162
162
  throw new FsSafeError("symlink", "symlink not allowed");
163
163
  }
@@ -176,7 +176,7 @@ async function openVerifiedLocalFile(filePath, options) {
176
176
  await inspectPathIdentity(async () => {
177
177
  // Reuse the post-realpath observation; unknown Windows identities still
178
178
  // get a fresh observation on inspectFileIdentity's retry.
179
- const realStat = resolvedStat ?? await fs.stat(realPath, { bigint: true });
179
+ const realStat = resolvedStat ?? fsSync.statSync(realPath, { bigint: true });
180
180
  resolvedStat = undefined;
181
181
  if (options?.hardlinks === "reject" && realStat.nlink > 1n) {
182
182
  throw hardlinkedPathNotAllowedError();
@@ -275,6 +275,7 @@ export class RootHandle {
275
275
  mkdir: this.defaults.mkdir,
276
276
  mode: this.defaults.mode,
277
277
  ...this.mutationOptions(options),
278
+ durable: options.durable ?? this.defaults.durable ?? true,
278
279
  });
279
280
  }
280
281
  async remove(relativePath, options = {}) {
@@ -307,6 +308,7 @@ export class RootHandle {
307
308
  mode: this.defaults.mode,
308
309
  renameIdentity: this.defaults.renameIdentity,
309
310
  ...this.mutationOptions(options),
311
+ durable: options.durable ?? this.defaults.durable ?? true,
310
312
  });
311
313
  }
312
314
  async create(relativePath, data, options = {}) {
@@ -317,6 +319,7 @@ export class RootHandle {
317
319
  mkdir: this.defaults.mkdir,
318
320
  mode: this.defaults.mode,
319
321
  ...this.mutationOptions(options),
322
+ durable: options.durable ?? this.defaults.durable ?? true,
320
323
  overwrite: false,
321
324
  });
322
325
  }
@@ -486,7 +489,7 @@ async function writeTempFileForAtomicReplace(params) {
486
489
  }
487
490
  return {
488
491
  handle: tempHandle,
489
- identity: await tempHandle.stat({ bigint: true }),
492
+ identity: fsSync.fstatSync(tempHandle.fd, { bigint: true }),
490
493
  };
491
494
  }
492
495
  catch (error) {
@@ -525,7 +528,7 @@ async function openWritableFileInRoot(root, params) {
525
528
  ? resolved
526
529
  : await prepareRootWriteTarget(rootReal, resolved);
527
530
  try {
528
- const resolvedRealPath = await fs.realpath(ioPath);
531
+ const resolvedRealPath = fsSync.realpathSync.native(ioPath);
529
532
  if (!isPathInside(rootWithSep, resolvedRealPath)) {
530
533
  throw outsideWorkspaceError();
531
534
  }
@@ -574,7 +577,7 @@ async function openWritableFileInRoot(root, params) {
574
577
  let realPathForCleanup = null;
575
578
  let createdIdentity = null;
576
579
  try {
577
- const stat = await handle.stat();
580
+ const stat = fsSync.fstatSync(handle.fd);
578
581
  if (createdForWrite) {
579
582
  createdIdentity = stat;
580
583
  }
@@ -585,7 +588,7 @@ async function openWritableFileInRoot(root, params) {
585
588
  throw hardlinkedPathNotAllowedError();
586
589
  }
587
590
  try {
588
- const lstat = await fs.lstat(ioPath);
591
+ const lstat = fsSync.lstatSync(ioPath);
589
592
  if (lstat.isSymbolicLink() || !lstat.isFile()) {
590
593
  throw new FsSafeError(lstat.isSymbolicLink() ? "symlink" : "not-file", "path is not a regular file under root");
591
594
  }
@@ -600,7 +603,7 @@ async function openWritableFileInRoot(root, params) {
600
603
  }
601
604
  const realPath = await resolveOpenedFileRealPathForHandle(handle, ioPath);
602
605
  realPathForCleanup = realPath;
603
- const realStat = await fs.stat(realPath);
606
+ const realStat = fsSync.statSync(realPath);
604
607
  if (!sameFileIdentity(stat, realStat)) {
605
608
  throw new FsSafeError("path-mismatch", "path mismatch");
606
609
  }
@@ -663,8 +666,9 @@ async function appendFileInRoot(root, params) {
663
666
  const payload = prefix.length > 0 ? Buffer.concat([Buffer.from(prefix, "utf8"), params.data]) : params.data;
664
667
  await target.handle.appendFile(payload);
665
668
  }
666
- await target.handle.sync();
667
- if (target.createdForWrite) {
669
+ if (params.durable !== false)
670
+ await target.handle.sync();
671
+ if (params.durable !== false && target.createdForWrite) {
668
672
  await syncDirectoryBestEffort(path.dirname(target.realPath));
669
673
  }
670
674
  }
@@ -720,6 +724,7 @@ async function commitPinnedWriteInRoot(root, pinned, params) {
720
724
  renameIdentity: params.renameIdentity,
721
725
  mkdir: params.mkdir !== false,
722
726
  mode: params.mode ?? pinned.mode,
727
+ sync: params.durable !== false,
723
728
  overwrite: params.overwrite,
724
729
  input: { kind: "buffer", data: params.data, encoding: params.encoding },
725
730
  rootIdentity: root.rootIdentity,
@@ -803,12 +808,12 @@ async function copyFileInRoot(root, params) {
803
808
  }
804
809
  }
805
810
  async function assertCopySourceCurrent(source, identity) {
806
- await inspectFileIdentity(() => source.handle.stat({ bigint: true }), identity);
811
+ await inspectFileIdentity(() => fsSync.fstatSync(source.handle.fd, { bigint: true }), identity);
807
812
  await assertCopySourcePathCurrent(source, identity);
808
813
  }
809
814
  async function assertCopySourcePathCurrent(source, identity) {
810
815
  await inspectFileIdentity(async () => {
811
- const current = await fs.lstat(source.realPath, { bigint: true });
816
+ const current = fsSync.lstatSync(source.realPath, { bigint: true });
812
817
  if (current.isSymbolicLink() || !current.isFile()) {
813
818
  throw new FsSafeError("path-mismatch", "copy source path changed");
814
819
  }
@@ -817,7 +822,7 @@ async function assertCopySourcePathCurrent(source, identity) {
817
822
  }
818
823
  async function removePathIfIdentityUnchanged(targetPath, identity) {
819
824
  const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
820
- const current = await fs.lstat(targetPath);
825
+ const current = fsSync.lstatSync(targetPath);
821
826
  if (current.isSymbolicLink() || !sameFileIdentityForCleanup(current, identity)) {
822
827
  return;
823
828
  }
@@ -847,7 +852,7 @@ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode,
847
852
  // and alias checks, but never open a competing owner's record just to reject it.
848
853
  if (!overwrite) {
849
854
  try {
850
- const existing = await fs.stat(resolved);
855
+ const existing = fsSync.statSync(resolved);
851
856
  if (!existing.isFile())
852
857
  throw new FsSafeError("not-file", "not a file");
853
858
  if (existing.nlink > 1)
@@ -945,7 +950,7 @@ async function prepareRemoveGuard(targetPath) {
945
950
  async function removePathFallback(resolved) {
946
951
  const guard = await prepareRemoveGuard(resolved.resolved);
947
952
  try {
948
- await ((await fs.lstat(resolved.resolved)).isDirectory() ? fs.rmdir(resolved.resolved) : fs.rm(resolved.resolved));
953
+ await (fsSync.lstatSync(resolved.resolved).isDirectory() ? fs.rmdir(resolved.resolved) : fs.rm(resolved.resolved));
949
954
  }
950
955
  catch (error) {
951
956
  throw normalizeRemovePathError(error);
@@ -961,7 +966,7 @@ async function mkdirPathFallback(resolved) {
961
966
  async function statPathFallback(root, relativePath) {
962
967
  const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
963
968
  try {
964
- const stat = pathStatFromStats(await fs.lstat(resolved.resolved));
969
+ const stat = pathStatFromStats(fsSync.lstatSync(resolved.resolved));
965
970
  await assertRootIdentityCurrent(root);
966
971
  return stat;
967
972
  }
@@ -985,7 +990,7 @@ async function listPathFallback(root, relativePath, withFileTypes) {
985
990
  for (const name of sortedNames) {
986
991
  entries.push({
987
992
  name,
988
- ...pathStatFromStats(await fs.lstat(path.join(resolved.resolved, name))),
993
+ ...pathStatFromStats(fsSync.lstatSync(path.join(resolved.resolved, name))),
989
994
  });
990
995
  }
991
996
  await assertRootIdentityCurrent(root);
@@ -1034,7 +1039,11 @@ async function movePathFallback(root, params) {
1034
1039
  relativePath: params.toRelative,
1035
1040
  policy: PATH_ALIAS_POLICIES.unlinkTarget,
1036
1041
  });
1037
- const targetStat = await fs.lstat(resolvedTarget.resolved).catch(() => undefined);
1042
+ let targetStat;
1043
+ try {
1044
+ targetStat = fsSync.lstatSync(resolvedTarget.resolved);
1045
+ }
1046
+ catch { /* Advisory lookup. */ }
1038
1047
  return !(process.platform !== "win32" &&
1039
1048
  params.overwrite &&
1040
1049
  targetStat?.isSymbolicLink() === true);
@@ -1042,7 +1051,7 @@ async function movePathFallback(root, params) {
1042
1051
  });
1043
1052
  let sourceStat;
1044
1053
  try {
1045
- sourceStat = await fs.lstat(source.resolved);
1054
+ sourceStat = fsSync.lstatSync(source.resolved);
1046
1055
  }
1047
1056
  catch (error) {
1048
1057
  if (isNotFoundPathError(error)) {
@@ -1061,7 +1070,7 @@ async function movePathFallback(root, params) {
1061
1070
  }
1062
1071
  if (!params.overwrite) {
1063
1072
  try {
1064
- await fs.lstat(target.resolved);
1073
+ fsSync.lstatSync(target.resolved);
1065
1074
  throw new FsSafeError("already-exists", "destination exists");
1066
1075
  }
1067
1076
  catch (error) {
@@ -1171,8 +1180,8 @@ async function writeMissingFileFallback(root, params) {
1171
1180
  const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600).catch((error) => recordExclusiveCreateFailure(error, targetPath));
1172
1181
  created = true;
1173
1182
  try {
1174
- createdIdentity = await handle.stat();
1175
- const writtenStat = await handle.stat({ bigint: true });
1183
+ createdIdentity = fsSync.fstatSync(handle.fd);
1184
+ const writtenStat = fsSync.fstatSync(handle.fd, { bigint: true });
1176
1185
  if (typeof params.data === "string") {
1177
1186
  await handle.writeFile(params.data, params.encoding ?? "utf8");
1178
1187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAqBA,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}
1
+ {"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAoBA,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}
@@ -1,5 +1,4 @@
1
1
  import fs from "node:fs";
2
- import fsp from "node:fs/promises";
3
2
  import path from "node:path";
4
3
  import { isNotFoundPathError } from "./path.js";
5
4
  function isFilesystemRoot(candidate) {
@@ -7,7 +6,7 @@ function isFilesystemRoot(candidate) {
7
6
  }
8
7
  async function pathExists(targetPath) {
9
8
  try {
10
- await fsp.lstat(targetPath);
9
+ fs.lstatSync(targetPath);
11
10
  return true;
12
11
  }
13
12
  catch (error) {
@@ -33,7 +32,7 @@ export async function resolvePathViaExistingAncestor(targetPath) {
33
32
  return normalized;
34
33
  }
35
34
  try {
36
- const resolvedAncestor = path.resolve(await fsp.realpath(cursor));
35
+ const resolvedAncestor = path.resolve(fs.realpathSync.native(cursor));
37
36
  return missingSuffix.length === 0
38
37
  ? resolvedAncestor
39
38
  : path.resolve(resolvedAncestor, ...missingSuffix);
@@ -1 +1 @@
1
- {"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"AAUA,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAiBF,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAQR"}
1
+ {"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"AASA,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAiBF,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAQR"}
@@ -1,5 +1,4 @@
1
1
  import fs from "node:fs";
2
- import fsp from "node:fs/promises";
3
2
  import path from "node:path";
4
3
  import { FsSafeError } from "./errors.js";
5
4
  import { isNotFoundPathError, isSymlinkOpenError } from "./path.js";
@@ -16,11 +15,11 @@ function normalizeSymlinkResolutionError(error, options) {
16
15
  }
17
16
  export async function resolveSymlinkHopPath(symlinkPath, options = {}) {
18
17
  try {
19
- return path.resolve(await fsp.realpath(symlinkPath));
18
+ return path.resolve(fs.realpathSync.native(symlinkPath));
20
19
  }
21
20
  catch (error) {
22
21
  normalizeSymlinkResolutionError(error, options);
23
- const linkTarget = await fsp.readlink(symlinkPath);
22
+ const linkTarget = fs.readlinkSync(symlinkPath);
24
23
  return resolvePathViaExistingAncestor(path.resolve(path.dirname(symlinkPath), linkTarget));
25
24
  }
26
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"root-path.d.ts","sourceRoot":"","sources":["../src/root-path.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yBAAyB,CAAC;AAE7E,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,wBAAwB;aACnC,MAAM;;;;aAIN,YAAY;;;;CAIJ,CAAC;AAEX,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;CAC5B,CAAC;AAEF,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
1
+ {"version":3,"file":"root-path.d.ts","sourceRoot":"","sources":["../src/root-path.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yBAAyB,CAAC;AAE7E,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,wBAAwB;aACnC,MAAM;;;;aAIN,YAAY;;;;CAIJ,CAAC;AAEX,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;CAC5B,CAAC;AAEF,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
package/dist/root-path.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import fs from "node:fs";
2
- import fsp from "node:fs/promises";
3
2
  import path from "node:path";
4
3
  import { formatErrorDetail, shortPath } from "./error-detail.js";
5
4
  import { FsSafeError } from "./errors.js";
@@ -234,7 +233,7 @@ async function resolveRootPathLexicalAsync(params) {
234
233
  state.lexicalCursor = path.join(state.lexicalCursor, segment);
235
234
  let stat;
236
235
  try {
237
- stat = await fsp.lstat(state.lexicalCursor);
236
+ stat = fs.lstatSync(state.lexicalCursor);
238
237
  }
239
238
  catch (error) {
240
239
  if (handleLexicalLstatFailure(context, error, idx))
@@ -423,8 +422,8 @@ function buildResolvedRootPath(params) {
423
422
  async function getPathKind(absolutePath, preserveFinalSymlink) {
424
423
  try {
425
424
  const stat = preserveFinalSymlink
426
- ? await fsp.lstat(absolutePath)
427
- : await fsp.stat(absolutePath);
425
+ ? fs.lstatSync(absolutePath)
426
+ : fs.statSync(absolutePath);
428
427
  return { exists: true, kind: toResolvedKind(stat) };
429
428
  }
430
429
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import fsSync from "node:fs";
1
2
  import fs from "node:fs/promises";
2
3
  import path from "node:path";
3
4
  import { formatErrorDetail } from "./error-detail.js";
@@ -16,7 +17,7 @@ function pathStaysWithinRoot(rootDir, candidatePath) {
16
17
  }
17
18
  async function resolveRealPathIfExists(targetPath) {
18
19
  try {
19
- return await fs.realpath(targetPath);
20
+ return fsSync.realpathSync.native(targetPath);
20
21
  }
21
22
  catch {
22
23
  return undefined;
@@ -24,11 +25,11 @@ async function resolveRealPathIfExists(targetPath) {
24
25
  }
25
26
  async function resolveTrustedRootRealPath(rootDir) {
26
27
  try {
27
- const rootLstat = await fs.lstat(rootDir);
28
+ const rootLstat = fsSync.lstatSync(rootDir);
28
29
  if (!rootLstat.isDirectory() || rootLstat.isSymbolicLink()) {
29
30
  return undefined;
30
31
  }
31
- return await fs.realpath(rootDir);
32
+ return fsSync.realpathSync.native(rootDir);
32
33
  }
33
34
  catch {
34
35
  return undefined;
@@ -36,7 +37,7 @@ async function resolveTrustedRootRealPath(rootDir) {
36
37
  }
37
38
  async function validateCanonicalPathWithinRoot(params) {
38
39
  try {
39
- const candidateLstat = await fs.lstat(params.candidatePath);
40
+ const candidateLstat = fsSync.lstatSync(params.candidatePath);
40
41
  if (candidateLstat.isSymbolicLink()) {
41
42
  return "invalid";
42
43
  }
@@ -49,7 +50,7 @@ async function validateCanonicalPathWithinRoot(params) {
49
50
  if (params.expect === "file" && candidateLstat.nlink > 1) {
50
51
  return "invalid";
51
52
  }
52
- const candidateRealPath = await fs.realpath(params.candidatePath);
53
+ const candidateRealPath = fsSync.realpathSync.native(params.candidatePath);
53
54
  return isPathInside(params.rootRealPath, candidateRealPath) ? "ok" : "invalid";
54
55
  }
55
56
  catch (err) {
@@ -109,7 +110,7 @@ async function resolveNearestExistingPath(targetPath) {
109
110
  let current = path.resolve(targetPath);
110
111
  while (true) {
111
112
  try {
112
- await fs.lstat(current);
113
+ fsSync.lstatSync(current);
113
114
  return current;
114
115
  }
115
116
  catch (err) {
@@ -133,7 +134,7 @@ async function assertNoSymlinkSegments(params) {
133
134
  for (const segment of relative.split(path.sep).filter(Boolean)) {
134
135
  current = path.join(current, segment);
135
136
  try {
136
- const stat = await fs.lstat(current);
137
+ const stat = fsSync.lstatSync(current);
137
138
  if (stat.isSymbolicLink()) {
138
139
  throw new FsSafeError("symlink", `Invalid path: must not traverse symlinks within ${params.scopeLabel}`);
139
140
  }
@@ -163,14 +164,14 @@ export async function ensureDirectoryWithinRoot(params) {
163
164
  return lexical;
164
165
  const rootDir = path.resolve(params.rootDir);
165
166
  const targetPath = lexical.path;
166
- const rootStat = await fs.lstat(rootDir);
167
+ const rootStat = fsSync.lstatSync(rootDir);
167
168
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
168
169
  return invalidPath(scopeLabel);
169
170
  }
170
171
  await assertNoSymlinkSegments({ rootDir, targetPath, scopeLabel });
171
- const rootReal = await fs.realpath(rootDir);
172
+ const rootReal = fsSync.realpathSync.native(rootDir);
172
173
  const nearestExistingPath = await resolveNearestExistingPath(targetPath);
173
- const nearestExistingReal = await fs.realpath(nearestExistingPath);
174
+ const nearestExistingReal = fsSync.realpathSync.native(nearestExistingPath);
174
175
  if (!isPathInside(rootReal, nearestExistingReal)) {
175
176
  return invalidPath(scopeLabel);
176
177
  }
@@ -180,7 +181,7 @@ export async function ensureDirectoryWithinRoot(params) {
180
181
  current = path.join(current, segment);
181
182
  while (true) {
182
183
  try {
183
- const stat = await fs.lstat(current);
184
+ const stat = fsSync.lstatSync(current);
184
185
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
185
186
  return invalidPath(scopeLabel);
186
187
  }
@@ -201,12 +202,12 @@ export async function ensureDirectoryWithinRoot(params) {
201
202
  }
202
203
  }
203
204
  }
204
- const currentReal = await fs.realpath(current);
205
+ const currentReal = fsSync.realpathSync.native(current);
205
206
  if (!isPathInside(rootReal, currentReal)) {
206
207
  return invalidPath(scopeLabel);
207
208
  }
208
209
  }
209
- const targetReal = await fs.realpath(targetPath);
210
+ const targetReal = fsSync.realpathSync.native(targetPath);
210
211
  if (!isPathInside(rootReal, targetReal)) {
211
212
  return invalidPath(scopeLabel);
212
213
  }
@@ -308,7 +309,7 @@ async function resolveCheckedPathsWithinRoot(params, allowMissingFallback) {
308
309
  return lexicalPathResult;
309
310
  }
310
311
  try {
311
- const resolvedExistingPath = await fs.realpath(raw);
312
+ const resolvedExistingPath = fsSync.realpathSync.native(raw);
312
313
  const relativePath = path.relative(rootRealPath, resolvedExistingPath);
313
314
  if (!isInRoot(relativePath)) {
314
315
  return lexicalPathResult;
@@ -350,7 +351,7 @@ async function resolveCheckedPathsWithinRoot(params, allowMissingFallback) {
350
351
  scopeLabel: params.scopeLabel,
351
352
  });
352
353
  const existingPath = await resolveNearestExistingPath(pathResult.fallbackPath);
353
- const existingRealPath = await fs.realpath(existingPath);
354
+ const existingRealPath = fsSync.realpathSync.native(existingPath);
354
355
  if (!isPathInside(rootRealPath, existingRealPath)) {
355
356
  return invalidPath(params.scopeLabel);
356
357
  }
@@ -1 +1 @@
1
- {"version":3,"file":"root-write-mode.d.ts","sourceRoot":"","sources":["../src/root-write-mode.ts"],"names":[],"mappings":"AASA,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwClB"}
1
+ {"version":3,"file":"root-write-mode.d.ts","sourceRoot":"","sources":["../src/root-write-mode.ts"],"names":[],"mappings":"AAUA,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwClB"}
@@ -1,3 +1,4 @@
1
+ import fsSync from "node:fs";
1
2
  import fs from "node:fs/promises";
2
3
  import { FsSafeError } from "./errors.js";
3
4
  import { sameFileIdentity } from "./file-identity.js";
@@ -8,7 +9,7 @@ import { inspectFileIdentity } from "./strict-file-identity.js";
8
9
  // The caller has already resolved and guarded this write target.
9
10
  export async function inheritWriteTargetMode(params) {
10
11
  try {
11
- const existing = await inspectFileIdentity(() => fs.lstat(params.targetPath, { bigint: true }));
12
+ const existing = await inspectFileIdentity(() => fsSync.lstatSync(params.targetPath, { bigint: true }));
12
13
  if (existing.isSymbolicLink())
13
14
  throw new FsSafeError("path-alias", "path alias escape blocked");
14
15
  if (!existing.isFile())
@@ -22,7 +23,7 @@ export async function inheritWriteTargetMode(params) {
22
23
  const handle = await fs.open(params.targetPath, resolveReadOpenFlags());
23
24
  try {
24
25
  // Bind admission to the inode whose metadata is inherited.
25
- if (!sameFileIdentity(await handle.stat({ bigint: true }), existing)) {
26
+ if (!sameFileIdentity(fsSync.fstatSync(handle.fd, { bigint: true }), existing)) {
26
27
  throw new FsSafeError("path-mismatch", "write target changed during mode inheritance");
27
28
  }
28
29
  }
@@ -32,11 +33,11 @@ export async function inheritWriteTargetMode(params) {
32
33
  try {
33
34
  // A parent can change after guarded resolution. Do not inherit metadata
34
35
  // from an outside inode, even if the parent is restored before publication.
35
- const realPath = await fs.realpath(params.targetPath);
36
+ const realPath = fsSync.realpathSync.native(params.targetPath);
36
37
  if (!isPathInside(params.rootWithSep, realPath))
37
38
  throw outsideWorkspaceError();
38
39
  await inspectFileIdentity(async () => {
39
- const current = await fs.stat(realPath, { bigint: true });
40
+ const current = fsSync.statSync(realPath, { bigint: true });
40
41
  if (!current.isFile())
41
42
  throw new FsSafeError("not-file", "not a file");
42
43
  if (current.nlink > 1n)
@@ -46,16 +46,16 @@ export async function verifyAtomicWriteResult(params) {
46
46
  try {
47
47
  // This descriptor remains owned by the writer, even when final mode forbids opens.
48
48
  const stat = assertDescriptor();
49
- assertPath(await fs.lstat(params.targetPath, { bigint: true }));
49
+ assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
50
50
  const { realPath } = await resolveOpenedFileRealPathForFd(params.fd, stat, params.targetPath);
51
- assertPath(await fs.stat(realPath, { bigint: true }));
51
+ assertPath(fsSync.statSync(realPath, { bigint: true }));
52
52
  if (!isPathInside(params.root.rootWithSep, realPath)) {
53
53
  throw outsideWorkspaceError();
54
54
  }
55
55
  await assertAsyncDirectoryGuard(params.parentGuard);
56
56
  await assertRootIdentityCurrent(params.root);
57
57
  // Recheck after canonical resolution and directory checks, including late links.
58
- assertPath(await fs.lstat(params.targetPath, { bigint: true }));
58
+ assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
59
59
  assertDescriptor();
60
60
  if (needsPathOpen) {
61
61
  // A retained fd cannot prove that an opaque Windows pathname still names it.
@@ -73,15 +73,15 @@ export async function verifyAtomicWriteResult(params) {
73
73
  });
74
74
  try {
75
75
  const reopenedStat = assertDescriptor(opened.fd);
76
- assertPath(await fs.lstat(params.targetPath, { bigint: true }));
76
+ assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
77
77
  const { realPath: reopenedPath } = await resolveOpenedFileRealPathForFd(opened.fd, reopenedStat, params.targetPath);
78
- assertPath(await fs.stat(reopenedPath, { bigint: true }));
78
+ assertPath(fsSync.statSync(reopenedPath, { bigint: true }));
79
79
  if (!isPathInside(params.root.rootWithSep, reopenedPath)) {
80
80
  throw outsideWorkspaceError();
81
81
  }
82
82
  await assertAsyncDirectoryGuard(params.parentGuard);
83
83
  await assertRootIdentityCurrent(params.root);
84
- assertPath(await fs.lstat(params.targetPath, { bigint: true }));
84
+ assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
85
85
  assertDescriptor(opened.fd);
86
86
  }
87
87
  finally {
@@ -140,7 +140,7 @@ async function enforcePrivateDirectoryMode(params) {
140
140
  }
141
141
  async function inspectPrivateDirectory(directory, kind) {
142
142
  return await inspectFileIdentity(async () => {
143
- const stat = await fsp.lstat(directory, { bigint: true });
143
+ const stat = fs.lstatSync(directory, { bigint: true });
144
144
  if (stat.isSymbolicLink()) {
145
145
  throw new Error(`Private secret ${kind} ${directory} must not be a symlink.`);
146
146
  }
@@ -260,7 +260,7 @@ export async function prepareSecretFileWrite(params) {
260
260
  async function materializeSecretFileAtomic(params, createOnly) {
261
261
  const { mode, rootGuard, parentGuard, fileName, finalFilePath } = await prepareSecretFileWrite(params);
262
262
  try {
263
- const stat = await fsp.lstat(finalFilePath);
263
+ const stat = fs.lstatSync(finalFilePath);
264
264
  if (createOnly) {
265
265
  throw new FsSafeError("secret-exists", `Private secret file ${finalFilePath} already exists.`);
266
266
  }
@@ -1,6 +1,6 @@
1
1
  import type { BigIntStats } from "node:fs";
2
2
  type ExactFileIdentity = Pick<BigIntStats, "dev" | "ino">;
3
- export declare function inspectFileIdentity<T extends ExactFileIdentity>(inspect: () => Promise<T>, expected?: ExactFileIdentity, platform?: NodeJS.Platform): Promise<T>;
3
+ export declare function inspectFileIdentity<T extends ExactFileIdentity>(inspect: () => T | Promise<T>, expected?: ExactFileIdentity, platform?: NodeJS.Platform): Promise<T>;
4
4
  export declare function inspectFileIdentitySync<T extends ExactFileIdentity>(inspect: () => T, expected?: ExactFileIdentity, platform?: NodeJS.Platform): T;
5
5
  export {};
6
6
  //# sourceMappingURL=strict-file-identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA+B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,iBAAiB,EACjE,OAAO,EAAE,MAAM,CAAC,EAChB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,CAAC,CAOH"}
1
+ {"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA+B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC7B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,iBAAiB,EACjE,OAAO,EAAE,MAAM,CAAC,EAChB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,CAAC,CAOH"}
@@ -1 +1 @@
1
- {"version":3,"file":"symlink-parents.d.ts","sourceRoot":"","sources":["../src/symlink-parents.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAyBF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,6BAA6B,GACpC,IAAI,CA6BN"}
1
+ {"version":3,"file":"symlink-parents.d.ts","sourceRoot":"","sources":["../src/symlink-parents.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAyBF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,6BAA6B,GACpC,IAAI,CA6BN"}
@@ -1,5 +1,4 @@
1
1
  import fsSync from "node:fs";
2
- import fs from "node:fs/promises";
3
2
  import path from "node:path";
4
3
  import { FsSafeError } from "./errors.js";
5
4
  import { hasNodeErrorCode, isPathRelativeEscape } from "./path.js";
@@ -30,7 +29,7 @@ export async function assertNoSymlinkParents(params) {
30
29
  for (const [index, segment] of walk.segments.entries()) {
31
30
  current = path.join(current, segment);
32
31
  try {
33
- const stat = await fs.lstat(current);
32
+ const stat = fsSync.lstatSync(current);
34
33
  if (stat.isSymbolicLink()) {
35
34
  if (params.allowRootChildSymlink && path.dirname(current) === walk.root) {
36
35
  continue;