@openclaw/fs-safe 0.1.0 → 0.1.2

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 (114) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +23 -3
  3. package/dist/archive-staging.d.ts.map +1 -1
  4. package/dist/archive-staging.js +51 -7
  5. package/dist/archive.js +2 -2
  6. package/dist/file-store-boundary.d.ts +27 -0
  7. package/dist/file-store-boundary.d.ts.map +1 -0
  8. package/dist/file-store-boundary.js +164 -0
  9. package/dist/file-store.d.ts.map +1 -1
  10. package/dist/file-store.js +62 -72
  11. package/dist/path.d.ts +1 -0
  12. package/dist/path.d.ts.map +1 -1
  13. package/dist/path.js +6 -3
  14. package/dist/private-file-store.d.ts +5 -7
  15. package/dist/private-file-store.d.ts.map +1 -1
  16. package/dist/private-file-store.js +21 -34
  17. package/dist/root-context.d.ts +23 -0
  18. package/dist/root-context.d.ts.map +1 -0
  19. package/dist/root-context.js +62 -0
  20. package/dist/root-errors.d.ts +4 -0
  21. package/dist/root-errors.d.ts.map +1 -0
  22. package/dist/root-errors.js +21 -0
  23. package/dist/root-impl.d.ts +108 -0
  24. package/dist/root-impl.d.ts.map +1 -0
  25. package/dist/{safe-root.js → root-impl.js} +464 -251
  26. package/dist/root.d.ts +1 -107
  27. package/dist/root.d.ts.map +1 -1
  28. package/dist/root.js +1 -1332
  29. package/docs/advanced.md +100 -0
  30. package/docs/archive.md +199 -0
  31. package/docs/atomic.md +163 -0
  32. package/docs/config.md +65 -0
  33. package/docs/contributing.md +80 -0
  34. package/docs/errors.md +154 -0
  35. package/docs/file-store.md +213 -0
  36. package/docs/filename.md +0 -0
  37. package/docs/index.md +76 -0
  38. package/docs/install-path.md +167 -0
  39. package/docs/install.md +150 -0
  40. package/docs/json-store.md +200 -0
  41. package/docs/json.md +158 -0
  42. package/docs/local-roots.md +164 -0
  43. package/docs/path-scope.md +130 -0
  44. package/docs/path.md +191 -0
  45. package/docs/permissions.md +95 -0
  46. package/docs/private-file-store.md +45 -0
  47. package/docs/python-helper.md +103 -0
  48. package/docs/quickstart.md +147 -0
  49. package/docs/reading.md +174 -0
  50. package/docs/regular-file.md +175 -0
  51. package/docs/root.md +205 -0
  52. package/docs/secret-file.md +154 -0
  53. package/docs/secure-file.md +82 -0
  54. package/docs/security-model.md +99 -0
  55. package/docs/sidecar-lock.md +229 -0
  56. package/docs/store.md +44 -0
  57. package/docs/temp.md +276 -0
  58. package/docs/test-hooks.md +74 -0
  59. package/docs/testing.md +168 -0
  60. package/docs/timing.md +121 -0
  61. package/docs/types.md +155 -0
  62. package/docs/walk.md +63 -0
  63. package/docs/writing.md +222 -0
  64. package/package.json +5 -2
  65. package/dist/archive-utils.d.ts +0 -3
  66. package/dist/archive-utils.d.ts.map +0 -1
  67. package/dist/archive-utils.js +0 -26
  68. package/dist/boundary-file-read.d.ts +0 -44
  69. package/dist/boundary-file-read.d.ts.map +0 -1
  70. package/dist/boundary-file-read.js +0 -129
  71. package/dist/boundary-path.d.ts +0 -39
  72. package/dist/boundary-path.d.ts.map +0 -1
  73. package/dist/boundary-path.js +0 -598
  74. package/dist/fs-pinned-path-helper.d.ts +0 -7
  75. package/dist/fs-pinned-path-helper.d.ts.map +0 -1
  76. package/dist/fs-pinned-path-helper.js +0 -182
  77. package/dist/fs-pinned-write-helper.d.ts +0 -21
  78. package/dist/fs-pinned-write-helper.d.ts.map +0 -1
  79. package/dist/fs-pinned-write-helper.js +0 -263
  80. package/dist/hardlink-guards.d.ts +0 -7
  81. package/dist/hardlink-guards.d.ts.map +0 -1
  82. package/dist/hardlink-guards.js +0 -30
  83. package/dist/install-safe-path.d.ts +0 -20
  84. package/dist/install-safe-path.d.ts.map +0 -1
  85. package/dist/install-safe-path.js +0 -94
  86. package/dist/json-file.d.ts +0 -3
  87. package/dist/json-file.d.ts.map +0 -1
  88. package/dist/json-file.js +0 -123
  89. package/dist/json-files.d.ts +0 -20
  90. package/dist/json-files.d.ts.map +0 -1
  91. package/dist/json-files.js +0 -153
  92. package/dist/path-alias-guards.d.ts +0 -19
  93. package/dist/path-alias-guards.d.ts.map +0 -1
  94. package/dist/path-alias-guards.js +0 -21
  95. package/dist/path-guards.d.ts +0 -7
  96. package/dist/path-guards.d.ts.map +0 -1
  97. package/dist/path-guards.js +0 -49
  98. package/dist/path-safety.d.ts +0 -12
  99. package/dist/path-safety.d.ts.map +0 -1
  100. package/dist/path-safety.js +0 -50
  101. package/dist/safe-open-sync.d.ts +0 -24
  102. package/dist/safe-open-sync.d.ts.map +0 -1
  103. package/dist/safe-open-sync.js +0 -71
  104. package/dist/safe-root.d.ts +0 -123
  105. package/dist/safe-root.d.ts.map +0 -1
  106. package/dist/secure-temp-workspace.d.ts +0 -25
  107. package/dist/secure-temp-workspace.d.ts.map +0 -1
  108. package/dist/secure-temp-workspace.js +0 -136
  109. package/dist/sibling-temp-file.d.ts +0 -16
  110. package/dist/sibling-temp-file.d.ts.map +0 -1
  111. package/dist/sibling-temp-file.js +0 -73
  112. package/dist/sibling-temp-write.d.ts +0 -8
  113. package/dist/sibling-temp-write.d.ts.map +0 -1
  114. package/dist/sibling-temp-write.js +0 -40
package/dist/path.js CHANGED
@@ -22,6 +22,11 @@ export function isNodeError(value) {
22
22
  export function hasNodeErrorCode(value, code) {
23
23
  return isNodeError(value) && value.code === code;
24
24
  }
25
+ export function assertNoNulPathInput(filePath, message = "path contains a NUL byte") {
26
+ if (filePath.includes("\0")) {
27
+ throw new FsSafeError("invalid-path", message);
28
+ }
29
+ }
25
30
  export function isNotFoundPathError(value) {
26
31
  return isNodeError(value) && typeof value.code === "string" && NOT_FOUND_CODES.has(value.code);
27
32
  }
@@ -94,9 +99,7 @@ export function splitSafeRelativePath(relativePath) {
94
99
  if (relativePath.length === 0 || relativePath === ".") {
95
100
  return [];
96
101
  }
97
- if (relativePath.includes("\0")) {
98
- throw new FsSafeError("invalid-path", "relative path contains a NUL byte");
99
- }
102
+ assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
100
103
  if (relativePath.includes("\\")) {
101
104
  throw new FsSafeError("invalid-path", "relative path must use forward slashes");
102
105
  }
@@ -1,18 +1,16 @@
1
- import { type FileStore } from "./file-store.js";
2
- export type PrivateStateStoreOptions = {
1
+ export type PrivateFileStore = {
3
2
  rootDir: string;
4
- };
5
- export type PrivateStateStore = Omit<FileStore, "readText" | "readJson" | "writeText" | "writeJson"> & {
3
+ path(relativePath: string): string;
6
4
  readText(relativePath: string, options?: {
7
5
  maxBytes?: number;
8
6
  }): Promise<string | null>;
9
7
  readJson<T = unknown>(relativePath: string, options?: {
10
8
  maxBytes?: number;
11
9
  }): Promise<T | null>;
12
- writeText(relativePath: string, content: string | Uint8Array): Promise<string>;
10
+ writeText(relativePath: string, content: string | Uint8Array): Promise<void>;
13
11
  writeJson(relativePath: string, value: unknown, options?: {
14
12
  trailingNewline?: boolean;
15
- }): Promise<string>;
13
+ }): Promise<void>;
16
14
  };
17
15
  export declare function writePrivateTextAtomic(params: {
18
16
  rootDir: string;
@@ -56,5 +54,5 @@ export declare function writePrivateJsonAtomicSync(params: {
56
54
  value: unknown;
57
55
  trailingNewline?: boolean;
58
56
  }): void;
59
- export declare function privateStateStore(options: PrivateStateStoreOptions): PrivateStateStore;
57
+ export declare function privateFileStore(rootDir: string): PrivateFileStore;
60
58
  //# sourceMappingURL=private-file-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"private-file-store.d.ts","sourceRoot":"","sources":["../src/private-file-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"private-file-store.d.ts","sourceRoot":"","sources":["../src/private-file-store.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxF,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAChG,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzG,CAAC;AAgBF,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;CAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhB;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBzB;AAoBD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,GAAG,IAAI,CAiBhB;AAED,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAGpB;AAED,wBAAgB,mBAAmB,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,CAAC,GAAG,IAAI,CAGX;AAoCD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;CAC9B,GAAG,IAAI,CA4CP;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,IAAI,CAOP;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAiClE"}
@@ -2,11 +2,10 @@ import { randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import fs from "node:fs";
4
4
  import { FsSafeError } from "./errors.js";
5
- import { fileStore } from "./file-store.js";
6
5
  import { isPathInside } from "./path.js";
7
6
  import { readRegularFileSync } from "./regular-file.js";
8
7
  import { root } from "./root.js";
9
- import { writeSecretFileAtomic } from "./secret-file.js";
8
+ import { writePrivateSecretFileAtomic } from "./secret-file.js";
10
9
  function resolvePrivateStorePath(rootDir, relativePath) {
11
10
  const root = path.resolve(rootDir);
12
11
  const raw = relativePath.trim();
@@ -21,7 +20,7 @@ function resolvePrivateStorePath(rootDir, relativePath) {
21
20
  return resolved;
22
21
  }
23
22
  export async function writePrivateTextAtomic(params) {
24
- await writeSecretFileAtomic(params);
23
+ await writePrivateSecretFileAtomic(params);
25
24
  }
26
25
  export async function readPrivateText(params) {
27
26
  const rootDir = path.resolve(params.rootDir);
@@ -173,7 +172,7 @@ export function writePrivateTextAtomicSync(params) {
173
172
  }
174
173
  export async function writePrivateJsonAtomic(params) {
175
174
  const json = JSON.stringify(params.value, null, 2);
176
- await writeSecretFileAtomic({
175
+ await writePrivateSecretFileAtomic({
177
176
  rootDir: params.rootDir,
178
177
  filePath: params.filePath,
179
178
  content: params.trailingNewline && !json.endsWith("\n") ? `${json}\n` : json,
@@ -187,47 +186,35 @@ export function writePrivateJsonAtomicSync(params) {
187
186
  content: params.trailingNewline && !json.endsWith("\n") ? `${json}\n` : json,
188
187
  });
189
188
  }
190
- export function privateStateStore(options) {
191
- const rootDir = path.resolve(options.rootDir);
192
- const store = fileStore({ rootDir, private: true });
189
+ export function privateFileStore(rootDir) {
190
+ const root = path.resolve(rootDir);
193
191
  return {
194
- ...store,
195
- rootDir,
196
- path: (relativePath) => resolvePrivateStorePath(rootDir, relativePath),
197
- readText: async (relativePath, options) => {
198
- const safePath = resolvePrivateStorePath(rootDir, relativePath);
199
- return await readPrivateText({
200
- rootDir,
201
- filePath: safePath,
202
- maxBytes: options?.maxBytes,
203
- });
204
- },
205
- readJson: async (relativePath, options) => {
206
- const safePath = resolvePrivateStorePath(rootDir, relativePath);
207
- return await readPrivateJson({
208
- rootDir,
209
- filePath: safePath,
210
- maxBytes: options?.maxBytes,
211
- });
212
- },
192
+ rootDir: root,
193
+ path: (relativePath) => resolvePrivateStorePath(root, relativePath),
194
+ readText: async (relativePath, options) => await readPrivateText({
195
+ rootDir: root,
196
+ filePath: resolvePrivateStorePath(root, relativePath),
197
+ maxBytes: options?.maxBytes,
198
+ }),
199
+ readJson: async (relativePath, options) => await readPrivateJson({
200
+ rootDir: root,
201
+ filePath: resolvePrivateStorePath(root, relativePath),
202
+ maxBytes: options?.maxBytes,
203
+ }),
213
204
  writeText: async (relativePath, content) => {
214
- const safePath = resolvePrivateStorePath(rootDir, relativePath);
215
205
  await writePrivateTextAtomic({
216
- rootDir,
217
- filePath: safePath,
206
+ rootDir: root,
207
+ filePath: resolvePrivateStorePath(root, relativePath),
218
208
  content,
219
209
  });
220
- return safePath;
221
210
  },
222
211
  writeJson: async (relativePath, value, options) => {
223
- const safePath = resolvePrivateStorePath(rootDir, relativePath);
224
212
  await writePrivateJsonAtomic({
225
- rootDir,
226
- filePath: safePath,
213
+ rootDir: root,
214
+ filePath: resolvePrivateStorePath(root, relativePath),
227
215
  value,
228
216
  trailingNewline: options?.trailingNewline,
229
217
  });
230
- return safePath;
231
218
  },
232
219
  };
233
220
  }
@@ -0,0 +1,23 @@
1
+ export type RootContext = {
2
+ rootDir: string;
3
+ rootReal: string;
4
+ rootWithSep: string;
5
+ };
6
+ export declare const ensureTrailingSep: (value: string) => string;
7
+ export declare function assertValidRootRelativePath(relativePath: string): void;
8
+ export declare function expandRelativePathWithHome(relativePath: string): Promise<string>;
9
+ export declare function resolveRootContext(rootDir: string): Promise<RootContext>;
10
+ export declare function resolvePathInRoot(root: RootContext, relativePath: string): Promise<{
11
+ rootReal: string;
12
+ rootWithSep: string;
13
+ resolved: string;
14
+ }>;
15
+ export declare function resolvePathWithinRoot(params: {
16
+ rootDir: string;
17
+ relativePath: string;
18
+ }): Promise<{
19
+ rootReal: string;
20
+ rootWithSep: string;
21
+ resolved: string;
22
+ }>;
23
+ //# sourceMappingURL=root-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-context.d.ts","sourceRoot":"","sources":["../src/root-context.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,WACM,CAAC;AAEtD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAEtE;AAID,wBAAsB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYtF;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAuB9E;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAQtE;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEvE"}
@@ -0,0 +1,62 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { FsSafeError } from "./errors.js";
5
+ import { expandHomePrefix } from "./home-dir.js";
6
+ import { assertNoNulPathInput, isNotFoundPathError, isPathInside } from "./path.js";
7
+ export const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
8
+ export function assertValidRootRelativePath(relativePath) {
9
+ assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
10
+ }
11
+ let cachedHomePath;
12
+ export async function expandRelativePathWithHome(relativePath) {
13
+ const rawHome = process.env.HOME || process.env.USERPROFILE || os.homedir();
14
+ if (cachedHomePath?.raw !== rawHome) {
15
+ let realHome = rawHome;
16
+ try {
17
+ realHome = await fs.realpath(rawHome);
18
+ }
19
+ catch {
20
+ // If the home dir cannot be canonicalized, keep lexical expansion behavior.
21
+ }
22
+ cachedHomePath = { raw: rawHome, real: realHome };
23
+ }
24
+ return expandHomePrefix(relativePath, { home: cachedHomePath.real });
25
+ }
26
+ export async function resolveRootContext(rootDir) {
27
+ assertNoNulPathInput(rootDir, "root dir contains a NUL byte");
28
+ let rootReal;
29
+ try {
30
+ rootReal = await fs.realpath(rootDir);
31
+ const rootStat = await fs.stat(rootReal);
32
+ if (!rootStat.isDirectory()) {
33
+ throw new FsSafeError("invalid-path", "root dir is not a directory");
34
+ }
35
+ }
36
+ catch (err) {
37
+ if (err instanceof FsSafeError) {
38
+ throw err;
39
+ }
40
+ if (isNotFoundPathError(err)) {
41
+ throw new FsSafeError("not-found", "root dir not found");
42
+ }
43
+ throw err;
44
+ }
45
+ return {
46
+ rootDir: path.resolve(rootDir),
47
+ rootReal,
48
+ rootWithSep: ensureTrailingSep(rootReal),
49
+ };
50
+ }
51
+ export async function resolvePathInRoot(root, relativePath) {
52
+ assertValidRootRelativePath(relativePath);
53
+ const expanded = await expandRelativePathWithHome(relativePath);
54
+ const resolved = path.resolve(root.rootWithSep, expanded);
55
+ if (!isPathInside(root.rootWithSep, resolved)) {
56
+ throw new FsSafeError("outside-workspace", "file is outside workspace root");
57
+ }
58
+ return { rootReal: root.rootReal, rootWithSep: root.rootWithSep, resolved };
59
+ }
60
+ export async function resolvePathWithinRoot(params) {
61
+ return await resolvePathInRoot(await resolveRootContext(params.rootDir), params.relativePath);
62
+ }
@@ -0,0 +1,4 @@
1
+ export declare function isAlreadyExistsError(error: unknown): boolean;
2
+ export declare function normalizePinnedWriteError(error: unknown): Error;
3
+ export declare function normalizePinnedPathError(error: unknown): Error;
4
+ //# sourceMappingURL=root-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAGA,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAO/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAO9D"}
@@ -0,0 +1,21 @@
1
+ import { FsSafeError } from "./errors.js";
2
+ import { hasNodeErrorCode } from "./path.js";
3
+ export function isAlreadyExistsError(error) {
4
+ return hasNodeErrorCode(error, "EEXIST") || /File exists|EEXIST/i.test(String(error));
5
+ }
6
+ export function normalizePinnedWriteError(error) {
7
+ if (error instanceof FsSafeError) {
8
+ return error;
9
+ }
10
+ return new FsSafeError("invalid-path", "path is not a regular file under root", {
11
+ cause: error instanceof Error ? error : undefined,
12
+ });
13
+ }
14
+ export function normalizePinnedPathError(error) {
15
+ if (error instanceof FsSafeError) {
16
+ return error;
17
+ }
18
+ return new FsSafeError("path-alias", "path is not under root", {
19
+ cause: error instanceof Error ? error : undefined,
20
+ });
21
+ }
@@ -0,0 +1,108 @@
1
+ import type { Stats } from "node:fs";
2
+ import type { FileHandle } from "node:fs/promises";
3
+ import type { DirEntry, PathStat } from "./types.js";
4
+ export type OpenResult = {
5
+ handle: FileHandle;
6
+ realPath: string;
7
+ stat: Stats;
8
+ [Symbol.asyncDispose](): Promise<void>;
9
+ };
10
+ export type ReadResult = {
11
+ buffer: Buffer;
12
+ realPath: string;
13
+ stat: Stats;
14
+ };
15
+ export type RootOptions = {
16
+ rootDir: string;
17
+ defaults?: RootDefaults;
18
+ };
19
+ export type SymlinkPolicy = "reject" | "follow-within-root";
20
+ export type HardlinkPolicy = "reject" | "allow";
21
+ export type WritableOpenMode = "replace" | "append" | "update";
22
+ export type RootDefaults = {
23
+ hardlinks?: HardlinkPolicy;
24
+ maxBytes?: number;
25
+ mkdir?: boolean;
26
+ mode?: number;
27
+ nonBlockingRead?: boolean;
28
+ symlinks?: SymlinkPolicy;
29
+ };
30
+ export type RootReadOptions = Pick<RootDefaults, "hardlinks" | "maxBytes" | "nonBlockingRead" | "symlinks">;
31
+ export type RootOpenOptions = Omit<RootReadOptions, "maxBytes">;
32
+ export type RootWriteOptions = Pick<RootDefaults, "mkdir" | "mode"> & {
33
+ encoding?: BufferEncoding;
34
+ overwrite?: boolean;
35
+ };
36
+ export type RootOpenWritableOptions = Pick<RootDefaults, "mkdir" | "mode"> & {
37
+ writeMode?: WritableOpenMode;
38
+ };
39
+ export type RootCopyOptions = Pick<RootDefaults, "maxBytes" | "mkdir" | "mode"> & {
40
+ sourceHardlinks?: HardlinkPolicy;
41
+ };
42
+ export type RootWriteJsonOptions = RootWriteOptions & {
43
+ replacer?: Parameters<typeof JSON.stringify>[1];
44
+ space?: Parameters<typeof JSON.stringify>[2];
45
+ trailingNewline?: boolean;
46
+ };
47
+ export type RootCreateOptions = Omit<RootWriteOptions, "overwrite">;
48
+ export type RootCreateJsonOptions = Omit<RootWriteJsonOptions, "overwrite">;
49
+ export type RootAppendOptions = RootWriteOptions & {
50
+ prependNewlineIfNeeded?: boolean;
51
+ };
52
+ export declare const DEFAULT_ROOT_MAX_BYTES: number;
53
+ export interface Root {
54
+ readonly rootDir: string;
55
+ readonly rootReal: string;
56
+ readonly rootWithSep: string;
57
+ readonly defaults: RootDefaults;
58
+ resolve(relativePath: string): Promise<string>;
59
+ open(relativePath: string, options?: RootOpenOptions): Promise<OpenResult>;
60
+ read(relativePath: string, options?: RootReadOptions): Promise<ReadResult>;
61
+ readBytes(relativePath: string, options?: RootReadOptions): Promise<Buffer>;
62
+ readText(relativePath: string, options?: RootReadOptions & {
63
+ encoding?: BufferEncoding;
64
+ }): Promise<string>;
65
+ readJson<T = unknown>(relativePath: string, options?: RootReadOptions & {
66
+ encoding?: BufferEncoding;
67
+ }): Promise<T>;
68
+ readAbsolute(filePath: string, options?: RootReadOptions): Promise<ReadResult>;
69
+ reader(options?: RootReadOptions): (filePath: string) => Promise<Buffer>;
70
+ openWritable(relativePath: string, options?: RootOpenWritableOptions): Promise<WritableOpenResult>;
71
+ append(relativePath: string, data: string | Buffer, options?: RootAppendOptions): Promise<void>;
72
+ remove(relativePath: string): Promise<void>;
73
+ mkdir(relativePath: string): Promise<void>;
74
+ ensureRoot(): Promise<void>;
75
+ write(relativePath: string, data: string | Buffer, options?: RootWriteOptions): Promise<void>;
76
+ create(relativePath: string, data: string | Buffer, options?: RootCreateOptions): Promise<void>;
77
+ writeJson(relativePath: string, data: unknown, options?: RootWriteJsonOptions): Promise<void>;
78
+ createJson(relativePath: string, data: unknown, options?: RootCreateJsonOptions): Promise<void>;
79
+ copyIn(relativePath: string, sourcePath: string, options?: RootCopyOptions): Promise<void>;
80
+ exists(relativePath: string): Promise<boolean>;
81
+ stat(relativePath: string): Promise<PathStat>;
82
+ list(relativePath: string, options?: {
83
+ withFileTypes?: false;
84
+ }): Promise<string[]>;
85
+ list(relativePath: string, options: {
86
+ withFileTypes: true;
87
+ }): Promise<DirEntry[]>;
88
+ move(fromRelative: string, toRelative: string, options?: {
89
+ overwrite?: boolean;
90
+ }): Promise<void>;
91
+ }
92
+ export declare function root(rootDir: string, defaults?: RootDefaults): Promise<Root>;
93
+ export declare function readLocalFileSafely(params: {
94
+ filePath: string;
95
+ maxBytes?: number;
96
+ }): Promise<ReadResult>;
97
+ export declare function openLocalFileSafely(params: {
98
+ filePath: string;
99
+ }): Promise<OpenResult>;
100
+ export type WritableOpenResult = {
101
+ handle: FileHandle;
102
+ createdForWrite: boolean;
103
+ realPath: string;
104
+ stat: Stats;
105
+ [Symbol.asyncDispose](): Promise<void>;
106
+ };
107
+ export declare function resolveOpenedFileRealPathForHandle(handle: FileHandle, ioPath: string): Promise<string>;
108
+ //# sourceMappingURL=root-impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"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"}