@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
@@ -1,123 +0,0 @@
1
- import type { Stats } from "node:fs";
2
- import type { FileHandle } from "node:fs/promises";
3
- import type { SafeDirEntry, SafePathStat } from "./types.js";
4
- export type SafeOpenResult = {
5
- handle: FileHandle;
6
- realPath: string;
7
- stat: Stats;
8
- };
9
- export type SafeLocalReadResult = {
10
- buffer: Buffer;
11
- realPath: string;
12
- stat: Stats;
13
- };
14
- export type SafeRootContext = {
15
- rootDir: string;
16
- rootReal: string;
17
- rootWithSep: string;
18
- };
19
- export type SafeRootOptions = {
20
- rootDir: string;
21
- defaults?: SafeRootDefaults;
22
- };
23
- export type SafeRootDefaults = {
24
- append?: SafeRootAppendOptions;
25
- copy?: SafeRootCopyOptions;
26
- /** openFile merges read defaults first, then open defaults, then call options. */
27
- open?: SafeRootOpenOptions;
28
- openWritable?: SafeRootOpenWritableOptions;
29
- read?: SafeRootReadOptions;
30
- write?: SafeRootWriteOptions;
31
- };
32
- export type SafeRootSymlinkPolicy = "reject" | "follow-within-root";
33
- export type SafeRootReadOptions = {
34
- rejectHardlinks?: boolean;
35
- nonBlockingRead?: boolean;
36
- symlinkPolicy?: SafeRootSymlinkPolicy;
37
- maxBytes?: number;
38
- };
39
- export type SafeRootOpenOptions = Omit<SafeRootReadOptions, "maxBytes">;
40
- export type SafeRootWriteOptions = {
41
- encoding?: BufferEncoding;
42
- mkdir?: boolean;
43
- };
44
- export type SafeRootOpenWritableOptions = {
45
- mkdir?: boolean;
46
- mode?: number;
47
- truncateExisting?: boolean;
48
- append?: boolean;
49
- };
50
- export type SafeRootCopyOptions = {
51
- maxBytes?: number;
52
- mkdir?: boolean;
53
- rejectSourceHardlinks?: boolean;
54
- };
55
- export type SafeRootWriteJsonOptions = SafeRootWriteOptions & {
56
- replacer?: Parameters<typeof JSON.stringify>[1];
57
- space?: Parameters<typeof JSON.stringify>[2];
58
- trailingNewline?: boolean;
59
- };
60
- export type SafeRootCreateOptions = SafeRootWriteOptions;
61
- export type SafeRootCreateJsonOptions = SafeRootWriteJsonOptions;
62
- export type SafeRootAppendOptions = {
63
- encoding?: BufferEncoding;
64
- mkdir?: boolean;
65
- prependNewlineIfNeeded?: boolean;
66
- };
67
- export declare class SafeRoot {
68
- readonly rootDir: string;
69
- readonly rootReal: string;
70
- readonly rootWithSep: string;
71
- readonly defaults: SafeRootDefaults;
72
- constructor(context: SafeRootContext, defaults?: SafeRootDefaults);
73
- private get context();
74
- resolve(relativePath: string): Promise<string>;
75
- openFile(relativePath: string, options?: SafeRootOpenOptions): Promise<SafeOpenResult>;
76
- readFile(relativePath: string, options?: SafeRootReadOptions): Promise<SafeLocalReadResult>;
77
- readFileBytes(relativePath: string, options?: SafeRootReadOptions): Promise<Buffer>;
78
- readText(relativePath: string, options?: SafeRootReadOptions & {
79
- encoding?: BufferEncoding;
80
- }): Promise<string>;
81
- readJson<T = unknown>(relativePath: string, options?: SafeRootReadOptions & {
82
- encoding?: BufferEncoding;
83
- }): Promise<T>;
84
- readPath(filePath: string, options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): Promise<SafeLocalReadResult>;
85
- reader(options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): (filePath: string) => Promise<Buffer>;
86
- openWritable(relativePath: string, options?: SafeRootOpenWritableOptions): Promise<SafeWritableOpenResult>;
87
- append(relativePath: string, data: string | Buffer, options?: SafeRootAppendOptions): Promise<void>;
88
- removeFile(relativePath: string): Promise<void>;
89
- mkdirp(relativePath: string, options?: {
90
- allowRoot?: boolean;
91
- }): Promise<void>;
92
- writeFile(relativePath: string, data: string | Buffer, options?: SafeRootWriteOptions): Promise<void>;
93
- createFile(relativePath: string, data: string | Buffer, options?: SafeRootCreateOptions): Promise<boolean>;
94
- writeJson(relativePath: string, data: unknown, options?: SafeRootWriteJsonOptions): Promise<void>;
95
- createJson(relativePath: string, data: unknown, options?: SafeRootCreateJsonOptions): Promise<boolean>;
96
- copyFileFrom(sourcePath: string, relativePath: string, options?: SafeRootCopyOptions): Promise<void>;
97
- statPath(relativePath: string): Promise<SafePathStat>;
98
- listDir(relativePath: string, options?: {
99
- withFileTypes?: false;
100
- }): Promise<string[]>;
101
- listDir(relativePath: string, options: {
102
- withFileTypes: true;
103
- }): Promise<SafeDirEntry[]>;
104
- move(from: string, to: string, options?: {
105
- overwrite?: boolean;
106
- }): Promise<void>;
107
- }
108
- export declare function safeRoot(rootDir: string, defaults?: SafeRootDefaults): Promise<SafeRoot>;
109
- export declare function readLocalFileSafely(params: {
110
- filePath: string;
111
- maxBytes?: number;
112
- }): Promise<SafeLocalReadResult>;
113
- export declare function openLocalFileSafely(params: {
114
- filePath: string;
115
- }): Promise<SafeOpenResult>;
116
- export type SafeWritableOpenResult = {
117
- handle: FileHandle;
118
- createdForWrite: boolean;
119
- realPath: string;
120
- stat: Stats;
121
- };
122
- export declare function resolveOpenedFileRealPathForHandle(handle: FileHandle, ioPath: string): Promise<string>;
123
- //# sourceMappingURL=safe-root.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"safe-root.d.ts","sourceRoot":"","sources":["../src/safe-root.ts"],"names":[],"mappings":"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"}
@@ -1,25 +0,0 @@
1
- export type SecureTempWorkspaceOptions = {
2
- rootDir: string;
3
- prefix: string;
4
- dirMode?: number;
5
- fileMode?: number;
6
- };
7
- export type SecureTempWorkspace = {
8
- dir: string;
9
- path(fileName: string): string;
10
- writePrivate(fileName: string, data: string | Uint8Array): Promise<string>;
11
- read(fileName: string): Promise<Buffer>;
12
- cleanup(): Promise<void>;
13
- };
14
- export type SecureTempWorkspaceSync = {
15
- dir: string;
16
- path(fileName: string): string;
17
- writePrivate(fileName: string, data: string | Uint8Array): string;
18
- read(fileName: string): Buffer;
19
- cleanup(): void;
20
- };
21
- export declare function createSecureTempWorkspace(options: SecureTempWorkspaceOptions): Promise<SecureTempWorkspace>;
22
- export declare function withSecureTempWorkspace<T>(options: SecureTempWorkspaceOptions, run: (workspace: SecureTempWorkspace) => Promise<T>): Promise<T>;
23
- export declare function createSecureTempWorkspaceSync(options: SecureTempWorkspaceOptions): SecureTempWorkspaceSync;
24
- export declare function withSecureTempWorkspaceSync<T>(options: SecureTempWorkspaceOptions, run: (workspace: SecureTempWorkspaceSync) => T): T;
25
- //# sourceMappingURL=secure-temp-workspace.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"secure-temp-workspace.d.ts","sourceRoot":"","sources":["../src/secure-temp-workspace.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAClE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAgDF,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CA0B9B;AAED,wBAAsB,uBAAuB,CAAC,CAAC,EAC7C,OAAO,EAAE,0BAA0B,EACnC,GAAG,EAAE,CAAC,SAAS,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,GAClD,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,0BAA0B,GAClC,uBAAuB,CAsCzB;AAED,wBAAgB,2BAA2B,CAAC,CAAC,EAC3C,OAAO,EAAE,0BAA0B,EACnC,GAAG,EAAE,CAAC,SAAS,EAAE,uBAAuB,KAAK,CAAC,GAC7C,CAAC,CAUH"}
@@ -1,136 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import fsSync from "node:fs";
3
- import fs from "node:fs/promises";
4
- import path from "node:path";
5
- function sanitizeTempPrefix(prefix) {
6
- const sanitized = prefix.trim().replace(/[^a-zA-Z0-9._-]/g, "-");
7
- if (!sanitized || sanitized === "." || sanitized === "..") {
8
- return "fs-safe-";
9
- }
10
- return sanitized.endsWith("-") ? sanitized : `${sanitized}-`;
11
- }
12
- function resolveWorkspaceLeaf(dir, fileName) {
13
- const raw = fileName.trim();
14
- if (!raw ||
15
- raw === "." ||
16
- raw === ".." ||
17
- raw.includes("\0") ||
18
- raw.includes("/") ||
19
- raw.includes("\\") ||
20
- path.basename(raw) !== raw) {
21
- throw new Error(`Invalid temp workspace file name: ${JSON.stringify(fileName)}`);
22
- }
23
- return path.join(dir, raw);
24
- }
25
- async function ensurePrivateDirectory(dir, mode) {
26
- await fs.mkdir(dir, { recursive: true, mode });
27
- const stat = await fs.lstat(dir);
28
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
29
- throw new Error(`Temp root must be a directory: ${dir}`);
30
- }
31
- await fs.chmod(dir, mode).catch(() => undefined);
32
- }
33
- function ensurePrivateDirectorySync(dir, mode) {
34
- fsSync.mkdirSync(dir, { recursive: true, mode });
35
- const stat = fsSync.lstatSync(dir);
36
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
37
- throw new Error(`Temp root must be a directory: ${dir}`);
38
- }
39
- try {
40
- fsSync.chmodSync(dir, mode);
41
- }
42
- catch {
43
- // Best-effort on platforms that do not enforce POSIX modes.
44
- }
45
- }
46
- export async function createSecureTempWorkspace(options) {
47
- const dirMode = options.dirMode ?? 0o700;
48
- const fileMode = options.fileMode ?? 0o600;
49
- const root = path.resolve(options.rootDir);
50
- await ensurePrivateDirectory(root, dirMode);
51
- const dir = await fs.mkdtemp(path.join(root, sanitizeTempPrefix(options.prefix)));
52
- await fs.chmod(dir, dirMode).catch(() => undefined);
53
- const stat = await fs.lstat(dir);
54
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
55
- throw new Error(`Temp workspace must be a directory: ${dir}`);
56
- }
57
- return {
58
- dir,
59
- path: (fileName) => resolveWorkspaceLeaf(dir, fileName),
60
- writePrivate: async (fileName, data) => {
61
- const filePath = resolveWorkspaceLeaf(dir, fileName);
62
- await fs.writeFile(filePath, data, { mode: fileMode, flag: "wx" });
63
- await fs.chmod(filePath, fileMode).catch(() => undefined);
64
- return filePath;
65
- },
66
- read: async (fileName) => await fs.readFile(resolveWorkspaceLeaf(dir, fileName)),
67
- cleanup: async () => {
68
- await fs.rm(dir, { recursive: true, force: true }).catch(() => undefined);
69
- },
70
- };
71
- }
72
- export async function withSecureTempWorkspace(options, run) {
73
- const workspace = await createSecureTempWorkspace({
74
- ...options,
75
- prefix: `${sanitizeTempPrefix(options.prefix)}${randomUUID()}-`,
76
- });
77
- try {
78
- return await run(workspace);
79
- }
80
- finally {
81
- await workspace.cleanup();
82
- }
83
- }
84
- export function createSecureTempWorkspaceSync(options) {
85
- const dirMode = options.dirMode ?? 0o700;
86
- const fileMode = options.fileMode ?? 0o600;
87
- const root = path.resolve(options.rootDir);
88
- ensurePrivateDirectorySync(root, dirMode);
89
- const dir = fsSync.mkdtempSync(path.join(root, sanitizeTempPrefix(options.prefix)));
90
- try {
91
- fsSync.chmodSync(dir, dirMode);
92
- }
93
- catch {
94
- // Best-effort on platforms that do not enforce POSIX modes.
95
- }
96
- const stat = fsSync.lstatSync(dir);
97
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
98
- throw new Error(`Temp workspace must be a directory: ${dir}`);
99
- }
100
- return {
101
- dir,
102
- path: (fileName) => resolveWorkspaceLeaf(dir, fileName),
103
- writePrivate: (fileName, data) => {
104
- const filePath = resolveWorkspaceLeaf(dir, fileName);
105
- fsSync.writeFileSync(filePath, data, { mode: fileMode, flag: "wx" });
106
- try {
107
- fsSync.chmodSync(filePath, fileMode);
108
- }
109
- catch {
110
- // Best-effort on platforms that do not enforce POSIX modes.
111
- }
112
- return filePath;
113
- },
114
- read: (fileName) => fsSync.readFileSync(resolveWorkspaceLeaf(dir, fileName)),
115
- cleanup: () => {
116
- try {
117
- fsSync.rmSync(dir, { recursive: true, force: true });
118
- }
119
- catch {
120
- // Best-effort cleanup.
121
- }
122
- },
123
- };
124
- }
125
- export function withSecureTempWorkspaceSync(options, run) {
126
- const workspace = createSecureTempWorkspaceSync({
127
- ...options,
128
- prefix: `${sanitizeTempPrefix(options.prefix)}${randomUUID()}-`,
129
- });
130
- try {
131
- return run(workspace);
132
- }
133
- finally {
134
- workspace.cleanup();
135
- }
136
- }
@@ -1,16 +0,0 @@
1
- export type WriteSiblingTempFileOptions<T> = {
2
- dir: string;
3
- writeTemp: (tempPath: string) => Promise<T>;
4
- resolveFinalPath: (result: T) => string;
5
- tempPrefix?: string;
6
- dirMode?: number;
7
- fileMode?: number;
8
- syncTempFile?: boolean;
9
- syncParentDir?: boolean;
10
- };
11
- export type WriteSiblingTempFileResult<T> = {
12
- filePath: string;
13
- result: T;
14
- };
15
- export declare function writeSiblingTempFile<T>(options: WriteSiblingTempFileOptions<T>): Promise<WriteSiblingTempFileResult<T>>;
16
- //# sourceMappingURL=sibling-temp-file.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sibling-temp-file.d.ts","sourceRoot":"","sources":["../src/sibling-temp-file.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5C,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAuCF,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CA+BxC"}
@@ -1,73 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- function buildTempPath(dir, tempPrefix) {
5
- return path.join(dir, `${tempPrefix ?? ".fs-safe-stream"}.${process.pid}.${randomUUID()}.tmp`);
6
- }
7
- async function syncFileBestEffort(filePath) {
8
- const handle = await fs.open(filePath, "r+");
9
- try {
10
- await handle.sync();
11
- }
12
- catch (error) {
13
- if (error.code !== "EPERM") {
14
- throw error;
15
- }
16
- }
17
- finally {
18
- await handle.close();
19
- }
20
- }
21
- async function syncDirectoryBestEffort(dirPath) {
22
- let handle;
23
- try {
24
- handle = await fs.open(dirPath, "r");
25
- await handle.sync();
26
- }
27
- catch {
28
- // Best-effort on platforms/filesystems that do not support directory fsync.
29
- }
30
- finally {
31
- await handle?.close().catch(() => undefined);
32
- }
33
- }
34
- function assertFinalPathIsSibling(dir, filePath) {
35
- const resolvedDir = path.resolve(dir);
36
- const resolvedFile = path.resolve(filePath);
37
- if (path.dirname(resolvedFile) !== resolvedDir) {
38
- throw new Error("Final path must be in the sibling temp directory.");
39
- }
40
- }
41
- export async function writeSiblingTempFile(options) {
42
- const dir = path.resolve(options.dir);
43
- await fs.mkdir(dir, { recursive: true, mode: options.dirMode ?? 0o700 });
44
- await fs.chmod(dir, options.dirMode ?? 0o700).catch(() => undefined);
45
- const tempPath = buildTempPath(dir, options.tempPrefix);
46
- let tempExists = false;
47
- try {
48
- tempExists = true;
49
- const result = await options.writeTemp(tempPath);
50
- if (options.fileMode !== undefined) {
51
- await fs.chmod(tempPath, options.fileMode).catch(() => undefined);
52
- }
53
- if (options.syncTempFile) {
54
- await syncFileBestEffort(tempPath);
55
- }
56
- const filePath = path.resolve(options.resolveFinalPath(result));
57
- assertFinalPathIsSibling(dir, filePath);
58
- await fs.rename(tempPath, filePath);
59
- tempExists = false;
60
- if (options.fileMode !== undefined) {
61
- await fs.chmod(filePath, options.fileMode).catch(() => undefined);
62
- }
63
- if (options.syncParentDir) {
64
- await syncDirectoryBestEffort(dir);
65
- }
66
- return { filePath, result };
67
- }
68
- finally {
69
- if (tempExists) {
70
- await fs.rm(tempPath, { force: true }).catch(() => undefined);
71
- }
72
- }
73
- }
@@ -1,8 +0,0 @@
1
- export declare function writeViaSiblingTempPath(params: {
2
- rootDir: string;
3
- targetPath: string;
4
- writeTemp: (tempPath: string) => Promise<void>;
5
- fallbackFileName?: string;
6
- tempPrefix?: string;
7
- }): Promise<void>;
8
- //# sourceMappingURL=sibling-temp-write.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sibling-temp-write.d.ts","sourceRoot":"","sources":["../src/sibling-temp-write.ts"],"names":[],"mappings":"AAmBA,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB"}
@@ -1,40 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { sanitizeUntrustedFileName } from "./filename.js";
5
- import { safeRoot } from "./safe-root.js";
6
- function buildSiblingTempPath(params) {
7
- const id = crypto.randomUUID();
8
- const safeTail = sanitizeUntrustedFileName(path.basename(params.targetPath), params.fallbackFileName);
9
- return path.join(path.dirname(params.targetPath), `${params.tempPrefix}${id}-${safeTail}.part`);
10
- }
11
- export async function writeViaSiblingTempPath(params) {
12
- const rootDir = await fs
13
- .realpath(path.resolve(params.rootDir))
14
- .catch(() => path.resolve(params.rootDir));
15
- const requestedTargetPath = path.resolve(params.targetPath);
16
- const targetPath = await fs
17
- .realpath(path.dirname(requestedTargetPath))
18
- .then((realDir) => path.join(realDir, path.basename(requestedTargetPath)))
19
- .catch(() => requestedTargetPath);
20
- const relativeTargetPath = path.relative(rootDir, targetPath);
21
- if (!relativeTargetPath ||
22
- relativeTargetPath === ".." ||
23
- relativeTargetPath.startsWith(`..${path.sep}`) ||
24
- path.isAbsolute(relativeTargetPath)) {
25
- throw new Error("Target path is outside the allowed root");
26
- }
27
- const tempPath = buildSiblingTempPath({
28
- targetPath,
29
- fallbackFileName: params.fallbackFileName ?? "output.bin",
30
- tempPrefix: params.tempPrefix ?? ".fs-safe-output-",
31
- });
32
- try {
33
- await params.writeTemp(tempPath);
34
- const root = await safeRoot(rootDir);
35
- await root.copyFileFrom(tempPath, relativeTargetPath, { mkdir: false });
36
- }
37
- finally {
38
- await fs.rm(tempPath, { force: true }).catch(() => { });
39
- }
40
- }