@openclaw/fs-safe 0.1.2 → 0.2.1

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 (120) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +39 -2
  3. package/dist/absolute-path.d.ts +14 -0
  4. package/dist/absolute-path.d.ts.map +1 -1
  5. package/dist/absolute-path.js +203 -2
  6. package/dist/advanced.d.ts +2 -1
  7. package/dist/advanced.d.ts.map +1 -1
  8. package/dist/advanced.js +2 -1
  9. package/dist/archive-staging.d.ts.map +1 -1
  10. package/dist/archive-staging.js +81 -7
  11. package/dist/archive.d.ts.map +1 -1
  12. package/dist/archive.js +25 -42
  13. package/dist/bounded-read-stream.d.ts +8 -0
  14. package/dist/bounded-read-stream.d.ts.map +1 -0
  15. package/dist/bounded-read-stream.js +20 -0
  16. package/dist/config.d.ts +1 -0
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +1 -0
  19. package/dist/directory-guard.d.ts +18 -0
  20. package/dist/directory-guard.d.ts.map +1 -0
  21. package/dist/directory-guard.js +70 -0
  22. package/dist/file-lock.d.ts +4 -2
  23. package/dist/file-lock.d.ts.map +1 -1
  24. package/dist/file-lock.js +13 -2
  25. package/dist/file-store-boundary.d.ts +2 -4
  26. package/dist/file-store-boundary.d.ts.map +1 -1
  27. package/dist/file-store-boundary.js +11 -7
  28. package/dist/file-store-prune.d.ts +12 -0
  29. package/dist/file-store-prune.d.ts.map +1 -0
  30. package/dist/file-store-prune.js +86 -0
  31. package/dist/file-store-source.d.ts +5 -0
  32. package/dist/file-store-source.d.ts.map +1 -0
  33. package/dist/file-store-source.js +30 -0
  34. package/dist/file-store.d.ts +2 -6
  35. package/dist/file-store.d.ts.map +1 -1
  36. package/dist/file-store.js +60 -53
  37. package/dist/guarded-mkdir.d.ts +6 -0
  38. package/dist/guarded-mkdir.d.ts.map +1 -0
  39. package/dist/guarded-mkdir.js +45 -0
  40. package/dist/guarded-mutation.d.ts +33 -0
  41. package/dist/guarded-mutation.d.ts.map +1 -0
  42. package/dist/guarded-mutation.js +76 -0
  43. package/dist/home-dir.d.ts.map +1 -1
  44. package/dist/home-dir.js +38 -33
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -0
  48. package/dist/json-document-store.d.ts +2 -0
  49. package/dist/json-document-store.d.ts.map +1 -1
  50. package/dist/json-document-store.js +7 -3
  51. package/dist/json-durable-queue.d.ts +45 -0
  52. package/dist/json-durable-queue.d.ts.map +1 -0
  53. package/dist/json-durable-queue.js +200 -0
  54. package/dist/json.d.ts +35 -5
  55. package/dist/json.d.ts.map +1 -1
  56. package/dist/json.js +69 -2
  57. package/dist/local-roots.d.ts.map +1 -1
  58. package/dist/local-roots.js +17 -3
  59. package/dist/lock-config.d.ts +10 -0
  60. package/dist/lock-config.d.ts.map +1 -0
  61. package/dist/lock-config.js +12 -0
  62. package/dist/move-path.d.ts +1 -0
  63. package/dist/move-path.d.ts.map +1 -1
  64. package/dist/move-path.js +195 -7
  65. package/dist/output.d.ts +13 -0
  66. package/dist/output.d.ts.map +1 -0
  67. package/dist/output.js +69 -0
  68. package/dist/path-stat.d.ts +4 -0
  69. package/dist/path-stat.d.ts.map +1 -0
  70. package/dist/path-stat.js +15 -0
  71. package/dist/path.d.ts.map +1 -1
  72. package/dist/path.js +4 -3
  73. package/dist/pinned-write.d.ts.map +1 -1
  74. package/dist/pinned-write.js +44 -19
  75. package/dist/private-temp-workspace.d.ts.map +1 -1
  76. package/dist/private-temp-workspace.js +28 -15
  77. package/dist/regular-file.d.ts.map +1 -1
  78. package/dist/regular-file.js +52 -8
  79. package/dist/replace-directory.d.ts.map +1 -1
  80. package/dist/replace-directory.js +5 -4
  81. package/dist/replace-file.d.ts +1 -1
  82. package/dist/replace-file.d.ts.map +1 -1
  83. package/dist/replace-file.js +93 -31
  84. package/dist/root-impl.d.ts.map +1 -1
  85. package/dist/root-impl.js +62 -53
  86. package/dist/safe-path-segment.d.ts +9 -0
  87. package/dist/safe-path-segment.d.ts.map +1 -0
  88. package/dist/safe-path-segment.js +51 -0
  89. package/dist/secret-file.d.ts.map +1 -1
  90. package/dist/secret-file.js +28 -7
  91. package/dist/sibling-temp.d.ts +1 -0
  92. package/dist/sibling-temp.d.ts.map +1 -1
  93. package/dist/sibling-temp.js +32 -7
  94. package/dist/sidecar-lock.d.ts +2 -0
  95. package/dist/sidecar-lock.d.ts.map +1 -1
  96. package/dist/sidecar-lock.js +93 -13
  97. package/dist/store.d.ts +1 -0
  98. package/dist/store.d.ts.map +1 -1
  99. package/dist/store.js +1 -0
  100. package/dist/temp-target.d.ts.map +1 -1
  101. package/dist/temp-target.js +7 -4
  102. package/dist/test-hooks.d.ts +6 -0
  103. package/dist/test-hooks.d.ts.map +1 -1
  104. package/dist/text-atomic.d.ts +7 -0
  105. package/dist/text-atomic.d.ts.map +1 -1
  106. package/dist/text-atomic.js +3 -2
  107. package/dist/trash.d.ts.map +1 -1
  108. package/dist/trash.js +48 -13
  109. package/docs/advanced.md +10 -0
  110. package/docs/atomic.md +33 -5
  111. package/docs/config.md +32 -3
  112. package/docs/index.md +3 -2
  113. package/docs/json-store.md +4 -0
  114. package/docs/json.md +34 -0
  115. package/docs/output.md +92 -0
  116. package/docs/sidecar-lock.md +13 -12
  117. package/docs/store.md +33 -0
  118. package/docs/temp.md +10 -2
  119. package/docs/testing.md +10 -0
  120. package/package.json +7 -2
package/dist/archive.js CHANGED
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import { constants as fsConstants } from "node:fs";
3
2
  import fs from "node:fs/promises";
4
3
  import path from "node:path";
@@ -10,7 +9,7 @@ import { resolveArchiveKind } from "./archive-kind.js";
10
9
  import { mergeExtractedTreeIntoDestination, prepareArchiveDestinationDir, prepareArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
11
10
  import { createTarEntryPreflightChecker, readTarEntryInfo, } from "./archive-tar.js";
12
11
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
13
- import { isNotFoundPathError } from "./path.js";
12
+ import { writeSiblingTempFile } from "./sibling-temp.js";
14
13
  import { withTimeout } from "./timing.js";
15
14
  export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
16
15
  export { resolveArchiveKind, resolvePackedRootDir } from "./archive-kind.js";
@@ -24,24 +23,6 @@ const OPEN_WRITE_CREATE_FLAGS = fsConstants.O_WRONLY |
24
23
  fsConstants.O_CREAT |
25
24
  fsConstants.O_EXCL |
26
25
  (SUPPORTS_NOFOLLOW ? fsConstants.O_NOFOLLOW : 0);
27
- async function cleanupPartialRegularFile(filePath) {
28
- let stat;
29
- try {
30
- stat = await fs.lstat(filePath);
31
- }
32
- catch (err) {
33
- if (isNotFoundPathError(err)) {
34
- return;
35
- }
36
- throw err;
37
- }
38
- if (stat.isFile()) {
39
- await fs.unlink(filePath).catch(() => undefined);
40
- }
41
- }
42
- function buildArchiveAtomicTempPath(targetPath) {
43
- return path.join(path.dirname(targetPath), `.${path.basename(targetPath)}.${process.pid}.${randomUUID()}.tmp`);
44
- }
45
26
  const ZIP_UNIX_FILE_TYPE_MASK = 0o170000;
46
27
  const ZIP_UNIX_SYMLINK_TYPE = 0o120000;
47
28
  function isZipSymlinkEntry(entry) {
@@ -80,23 +61,28 @@ async function writeZipFileEntry(params) {
80
61
  const readable = await readZipEntryStream(params.entry);
81
62
  const destinationPath = params.outPath;
82
63
  let tempHandle = null;
83
- let tempPath = null;
84
64
  let handleClosedByStream = false;
85
65
  try {
86
- tempPath = buildArchiveAtomicTempPath(destinationPath);
87
- tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, 0o666);
88
- const writable = tempHandle.createWriteStream();
89
- writable.once("close", () => {
90
- handleClosedByStream = true;
66
+ await writeSiblingTempFile({
67
+ dir: path.dirname(destinationPath),
68
+ tempPrefix: `.${path.basename(destinationPath)}.fs-safe-archive`,
69
+ chmodDir: false,
70
+ writeTemp: async (tempPath) => {
71
+ tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, 0o666);
72
+ const writable = tempHandle.createWriteStream();
73
+ writable.once("close", () => {
74
+ handleClosedByStream = true;
75
+ });
76
+ await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), writable);
77
+ if (!handleClosedByStream) {
78
+ await tempHandle.close().catch(() => undefined);
79
+ handleClosedByStream = true;
80
+ }
81
+ tempHandle = null;
82
+ return destinationPath;
83
+ },
84
+ resolveFinalPath: (filePath) => filePath,
91
85
  });
92
- await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), writable);
93
- if (!handleClosedByStream) {
94
- await tempHandle.close().catch(() => undefined);
95
- handleClosedByStream = true;
96
- }
97
- tempHandle = null;
98
- await fs.rename(tempPath, destinationPath);
99
- tempPath = null;
100
86
  // Best-effort permission restore for zip entries created on unix.
101
87
  if (typeof params.entry.unixPermissions === "number") {
102
88
  const mode = params.entry.unixPermissions & 0o777;
@@ -106,17 +92,14 @@ async function writeZipFileEntry(params) {
106
92
  }
107
93
  }
108
94
  catch (err) {
109
- if (tempPath) {
110
- await fs.rm(tempPath, { force: true }).catch(() => undefined);
111
- }
112
- else {
113
- await cleanupPartialRegularFile(destinationPath).catch(() => undefined);
114
- }
95
+ // Failures here happen before the temp has been committed. The destination
96
+ // parent may already be untrusted, so cleanup must stay limited to temp state.
115
97
  throw err;
116
98
  }
117
99
  finally {
118
- if (tempHandle && !handleClosedByStream) {
119
- await tempHandle.close().catch(() => undefined);
100
+ const openTempHandle = tempHandle;
101
+ if (openTempHandle && !handleClosedByStream) {
102
+ await openTempHandle.close().catch(() => undefined);
120
103
  }
121
104
  }
122
105
  }
@@ -0,0 +1,8 @@
1
+ import { Transform } from "node:stream";
2
+ export declare function createMaxBytesTransform(maxBytes: number): Transform;
3
+ export declare function createBoundedReadStream(opened: {
4
+ handle: {
5
+ createReadStream(): NodeJS.ReadableStream;
6
+ };
7
+ }, maxBytes: number | undefined): NodeJS.ReadableStream;
8
+ //# sourceMappingURL=bounded-read-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAkBnE;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE;IAAE,MAAM,EAAE;QAAE,gBAAgB,IAAI,MAAM,CAAC,cAAc,CAAA;KAAE,CAAA;CAAE,EACjE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAAC,cAAc,CAGvB"}
@@ -0,0 +1,20 @@
1
+ import { Transform } from "node:stream";
2
+ import { FsSafeError } from "./errors.js";
3
+ export function createMaxBytesTransform(maxBytes) {
4
+ let bytes = 0;
5
+ return new Transform({
6
+ transform(chunk, _encoding, callback) {
7
+ const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
8
+ bytes += buffer.byteLength;
9
+ if (bytes > maxBytes) {
10
+ callback(new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
11
+ return;
12
+ }
13
+ callback(null, buffer);
14
+ },
15
+ });
16
+ }
17
+ export function createBoundedReadStream(opened, maxBytes) {
18
+ const stream = opened.handle.createReadStream();
19
+ return maxBytes === undefined ? stream : stream.pipe(createMaxBytesTransform(maxBytes));
20
+ }
package/dist/config.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { configureFsSafePython, getFsSafePythonConfig, type FsSafePythonConfig, type FsSafePythonMode, } from "./pinned-python-config.js";
2
+ export { configureFsSafeLocks, getFsSafeLockConfig, type FsSafeLockConfig, } from "./lock-config.js";
2
3
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
package/dist/config.js CHANGED
@@ -1 +1,2 @@
1
1
  export { configureFsSafePython, getFsSafePythonConfig, } from "./pinned-python-config.js";
2
+ export { configureFsSafeLocks, getFsSafeLockConfig, } from "./lock-config.js";
@@ -0,0 +1,18 @@
1
+ import type { Stats } from "node:fs";
2
+ export type AsyncDirectoryGuard = {
3
+ dir: string;
4
+ realPath: string;
5
+ stat: Stats;
6
+ };
7
+ export type SyncDirectoryGuard = {
8
+ dir: string;
9
+ realPath: string;
10
+ stat: Stats;
11
+ };
12
+ export declare function createAsyncDirectoryGuard(dir: string): Promise<AsyncDirectoryGuard>;
13
+ export declare function assertAsyncDirectoryGuard(guard: AsyncDirectoryGuard): Promise<void>;
14
+ export declare function createSyncDirectoryGuard(dir: string): SyncDirectoryGuard;
15
+ export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard): void;
16
+ export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string): Promise<AsyncDirectoryGuard>;
17
+ export declare function createNearestExistingSyncDirectoryGuard(rootReal: string, targetPath: string): SyncDirectoryGuard;
18
+ //# sourceMappingURL=directory-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAQrC,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAMzF;AAED,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzF;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAMxE;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAQxE;AAED,wBAAsB,mCAAmC,CACvD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,mBAAmB,CAAC,CAc9B;AAED,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAcpB"}
@@ -0,0 +1,70 @@
1
+ import fsSync from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { FsSafeError } from "./errors.js";
5
+ import { sameFileIdentity } from "./file-identity.js";
6
+ import { isNotFoundPathError } from "./path.js";
7
+ export async function createAsyncDirectoryGuard(dir) {
8
+ const stat = await fs.lstat(dir);
9
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
10
+ throw new FsSafeError("not-file", "directory component must be a directory");
11
+ }
12
+ return { dir, realPath: await fs.realpath(dir), stat };
13
+ }
14
+ export async function assertAsyncDirectoryGuard(guard) {
15
+ const stat = await fs.lstat(guard.dir);
16
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
17
+ throw new FsSafeError("not-file", "directory component must be a directory");
18
+ }
19
+ if (!sameFileIdentity(stat, guard.stat) || (await fs.realpath(guard.dir)) !== guard.realPath) {
20
+ throw new FsSafeError("path-mismatch", "directory changed during operation");
21
+ }
22
+ }
23
+ export function createSyncDirectoryGuard(dir) {
24
+ const stat = fsSync.lstatSync(dir);
25
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
26
+ throw new FsSafeError("not-file", "directory component must be a directory");
27
+ }
28
+ return { dir, realPath: fsSync.realpathSync(dir), stat };
29
+ }
30
+ export function assertSyncDirectoryGuard(guard) {
31
+ const stat = fsSync.lstatSync(guard.dir);
32
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
33
+ throw new FsSafeError("not-file", "directory component must be a directory");
34
+ }
35
+ if (!sameFileIdentity(stat, guard.stat) || fsSync.realpathSync(guard.dir) !== guard.realPath) {
36
+ throw new FsSafeError("path-mismatch", "directory changed during operation");
37
+ }
38
+ }
39
+ export async function createNearestExistingDirectoryGuard(rootReal, targetPath) {
40
+ let current = path.resolve(targetPath);
41
+ const root = path.resolve(rootReal);
42
+ while (current !== root) {
43
+ try {
44
+ return await createAsyncDirectoryGuard(current);
45
+ }
46
+ catch (error) {
47
+ if (!isNotFoundPathError(error)) {
48
+ throw error;
49
+ }
50
+ current = path.dirname(current);
51
+ }
52
+ }
53
+ return await createAsyncDirectoryGuard(root);
54
+ }
55
+ export function createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
56
+ let current = path.resolve(targetPath);
57
+ const root = path.resolve(rootReal);
58
+ while (current !== root) {
59
+ try {
60
+ return createSyncDirectoryGuard(current);
61
+ }
62
+ catch (error) {
63
+ if (!isNotFoundPathError(error)) {
64
+ throw error;
65
+ }
66
+ current = path.dirname(current);
67
+ }
68
+ }
69
+ return createSyncDirectoryGuard(root);
70
+ }
@@ -1,7 +1,9 @@
1
- import { type SidecarLockAcquireOptions, type SidecarLockHandle, type SidecarLockHeldEntry, type SidecarLockRetryOptions } from "./sidecar-lock.js";
1
+ import { type SidecarLockAcquireOptions, type SidecarLockHandle, type SidecarLockHeldEntry, type SidecarLockRetryOptions, type SidecarLockStaleRecovery } from "./sidecar-lock.js";
2
2
  export type FileLockRetryOptions = SidecarLockRetryOptions;
3
- export type FileLockAcquireOptions<TPayload extends Record<string, unknown>> = Omit<SidecarLockAcquireOptions<TPayload>, "targetPath"> & {
3
+ export type FileLockStaleRecovery = SidecarLockStaleRecovery;
4
+ export type FileLockAcquireOptions<TPayload extends Record<string, unknown>> = Omit<SidecarLockAcquireOptions<TPayload>, "targetPath" | "staleMs"> & {
4
5
  managerKey?: string;
6
+ staleMs?: number;
5
7
  };
6
8
  export type FileLockHandle = SidecarLockHandle;
7
9
  export type FileLockHeldEntry = SidecarLockHeldEntry;
@@ -1 +1 @@
1
- {"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAE3D,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,IAAI,CACjF,yBAAyB,CAAC,QAAQ,CAAC,EACnC,YAAY,CACb,GAAG;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAC/C,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACxC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3B,QAAQ,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,IAAI,iBAAiB,EAAE,CAAC;CACpC,CAAC;AAMF,wBAAsB,eAAe,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACxC,OAAO,CAAC,cAAc,CAAC,CAGzB;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAelE;AAED,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAEzF"}
1
+ {"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC9B,MAAM,mBAAmB,CAAC;AAG3B,MAAM,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAC3D,MAAM,MAAM,qBAAqB,GAAG,wBAAwB,CAAC;AAE7D,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,IAAI,CACjF,yBAAyB,CAAC,QAAQ,CAAC,EACnC,YAAY,GAAG,SAAS,CACzB,GAAG;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAC/C,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAErD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACxC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3B,QAAQ,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,IAAI,iBAAiB,EAAE,CAAC;CACpC,CAAC;AAmBF,wBAAsB,eAAe,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GACxC,OAAO,CAAC,cAAc,CAAC,CAGzB;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAGZ;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAelE;AAED,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAEzF"}
package/dist/file-lock.js CHANGED
@@ -1,7 +1,18 @@
1
1
  import { createSidecarLockManager, } from "./sidecar-lock.js";
2
+ import { getFsSafeLockConfig } from "./lock-config.js";
2
3
  function resolveFileLockManagerKey(targetPath, managerKey) {
3
4
  return managerKey ?? `fs-safe.file-lock:${targetPath}`;
4
5
  }
6
+ function withLockDefaults(options) {
7
+ const defaults = getFsSafeLockConfig();
8
+ return {
9
+ ...options,
10
+ retry: options.retry ?? defaults.retry,
11
+ staleMs: options.staleMs ?? defaults.staleMs ?? 30_000,
12
+ staleRecovery: options.staleRecovery ?? defaults.staleRecovery,
13
+ timeoutMs: options.timeoutMs ?? defaults.timeoutMs,
14
+ };
15
+ }
5
16
  export async function acquireFileLock(targetPath, options) {
6
17
  return await createFileLockManager(resolveFileLockManagerKey(targetPath, options.managerKey))
7
18
  .acquire(targetPath, options);
@@ -15,11 +26,11 @@ export function createFileLockManager(key) {
15
26
  return {
16
27
  acquire: async (targetPath, options) => {
17
28
  const { managerKey: _managerKey, ...acquireOptions } = options;
18
- return await manager.acquire({ ...acquireOptions, targetPath });
29
+ return await manager.acquire({ ...withLockDefaults(acquireOptions), targetPath });
19
30
  },
20
31
  withLock: async (targetPath, options, fn) => {
21
32
  const { managerKey: _managerKey, ...acquireOptions } = options;
22
- return await manager.withLock({ ...acquireOptions, targetPath }, fn);
33
+ return await manager.withLock({ ...withLockDefaults(acquireOptions), targetPath }, fn);
23
34
  },
24
35
  drain: manager.drain,
25
36
  reset: manager.reset,
@@ -1,9 +1,7 @@
1
1
  import { type Readable } from "node:stream";
2
+ import { type SyncDirectoryGuard } from "./directory-guard.js";
2
3
  import { type Root } from "./root.js";
3
- export type SyncParentGuard = {
4
- dir: string;
5
- realPath: string;
6
- };
4
+ export type SyncParentGuard = SyncDirectoryGuard;
7
5
  export declare function ensureParentInRoot(scopedRoot: Root, relativePath: string, mode: number): Promise<void>;
8
6
  export declare function openWritableStoreRoot(params: {
9
7
  rootDir: string;
@@ -1 +1 @@
1
- {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAOF,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AA+CD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAuC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CASrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAwClB"}
1
+ {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AA+CD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAuC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAwClB"}
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { Transform } from "node:stream";
5
5
  import { pipeline } from "node:stream/promises";
6
+ import { assertSyncDirectoryGuard as assertDirectoryGuardSync, createSyncDirectoryGuard, } from "./directory-guard.js";
6
7
  import { FsSafeError } from "./errors.js";
7
8
  import { isPathInside } from "./path.js";
8
9
  import { resolveOpenedFileRealPathForHandle, root } from "./root.js";
@@ -106,13 +107,16 @@ export async function writeStreamToTempSource(params) {
106
107
  }
107
108
  }
108
109
  export function assertSyncDirectoryGuard(guard) {
109
- const stat = syncFs.lstatSync(guard.dir);
110
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
111
- throw new FsSafeError("not-file", `store directory component must be a directory: ${guard.dir}`);
110
+ try {
111
+ assertDirectoryGuardSync(guard);
112
112
  }
113
- const realPath = syncFs.realpathSync(guard.dir);
114
- if (realPath !== guard.realPath) {
115
- throw new FsSafeError("path-mismatch", "store directory changed during write");
113
+ catch (error) {
114
+ if (error instanceof FsSafeError && error.code === "path-mismatch") {
115
+ throw new FsSafeError("path-mismatch", "store directory changed during write", {
116
+ cause: error,
117
+ });
118
+ }
119
+ throw error;
116
120
  }
117
121
  }
118
122
  function chmodDirectorySyncBestEffort(dir, mode) {
@@ -158,7 +162,7 @@ export function ensureParentSync(params) {
158
162
  }
159
163
  chmodDirectorySyncBestEffort(current, params.mode);
160
164
  }
161
- const guard = { dir, realPath: syncFs.realpathSync(dir) };
165
+ const guard = createSyncDirectoryGuard(dir);
162
166
  assertSyncDirectoryGuard(guard);
163
167
  return guard;
164
168
  }
@@ -0,0 +1,12 @@
1
+ export type FileStorePruneOptions = {
2
+ ttlMs: number;
3
+ recursive?: boolean;
4
+ maxDepth?: number;
5
+ pruneEmptyDirs?: boolean;
6
+ };
7
+ export declare function pruneExpiredStoreEntries(params: {
8
+ rootDir: string;
9
+ dirMode: number;
10
+ options: FileStorePruneOptions;
11
+ }): Promise<void>;
12
+ //# sourceMappingURL=file-store-prune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store-prune.d.ts","sourceRoot":"","sources":["../src/file-store-prune.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,qBAAqB,CAAC;CAChC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhB"}
@@ -0,0 +1,86 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { isPathInside } from "./path.js";
5
+ import { root } from "./root.js";
6
+ import { getFsSafeTestHooks } from "./test-hooks.js";
7
+ export async function pruneExpiredStoreEntries(params) {
8
+ const now = Date.now();
9
+ const recursive = params.options.recursive ?? false;
10
+ const maxDepth = params.options.maxDepth;
11
+ const pruneEmptyDirs = (recursive || maxDepth !== undefined) && (params.options.pruneEmptyDirs ?? false);
12
+ await fs.mkdir(params.rootDir, { recursive: true, mode: params.dirMode });
13
+ const rootReal = await fs.realpath(params.rootDir);
14
+ const scopedRoot = await root(rootReal);
15
+ const rootGuard = {
16
+ dir: rootReal,
17
+ realPath: rootReal,
18
+ stat: await fs.lstat(rootReal),
19
+ };
20
+ async function assertRootGuard() {
21
+ const stat = await fs.lstat(rootGuard.dir);
22
+ if (stat.isSymbolicLink() ||
23
+ !stat.isDirectory() ||
24
+ stat.dev !== rootGuard.stat.dev ||
25
+ stat.ino !== rootGuard.stat.ino ||
26
+ (await fs.realpath(rootGuard.dir)) !== rootGuard.realPath) {
27
+ throw new FsSafeError("path-mismatch", "store root changed during prune");
28
+ }
29
+ }
30
+ async function readStableDirectory(dir) {
31
+ const before = await fs.lstat(dir).catch(() => null);
32
+ if (!before || before.isSymbolicLink() || !before.isDirectory()) {
33
+ return null;
34
+ }
35
+ const real = await fs.realpath(dir).catch(() => null);
36
+ if (!real || !isPathInside(rootReal, real)) {
37
+ return null;
38
+ }
39
+ const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => null);
40
+ if (!entries) {
41
+ return null;
42
+ }
43
+ const after = await fs.lstat(dir).catch(() => null);
44
+ if (!after || before.dev !== after.dev || before.ino !== after.ino) {
45
+ return null;
46
+ }
47
+ return entries;
48
+ }
49
+ async function pruneDir(dir, relativeDir, depth) {
50
+ const entries = await readStableDirectory(dir);
51
+ if (!entries) {
52
+ return false;
53
+ }
54
+ for (const entry of entries) {
55
+ const fullPath = path.join(dir, entry.name);
56
+ const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
57
+ const stat = await fs.lstat(fullPath).catch(() => null);
58
+ if (!stat || stat.isSymbolicLink()) {
59
+ continue;
60
+ }
61
+ if (stat.isDirectory()) {
62
+ const shouldDescend = maxDepth !== undefined ? depth < maxDepth : recursive;
63
+ if (shouldDescend) {
64
+ await getFsSafeTestHooks()?.beforeFileStorePruneDescend?.(fullPath);
65
+ }
66
+ if (shouldDescend && (await pruneDir(fullPath, relativePath, depth + 1))) {
67
+ await assertRootGuard();
68
+ // Keep empty-dir pruning on the same root-bounded remove path as files;
69
+ // the Root fallback handles empty directories without recursive delete.
70
+ await scopedRoot.remove(relativePath).catch(() => undefined);
71
+ }
72
+ continue;
73
+ }
74
+ if (stat.isFile() && now - stat.mtimeMs > params.options.ttlMs) {
75
+ await assertRootGuard();
76
+ await scopedRoot.remove(relativePath).catch(() => undefined);
77
+ }
78
+ }
79
+ if (!pruneEmptyDirs) {
80
+ return false;
81
+ }
82
+ const remaining = await readStableDirectory(dir);
83
+ return remaining !== null && remaining.length === 0;
84
+ }
85
+ await pruneDir(rootReal, "", 0);
86
+ }
@@ -0,0 +1,5 @@
1
+ export declare function readFileStoreCopySource(params: {
2
+ sourcePath: string;
3
+ maxBytes?: number;
4
+ }): Promise<Buffer>;
5
+ //# sourceMappingURL=file-store-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store-source.d.ts","sourceRoot":"","sources":["../src/file-store-source.ts"],"names":[],"mappings":"AAIA,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB"}
@@ -0,0 +1,30 @@
1
+ import fs from "node:fs/promises";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { readRegularFile } from "./regular-file.js";
4
+ export async function readFileStoreCopySource(params) {
5
+ const sourceStat = await fs.lstat(params.sourcePath);
6
+ if (sourceStat.isSymbolicLink() || !sourceStat.isFile()) {
7
+ throw new FsSafeError("not-file", "source path is not a file");
8
+ }
9
+ if (params.maxBytes !== undefined && sourceStat.size > params.maxBytes) {
10
+ throw new FsSafeError("too-large", `file exceeds maximum size of ${params.maxBytes} bytes`);
11
+ }
12
+ try {
13
+ return (await readRegularFile({ filePath: params.sourcePath, maxBytes: params.maxBytes }))
14
+ .buffer;
15
+ }
16
+ catch (error) {
17
+ const message = error instanceof Error ? error.message : String(error);
18
+ if (message.includes("regular file") || message.includes("not a regular file")) {
19
+ throw new FsSafeError("not-file", "source path is not a file", {
20
+ cause: error instanceof Error ? error : undefined,
21
+ });
22
+ }
23
+ if (params.maxBytes !== undefined && message.includes(`exceeds ${params.maxBytes} bytes`)) {
24
+ throw new FsSafeError("too-large", `file exceeds maximum size of ${params.maxBytes} bytes`, {
25
+ cause: error instanceof Error ? error : undefined,
26
+ });
27
+ }
28
+ throw error;
29
+ }
30
+ }
@@ -1,4 +1,6 @@
1
1
  import type { Readable } from "node:stream";
2
+ import { type FileStorePruneOptions } from "./file-store-prune.js";
3
+ export type { FileStorePruneOptions } from "./file-store-prune.js";
2
4
  import { type JsonFileStoreOptions, type JsonStore } from "./json-document-store.js";
3
5
  import { type OpenResult, type ReadResult, type Root, type RootReadOptions } from "./root.js";
4
6
  export type FileStoreOptions = {
@@ -17,12 +19,6 @@ export type FileStoreWriteOptions = {
17
19
  export type FileStoreReadOptions = RootReadOptions & {
18
20
  encoding?: BufferEncoding;
19
21
  };
20
- export type FileStorePruneOptions = {
21
- ttlMs: number;
22
- recursive?: boolean;
23
- maxDepth?: number;
24
- pruneEmptyDirs?: boolean;
25
- };
26
22
  export type FileStore = {
27
23
  readonly rootDir: string;
28
24
  path(relativePath: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"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"}