@gobing-ai/ts-runtime 0.3.20 → 0.3.22
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.
- package/dist/file-system-cf.d.ts.map +1 -1
- package/dist/file-system-cf.js +3 -0
- package/dist/file-system-node.d.ts.map +1 -1
- package/dist/file-system-node.js +7 -4
- package/dist/file-system.d.ts +2 -0
- package/dist/file-system.d.ts.map +1 -1
- package/dist/fs.d.ts +16 -105
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +16 -243
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/process-executor.d.ts +2 -0
- package/dist/process-executor.d.ts.map +1 -1
- package/dist/process-executor.js +2 -0
- package/dist/schema-validation.d.ts +4 -4
- package/dist/schema-validation.js +3 -3
- package/package.json +3 -3
- package/src/file-system-cf.ts +4 -0
- package/src/file-system-node.ts +8 -3
- package/src/file-system.ts +3 -1
- package/src/fs.ts +39 -331
- package/src/index.ts +0 -10
- package/src/process-executor.ts +5 -0
- package/src/schema-validation.ts +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system-cf.d.ts","sourceRoot":"","sources":["../src/file-system-cf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhD;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,UAAU,
|
|
1
|
+
{"version":3,"file":"file-system-cf.d.ts","sourceRoot":"","sources":["../src/file-system-cf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhD;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,UAAU,CAkD/C"}
|
package/dist/file-system-cf.js
CHANGED
|
@@ -48,6 +48,9 @@ export function createCfFileSystem() {
|
|
|
48
48
|
deleteFile: (_path) => {
|
|
49
49
|
throw new Error(`FileSystem.deleteFile: ${UNSUPPORTED}`);
|
|
50
50
|
},
|
|
51
|
+
rename: (_src, _dest) => {
|
|
52
|
+
throw new Error(`FileSystem.rename: ${UNSUPPORTED}`);
|
|
53
|
+
},
|
|
51
54
|
createWriteStream: (_path) => {
|
|
52
55
|
throw new Error(`FileSystem.createWriteStream: ${UNSUPPORTED}`);
|
|
53
56
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system-node.d.ts","sourceRoot":"","sources":["../src/file-system-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"file-system-node.d.ts","sourceRoot":"","sources":["../src/file-system-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,CA2D9D;AAWD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAaxD"}
|
package/dist/file-system-node.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* `node:fs` fully, so this works on both runtimes without a Bun-specific
|
|
10
10
|
* variant.
|
|
11
11
|
*/
|
|
12
|
-
import { appendFileSync, cpSync, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
12
|
+
import { appendFileSync, cpSync, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
13
13
|
import { dirname, resolve as resolvePath } from 'node:path';
|
|
14
14
|
/**
|
|
15
15
|
* Create a {@link FileSystem} backed by `node:fs`.
|
|
@@ -38,13 +38,16 @@ export function createNodeFileSystem(root) {
|
|
|
38
38
|
deleteFile: (path) => {
|
|
39
39
|
rmSync(path, { recursive: true, force: true });
|
|
40
40
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return createWriteStream(path, { flags: 'a' });
|
|
41
|
+
rename: (src, dest) => {
|
|
42
|
+
renameSync(src, dest);
|
|
44
43
|
},
|
|
45
44
|
copy: (src, dest) => {
|
|
46
45
|
cpSync(src, dest, { recursive: true });
|
|
47
46
|
},
|
|
47
|
+
createWriteStream: (path) => {
|
|
48
|
+
ensureParentDir(path);
|
|
49
|
+
return createWriteStream(path, { flags: 'a' });
|
|
50
|
+
},
|
|
48
51
|
stat: (path) => {
|
|
49
52
|
try {
|
|
50
53
|
const s = statSync(path);
|
package/dist/file-system.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface FileSystem {
|
|
|
30
30
|
readDir(path: string): string[] | Promise<string[]>;
|
|
31
31
|
/** Delete a file or directory recursively. */
|
|
32
32
|
deleteFile(path: string): void | Promise<void>;
|
|
33
|
+
/** Atomically rename a file or directory. */
|
|
34
|
+
rename(src: string, dest: string): void | Promise<void>;
|
|
33
35
|
/** Recursively copy a file or directory. */
|
|
34
36
|
copy(src: string, dest: string): void | Promise<void>;
|
|
35
37
|
/** Get file or directory stats. Returns `null` if the path doesn't exist. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,WAAW,QAAQ;IACrB,MAAM,IAAI,OAAO,CAAC;IAClB,WAAW,IAAI,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACvB,mCAAmC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjD,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,kEAAkE;IAClE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/D,iEAAiE;IACjE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE,kFAAkF;IAClF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,2DAA2D;IAC3D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpD,8CAA8C;IAC9C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C,4CAA4C;IAC5C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,WAAW,QAAQ;IACrB,MAAM,IAAI,OAAO,CAAC;IAClB,WAAW,IAAI,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACvB,mCAAmC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjD,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,kEAAkE;IAClE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/D,iEAAiE;IACjE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE,kFAAkF;IAClF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,2DAA2D;IAC3D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpD,8CAA8C;IAC9C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C,6CAA6C;IAC7C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD,4CAA4C;IAC5C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,6EAA6E;IAC7E,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAE/D,6FAA6F;IAC7F,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,GAAG,IAAI,IAAI,CAAA;KAAE,CAAC;IAE7E,0DAA0D;IAC1D,OAAO,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAEvC,2CAA2C;IAC3C,cAAc,IAAI,MAAM,CAAC;CAC5B"}
|
package/dist/fs.d.ts
CHANGED
|
@@ -1,113 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
export interface FileStat {
|
|
3
|
-
isFile(): boolean;
|
|
4
|
-
isDirectory(): boolean;
|
|
5
|
-
size: number;
|
|
6
|
-
mtimeMs: number;
|
|
7
|
-
}
|
|
8
|
-
/** Write-only append stream for log output. */
|
|
9
|
-
export interface LogStream {
|
|
10
|
-
write(chunk: string): void;
|
|
11
|
-
end(): void;
|
|
12
|
-
}
|
|
13
|
-
/** @deprecated Use {@link import('./file-system').FileSystem} instead — the new interface has union return types and does not require a separate SyncFileSystem. */
|
|
14
|
-
export interface FileSystem {
|
|
15
|
-
readFile(path: string): Promise<string>;
|
|
16
|
-
writeFile(path: string, content: string): Promise<void>;
|
|
17
|
-
appendFile(path: string, content: string): Promise<void>;
|
|
18
|
-
mkdir(path: string): Promise<void>;
|
|
19
|
-
exists(path: string): Promise<boolean>;
|
|
20
|
-
readDir(path: string): Promise<string[]>;
|
|
21
|
-
unlink(path: string): Promise<void>;
|
|
22
|
-
stat(path: string): Promise<FileStat | null>;
|
|
23
|
-
realpath(path: string): Promise<string>;
|
|
24
|
-
copy(src: string, dest: string): Promise<void>;
|
|
25
|
-
rename(src: string, dest: string): Promise<void>;
|
|
26
|
-
createLogStream(path: string): LogStream;
|
|
27
|
-
}
|
|
28
|
-
/** @deprecated Use {@link import('./file-system').FileSystem} instead — its union return types make a separate sync interface unnecessary. */
|
|
29
|
-
export interface SyncFileSystem {
|
|
30
|
-
readFile(path: string): string;
|
|
31
|
-
writeFile(path: string, content: string): void;
|
|
32
|
-
mkdir(path: string): void;
|
|
33
|
-
exists(path: string): boolean;
|
|
34
|
-
readDir(path: string): string[];
|
|
35
|
-
stat(path: string): FileStat | null;
|
|
36
|
-
unlink(path: string): void;
|
|
37
|
-
}
|
|
38
|
-
/** {@link FileSystem} backed by `node:fs/promises`. Lazy-loads the module to avoid top-level import cost. */
|
|
39
|
-
/** @deprecated Use createNodeFileSystem() from './file-system-node' instead. */
|
|
40
|
-
export declare class NodeFileSystem implements FileSystem {
|
|
41
|
-
readFile(path: string): Promise<string>;
|
|
42
|
-
writeFile(path: string, content: string): Promise<void>;
|
|
43
|
-
appendFile(path: string, content: string): Promise<void>;
|
|
44
|
-
mkdir(path: string): Promise<void>;
|
|
45
|
-
exists(path: string): Promise<boolean>;
|
|
46
|
-
readDir(path: string): Promise<string[]>;
|
|
47
|
-
unlink(path: string): Promise<void>;
|
|
48
|
-
stat(path: string): Promise<FileStat | null>;
|
|
49
|
-
realpath(path: string): Promise<string>;
|
|
50
|
-
copy(src: string, dest: string): Promise<void>;
|
|
51
|
-
rename(src: string, dest: string): Promise<void>;
|
|
52
|
-
createLogStream(path: string): LogStream;
|
|
53
|
-
}
|
|
54
|
-
/** {@link SyncFileSystem} backed by `node:fs` synchronous APIs. */
|
|
55
|
-
/** @deprecated Use createNodeFileSystem() from './file-system-node' instead — its FileSystem interface has union return types, eliminating the need for a separate sync implementation. */
|
|
56
|
-
export declare class NodeSyncFileSystem implements SyncFileSystem {
|
|
57
|
-
readFile(path: string): string;
|
|
58
|
-
writeFile(path: string, content: string): void;
|
|
59
|
-
mkdir(path: string): void;
|
|
60
|
-
exists(path: string): boolean;
|
|
61
|
-
readDir(path: string): string[];
|
|
62
|
-
stat(path: string): FileStat | null;
|
|
63
|
-
unlink(path: string): void;
|
|
64
|
-
}
|
|
65
|
-
/** {@link FileSystem} stub for Cloudflare Workers — all file operations throw. Use D1, KV, or R2 instead. */
|
|
66
|
-
/** @deprecated Use createCfFileSystem() from './file-system-cf' instead. */
|
|
67
|
-
export declare class CloudflareFileSystem implements FileSystem {
|
|
68
|
-
readFile(path: string): Promise<string>;
|
|
69
|
-
writeFile(path: string, _content: string): Promise<void>;
|
|
70
|
-
appendFile(path: string, _content: string): Promise<void>;
|
|
71
|
-
mkdir(_path: string): Promise<void>;
|
|
72
|
-
exists(_path: string): Promise<boolean>;
|
|
73
|
-
readDir(path: string): Promise<string[]>;
|
|
74
|
-
unlink(path: string): Promise<void>;
|
|
75
|
-
stat(_path: string): Promise<FileStat | null>;
|
|
76
|
-
realpath(path: string): Promise<string>;
|
|
77
|
-
copy(src: string, _dest: string): Promise<void>;
|
|
78
|
-
rename(src: string, _dest: string): Promise<void>;
|
|
79
|
-
createLogStream(path: string): LogStream;
|
|
80
|
-
}
|
|
81
|
-
/** Swaps the active global file system, returning a restore function for the previous instance. */
|
|
82
|
-
/** @deprecated Use RuntimeFactory.createFileSystem() or ctx.require('fileSystem') instead. The global swap is replaced by factory-based DI. */
|
|
83
|
-
export declare function setFileSystem(fileSystem: FileSystem): () => void;
|
|
84
|
-
/** Returns the currently active global {@link FileSystem} instance. */
|
|
85
|
-
/** @deprecated Use RuntimeFactory.createFileSystem() or ctx.require('fileSystem') instead. */
|
|
86
|
-
export declare function getFs(): FileSystem;
|
|
1
|
+
import type { FileSystem as CanonicalFileSystem } from './file-system';
|
|
87
2
|
/** Creates parent directories for a file path before writing. */
|
|
88
|
-
export declare function ensureDirForFile(path: string, fs?:
|
|
89
|
-
/** Synchronous variant of {@link ensureDirForFile}. */
|
|
90
|
-
/** @deprecated The new createNodeFileSystem() handles parent-directory creation internally. */
|
|
91
|
-
export declare function ensureDirForFileSync(path: string, fs: SyncFileSystem): void;
|
|
3
|
+
export declare function ensureDirForFile(path: string, fs?: CanonicalFileSystem): Promise<void>;
|
|
92
4
|
/** Atomically writes a file by writing to a temp path then renaming, avoiding partial writes on crash. */
|
|
93
|
-
export declare function atomicWriteFile(path: string, content: string, fs?:
|
|
5
|
+
export declare function atomicWriteFile(path: string, content: string, fs?: CanonicalFileSystem): Promise<void>;
|
|
94
6
|
/** Atomically writes a value as JSON with trailing newline. */
|
|
95
|
-
export declare function atomicWriteJson(path: string, value: unknown, fs?:
|
|
7
|
+
export declare function atomicWriteJson(path: string, value: unknown, fs?: CanonicalFileSystem): Promise<void>;
|
|
96
8
|
/** Reads and parses a JSON file. */
|
|
97
|
-
export declare function readJsonFile<T = unknown>(path: string, fs?:
|
|
9
|
+
export declare function readJsonFile<T = unknown>(path: string, fs?: CanonicalFileSystem): Promise<T>;
|
|
98
10
|
/** Writes a value as JSON with 2-space indentation and trailing newline. */
|
|
99
|
-
export declare function writeJsonFile(path: string, value: unknown, fs?:
|
|
100
|
-
/**
|
|
101
|
-
|
|
11
|
+
export declare function writeJsonFile(path: string, value: unknown, fs?: CanonicalFileSystem): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Recursively walks a directory, returning sorted paths to all files,
|
|
14
|
+
* optionally excluding entries by name.
|
|
15
|
+
*/
|
|
16
|
+
export declare function walkDir(path: string, fs?: CanonicalFileSystem, exclude?: Set<string>): Promise<string[]>;
|
|
102
17
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* @deprecated Use {@link import('./file-system-node').findProjectRoot} (or `createNodeFileSystem().getProjectRoot()`).
|
|
106
|
-
* This delegates to the single shared implementation.
|
|
18
|
+
* Creates a writable stream for append-only output at the given path.
|
|
107
19
|
*/
|
|
108
|
-
export declare function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
export declare function createLogStream(path: string, fs?: FileSystem): LogStream;
|
|
20
|
+
export declare function createLogStream(path: string, fs?: CanonicalFileSystem): {
|
|
21
|
+
write(chunk: string): void;
|
|
22
|
+
end(): void;
|
|
23
|
+
};
|
|
113
24
|
//# sourceMappingURL=fs.d.ts.map
|
package/dist/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGvE,iEAAiE;AACjE,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAE,mBAA4C,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpH;AAED,0GAA0G;AAC1G,wBAAsB,eAAe,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,GAAE,mBAA4C,GACjD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,+DAA+D;AAC/D,wBAAsB,eAAe,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,EAAE,GAAE,mBAA4C,GACjD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,oCAAoC;AACpC,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAC1C,IAAI,EAAE,MAAM,EACZ,EAAE,GAAE,mBAA4C,GACjD,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED,4EAA4E;AAC5E,wBAAsB,aAAa,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,EAAE,GAAE,mBAA4C,GACjD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,OAAO,CACzB,IAAI,EAAE,MAAM,EACZ,EAAE,GAAE,mBAA4C,EAChD,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CAcnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC3B,IAAI,EAAE,MAAM,EACZ,EAAE,GAAE,mBAA4C,GACjD;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,IAAI,IAAI,CAAA;CAAE,CAE7C"}
|
package/dist/fs.js
CHANGED
|
@@ -1,247 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirnamePath, getProcessCwd, joinPath, resolvePath } from './path.js';
|
|
4
|
-
let fsPromisesModule = null;
|
|
5
|
-
let fsModule = null;
|
|
6
|
-
function nodeFsPromises() {
|
|
7
|
-
fsPromisesModule ??= import('node:fs/promises');
|
|
8
|
-
return fsPromisesModule;
|
|
9
|
-
}
|
|
10
|
-
function nodeFs() {
|
|
11
|
-
fsModule ??= import('node:fs');
|
|
12
|
-
return fsModule;
|
|
13
|
-
}
|
|
14
|
-
/** {@link FileSystem} backed by `node:fs/promises`. Lazy-loads the module to avoid top-level import cost. */
|
|
15
|
-
/** @deprecated Use createNodeFileSystem() from './file-system-node.js' instead. */
|
|
16
|
-
export class NodeFileSystem {
|
|
17
|
-
async readFile(path) {
|
|
18
|
-
const { readFile } = await nodeFsPromises();
|
|
19
|
-
return await readFile(path, 'utf-8');
|
|
20
|
-
}
|
|
21
|
-
async writeFile(path, content) {
|
|
22
|
-
const { writeFile } = await nodeFsPromises();
|
|
23
|
-
await ensureDirForFile(path, this);
|
|
24
|
-
await writeFile(path, content, 'utf-8');
|
|
25
|
-
}
|
|
26
|
-
async appendFile(path, content) {
|
|
27
|
-
const { appendFile } = await nodeFsPromises();
|
|
28
|
-
await ensureDirForFile(path, this);
|
|
29
|
-
await appendFile(path, content, 'utf-8');
|
|
30
|
-
}
|
|
31
|
-
async mkdir(path) {
|
|
32
|
-
const { mkdir } = await nodeFsPromises();
|
|
33
|
-
await mkdir(path, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
async exists(path) {
|
|
36
|
-
const { access } = await nodeFsPromises();
|
|
37
|
-
try {
|
|
38
|
-
await access(path);
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async readDir(path) {
|
|
46
|
-
const { readdir } = await nodeFsPromises();
|
|
47
|
-
return await readdir(path);
|
|
48
|
-
}
|
|
49
|
-
async unlink(path) {
|
|
50
|
-
const { rm } = await nodeFsPromises();
|
|
51
|
-
await rm(path, { recursive: true, force: true });
|
|
52
|
-
}
|
|
53
|
-
async stat(path) {
|
|
54
|
-
const { stat } = await nodeFsPromises();
|
|
55
|
-
try {
|
|
56
|
-
const value = await stat(path);
|
|
57
|
-
return {
|
|
58
|
-
isFile: () => value.isFile(),
|
|
59
|
-
isDirectory: () => value.isDirectory(),
|
|
60
|
-
size: value.size,
|
|
61
|
-
mtimeMs: value.mtimeMs,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
async realpath(path) {
|
|
69
|
-
const { realpath } = await nodeFsPromises();
|
|
70
|
-
return await realpath(path);
|
|
71
|
-
}
|
|
72
|
-
async copy(src, dest) {
|
|
73
|
-
const { cp } = await nodeFsPromises();
|
|
74
|
-
await cp(src, dest, { recursive: true });
|
|
75
|
-
}
|
|
76
|
-
async rename(src, dest) {
|
|
77
|
-
const { rename } = await nodeFsPromises();
|
|
78
|
-
await rename(src, dest);
|
|
79
|
-
}
|
|
80
|
-
createLogStream(path) {
|
|
81
|
-
return new LazyNodeLogStream(path);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/** {@link SyncFileSystem} backed by `node:fs` synchronous APIs. */
|
|
85
|
-
/** @deprecated Use createNodeFileSystem() from './file-system-node.js' instead — its FileSystem interface has union return types, eliminating the need for a separate sync implementation. */
|
|
86
|
-
export class NodeSyncFileSystem {
|
|
87
|
-
readFile(path) {
|
|
88
|
-
return readFileSync(path, 'utf-8');
|
|
89
|
-
}
|
|
90
|
-
writeFile(path, content) {
|
|
91
|
-
ensureDirForFileSync(path, this);
|
|
92
|
-
writeFileSync(path, content, 'utf-8');
|
|
93
|
-
}
|
|
94
|
-
mkdir(path) {
|
|
95
|
-
mkdirSync(path, { recursive: true });
|
|
96
|
-
}
|
|
97
|
-
exists(path) {
|
|
98
|
-
try {
|
|
99
|
-
return this.stat(path) !== null;
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
readDir(path) {
|
|
106
|
-
return readdirSync(path);
|
|
107
|
-
}
|
|
108
|
-
stat(path) {
|
|
109
|
-
try {
|
|
110
|
-
const value = statSync(path);
|
|
111
|
-
return {
|
|
112
|
-
isFile: () => value.isFile(),
|
|
113
|
-
isDirectory: () => value.isDirectory(),
|
|
114
|
-
size: value.size,
|
|
115
|
-
mtimeMs: value.mtimeMs,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
unlink(path) {
|
|
123
|
-
rmSync(path, { recursive: true, force: true });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
class LazyNodeLogStream {
|
|
127
|
-
ready;
|
|
128
|
-
ended = false;
|
|
129
|
-
// Single serialized chain: every write/end is appended here, so the underlying stream observes
|
|
130
|
-
// them in call order regardless of how the resolving microtasks interleave. A per-write `shift()`
|
|
131
|
-
// off a shared buffer (the previous approach) could reorder writes that arrived in the same tick.
|
|
132
|
-
tail;
|
|
133
|
-
constructor(path) {
|
|
134
|
-
this.ready = nodeFs().then(({ createWriteStream, mkdirSync }) => {
|
|
135
|
-
mkdirSync(dirnamePath(path), { recursive: true });
|
|
136
|
-
const stream = createWriteStream(path, { flags: 'a' });
|
|
137
|
-
return {
|
|
138
|
-
write: (chunk) => stream.write(chunk),
|
|
139
|
-
end: () => stream.end(),
|
|
140
|
-
};
|
|
141
|
-
});
|
|
142
|
-
this.tail = this.ready;
|
|
143
|
-
}
|
|
144
|
-
write(chunk) {
|
|
145
|
-
if (this.ended)
|
|
146
|
-
return;
|
|
147
|
-
this.tail = this.tail.then(() => this.ready.then((stream) => stream.write(chunk)));
|
|
148
|
-
}
|
|
149
|
-
end() {
|
|
150
|
-
if (this.ended)
|
|
151
|
-
return;
|
|
152
|
-
this.ended = true;
|
|
153
|
-
this.tail = this.tail.then(() => this.ready.then((stream) => stream.end()));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
const CLOUDFLARE_FS_ERROR = 'FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2.';
|
|
157
|
-
/** {@link FileSystem} stub for Cloudflare Workers — all file operations throw. Use D1, KV, or R2 instead. */
|
|
158
|
-
/** @deprecated Use createCfFileSystem() from './file-system-cf.js' instead. */
|
|
159
|
-
export class CloudflareFileSystem {
|
|
160
|
-
async readFile(path) {
|
|
161
|
-
throw unsupportedCloudflareFs('readFile', path);
|
|
162
|
-
}
|
|
163
|
-
async writeFile(path, _content) {
|
|
164
|
-
throw unsupportedCloudflareFs('writeFile', path);
|
|
165
|
-
}
|
|
166
|
-
async appendFile(path, _content) {
|
|
167
|
-
throw unsupportedCloudflareFs('appendFile', path);
|
|
168
|
-
}
|
|
169
|
-
async mkdir(_path) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
async exists(_path) {
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
async readDir(path) {
|
|
176
|
-
throw unsupportedCloudflareFs('readDir', path);
|
|
177
|
-
}
|
|
178
|
-
async unlink(path) {
|
|
179
|
-
throw unsupportedCloudflareFs('unlink', path);
|
|
180
|
-
}
|
|
181
|
-
async stat(_path) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
async realpath(path) {
|
|
185
|
-
return resolveProjectPath(path);
|
|
186
|
-
}
|
|
187
|
-
async copy(src, _dest) {
|
|
188
|
-
throw unsupportedCloudflareFs('copy', src);
|
|
189
|
-
}
|
|
190
|
-
async rename(src, _dest) {
|
|
191
|
-
throw unsupportedCloudflareFs('rename', src);
|
|
192
|
-
}
|
|
193
|
-
createLogStream(path) {
|
|
194
|
-
throw unsupportedCloudflareFs('createLogStream', path);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function unsupportedCloudflareFs(operation, path) {
|
|
198
|
-
return new Error(`CloudflareFileSystem.${operation} failed for "${path}": ${CLOUDFLARE_FS_ERROR}`);
|
|
199
|
-
}
|
|
200
|
-
let activeFileSystem = new NodeFileSystem();
|
|
201
|
-
/** Swaps the active global file system, returning a restore function for the previous instance. */
|
|
202
|
-
/** @deprecated Use RuntimeFactory.createFileSystem() or ctx.require('fileSystem') instead. The global swap is replaced by factory-based DI. */
|
|
203
|
-
export function setFileSystem(fileSystem) {
|
|
204
|
-
const previous = activeFileSystem;
|
|
205
|
-
activeFileSystem = fileSystem;
|
|
206
|
-
return () => {
|
|
207
|
-
activeFileSystem = previous;
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
/** Returns the currently active global {@link FileSystem} instance. */
|
|
211
|
-
/** @deprecated Use RuntimeFactory.createFileSystem() or ctx.require('fileSystem') instead. */
|
|
212
|
-
export function getFs() {
|
|
213
|
-
return activeFileSystem;
|
|
214
|
-
}
|
|
1
|
+
import { createNodeFileSystem } from './file-system-node.js';
|
|
2
|
+
import { dirnamePath, joinPath } from './path.js';
|
|
215
3
|
/** Creates parent directories for a file path before writing. */
|
|
216
|
-
export async function ensureDirForFile(path, fs =
|
|
217
|
-
await fs.
|
|
218
|
-
}
|
|
219
|
-
/** Synchronous variant of {@link ensureDirForFile}. */
|
|
220
|
-
/** @deprecated The new createNodeFileSystem() handles parent-directory creation internally. */
|
|
221
|
-
export function ensureDirForFileSync(path, fs) {
|
|
222
|
-
fs.mkdir(dirnamePath(path));
|
|
4
|
+
export async function ensureDirForFile(path, fs = createNodeFileSystem()) {
|
|
5
|
+
await fs.ensureDir(dirnamePath(path));
|
|
223
6
|
}
|
|
224
7
|
/** Atomically writes a file by writing to a temp path then renaming, avoiding partial writes on crash. */
|
|
225
|
-
export async function atomicWriteFile(path, content, fs =
|
|
8
|
+
export async function atomicWriteFile(path, content, fs = createNodeFileSystem()) {
|
|
226
9
|
await ensureDirForFile(path, fs);
|
|
227
10
|
const tempPath = `${path}.${getProcessPid()}.${uniqueToken()}.tmp`;
|
|
228
11
|
await fs.writeFile(tempPath, content);
|
|
229
12
|
await fs.rename(tempPath, path);
|
|
230
13
|
}
|
|
231
14
|
/** Atomically writes a value as JSON with trailing newline. */
|
|
232
|
-
export async function atomicWriteJson(path, value, fs =
|
|
15
|
+
export async function atomicWriteJson(path, value, fs = createNodeFileSystem()) {
|
|
233
16
|
await atomicWriteFile(path, `${JSON.stringify(value, null, 2)}\n`, fs);
|
|
234
17
|
}
|
|
235
18
|
/** Reads and parses a JSON file. */
|
|
236
|
-
export async function readJsonFile(path, fs =
|
|
19
|
+
export async function readJsonFile(path, fs = createNodeFileSystem()) {
|
|
237
20
|
return JSON.parse(await fs.readFile(path));
|
|
238
21
|
}
|
|
239
22
|
/** Writes a value as JSON with 2-space indentation and trailing newline. */
|
|
240
|
-
export async function writeJsonFile(path, value, fs =
|
|
23
|
+
export async function writeJsonFile(path, value, fs = createNodeFileSystem()) {
|
|
241
24
|
await fs.writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
242
25
|
}
|
|
243
|
-
/**
|
|
244
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Recursively walks a directory, returning sorted paths to all files,
|
|
28
|
+
* optionally excluding entries by name.
|
|
29
|
+
*/
|
|
30
|
+
export async function walkDir(path, fs = createNodeFileSystem(), exclude) {
|
|
245
31
|
const entries = (await fs.readDir(path)).sort();
|
|
246
32
|
const result = [];
|
|
247
33
|
for (const entry of entries) {
|
|
@@ -259,27 +45,14 @@ export async function walkDir(path, fs = getFs(), exclude) {
|
|
|
259
45
|
return result;
|
|
260
46
|
}
|
|
261
47
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* @deprecated Use {@link import('./file-system-node.js').findProjectRoot} (or `createNodeFileSystem().getProjectRoot()`).
|
|
265
|
-
* This delegates to the single shared implementation.
|
|
48
|
+
* Creates a writable stream for append-only output at the given path.
|
|
266
49
|
*/
|
|
267
|
-
export function
|
|
268
|
-
return
|
|
269
|
-
}
|
|
270
|
-
/** Resolves path segments relative to the project root. */
|
|
271
|
-
export function resolveProjectPath(...segments) {
|
|
272
|
-
return resolvePath(getProjectRoot(), ...segments);
|
|
273
|
-
}
|
|
274
|
-
/** Creates a {@link LogStream} at the given path using the active file system. */
|
|
275
|
-
export function createLogStream(path, fs = getFs()) {
|
|
276
|
-
return fs.createLogStream(path);
|
|
50
|
+
export function createLogStream(path, fs = createNodeFileSystem()) {
|
|
51
|
+
return fs.createWriteStream(path);
|
|
277
52
|
}
|
|
278
53
|
function getProcessPid() {
|
|
279
54
|
return globalThis.process?.pid ?? 0;
|
|
280
55
|
}
|
|
281
|
-
// Two writers to the same path in the same millisecond must not share a temp name, or one clobbers
|
|
282
|
-
// the other before rename. randomUUID disambiguates; Date.now keeps names sortable for debugging.
|
|
283
56
|
function uniqueToken() {
|
|
284
57
|
return `${Date.now()}.${crypto.randomUUID()}`;
|
|
285
58
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { D1NotConfiguredError } from './db-errors';
|
|
|
5
5
|
export type { FileStat, FileSystem } from './file-system';
|
|
6
6
|
export { createCfFileSystem } from './file-system-cf';
|
|
7
7
|
export { createNodeFileSystem, findProjectRoot } from './file-system-node';
|
|
8
|
-
export { atomicWriteFile, atomicWriteJson,
|
|
8
|
+
export { atomicWriteFile, atomicWriteJson, createLogStream, ensureDirForFile, readJsonFile, walkDir, writeJsonFile, } from './fs';
|
|
9
9
|
export * from './path';
|
|
10
10
|
export { _resetRuntimeFactory, isCloudflareWorkerRuntime, loadRuntimeFactory } from './platform';
|
|
11
11
|
export type { OutputPolicy, PipeProcess, PipeProcessOptions, ProcessEventDetail, ProcessEventSink, ProcessEvents, ProcessExecutorConfig, ProcessExitReason, ProcessOptions, ProcessResult, ProcessSignal, TracerPort, } from './process-executor';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,+BAA+B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACH,eAAe,EACf,eAAe,EACf,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,+BAA+B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACH,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,aAAa,GAChB,MAAM,MAAM,CAAC;AACd,cAAc,QAAQ,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EACR,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,aAAa,EACb,UAAU,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AAIxB,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAExG;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,cAAc,oBAAoB,EAAE,sBAAsB,CAAC,CAAC;AAE3G;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAAC,cAAc,oBAAoB,EAAE,qBAAqB,CAAC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { createRuntimeContextFromFactory } from './context.js';
|
|
|
4
4
|
export { D1NotConfiguredError } from './db-errors.js';
|
|
5
5
|
export { createCfFileSystem } from './file-system-cf.js';
|
|
6
6
|
export { createNodeFileSystem, findProjectRoot } from './file-system-node.js';
|
|
7
|
-
export { atomicWriteFile, atomicWriteJson,
|
|
7
|
+
export { atomicWriteFile, atomicWriteJson, createLogStream, ensureDirForFile, readJsonFile, walkDir, writeJsonFile, } from './fs.js';
|
|
8
8
|
export * from './path.js';
|
|
9
9
|
export { _resetRuntimeFactory, isCloudflareWorkerRuntime, loadRuntimeFactory } from './platform.js';
|
|
10
10
|
export { ProcessExecutor } from './process-executor.js';
|
|
@@ -24,6 +24,8 @@ export interface ProcessOptions {
|
|
|
24
24
|
label?: string;
|
|
25
25
|
rejectOnError?: boolean;
|
|
26
26
|
forceBuffered?: boolean;
|
|
27
|
+
/** AbortSignal forwarded to execa as `cancelSignal` — aborts the child process when fired. */
|
|
28
|
+
signal?: AbortSignal;
|
|
27
29
|
}
|
|
28
30
|
/** Result of a completed child process, including exit code, captured output, and duration. */
|
|
29
31
|
export interface ProcessResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-executor.d.ts","sourceRoot":"","sources":["../src/process-executor.ts"],"names":[],"mappings":"AAKA,uGAAuG;AACvG,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtF,sGAAsG;AACtG,MAAM,WAAW,qBAAqB;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;CACvB;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"process-executor.d.ts","sourceRoot":"","sources":["../src/process-executor.ts"],"names":[],"mappings":"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"}
|
package/dist/process-executor.js
CHANGED
|
@@ -29,6 +29,7 @@ export class ProcessExecutor {
|
|
|
29
29
|
rejectOnError: options.rejectOnError ?? false,
|
|
30
30
|
outputPolicy: this.config.output,
|
|
31
31
|
forceBuffered: options.forceBuffered ?? false,
|
|
32
|
+
signal: options.signal,
|
|
32
33
|
});
|
|
33
34
|
const startedAt = Date.now();
|
|
34
35
|
this.emitProcessEvent('process.started', {
|
|
@@ -291,6 +292,7 @@ function buildExecaOptions(opts) {
|
|
|
291
292
|
...(opts.env !== undefined ? { env: opts.env } : {}),
|
|
292
293
|
...(opts.timeout !== undefined ? { timeout: opts.timeout } : {}),
|
|
293
294
|
...(opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {}),
|
|
295
|
+
...(opts.signal !== undefined ? { cancelSignal: opts.signal } : {}),
|
|
294
296
|
};
|
|
295
297
|
}
|
|
296
298
|
function asString(value) {
|
|
@@ -35,10 +35,10 @@ export interface StructuredConfigLoadOptions {
|
|
|
35
35
|
*/
|
|
36
36
|
resolve?: (specifier: string, from: string) => string;
|
|
37
37
|
/**
|
|
38
|
-
* File system used to read the config and local schema files. Defaults to
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* File system used to read the config and local schema files. Defaults to
|
|
39
|
+
* `createNodeFileSystem()`; supply a {@link FileSystem} from the runtime factory
|
|
40
|
+
* to route reads through a different adapter or to inject a virtual file system
|
|
41
|
+
* in tests.
|
|
42
42
|
*/
|
|
43
43
|
fileSystem?: Pick<FileSystem, 'readFile'>;
|
|
44
44
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse as parseYaml } from 'yaml';
|
|
2
|
-
import {
|
|
2
|
+
import { createNodeFileSystem } from './file-system-node.js';
|
|
3
3
|
import { dirnamePath, getProcessCwd, isAbsolutePath, joinPath } from './path.js';
|
|
4
4
|
/** Default time budget for a single remote schema fetch. */
|
|
5
5
|
const REMOTE_SCHEMA_FETCH_TIMEOUT_MS = 5_000;
|
|
@@ -16,7 +16,7 @@ export class StructuredConfigSchemaError extends Error {
|
|
|
16
16
|
}
|
|
17
17
|
/** Reads a config file from disk, parses it (YAML or JSON), and validates against its declared `$schema`. */
|
|
18
18
|
export async function loadStructuredConfig(path, options = {}) {
|
|
19
|
-
const content = await (options.fileSystem ??
|
|
19
|
+
const content = await (options.fileSystem ?? createNodeFileSystem()).readFile(path);
|
|
20
20
|
return await parseStructuredConfig(content, path, options);
|
|
21
21
|
}
|
|
22
22
|
/** Parses a config string (YAML or JSON) and validates against its declared `$schema` if present. */
|
|
@@ -216,7 +216,7 @@ async function readSchema(schemaLocation, options) {
|
|
|
216
216
|
}
|
|
217
217
|
return await readBoundedBody(response, schemaLocation);
|
|
218
218
|
}
|
|
219
|
-
return await (options.fileSystem ??
|
|
219
|
+
return await (options.fileSystem ?? createNodeFileSystem()).readFile(schemaLocation);
|
|
220
220
|
}
|
|
221
221
|
/**
|
|
222
222
|
* Read a response body under a hard byte cap. `Content-Length` is a fast-path reject, but servers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-runtime",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"description": "@gobing-ai/ts-runtime — Runtime abstractions for Bun, Node, and Cloudflare Workers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -58,13 +58,13 @@
|
|
|
58
58
|
"release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-runtime-v<version> && git push --tags' && exit 1"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@gobing-ai/ts-utils": "^0.3.
|
|
61
|
+
"@gobing-ai/ts-utils": "^0.3.22",
|
|
62
62
|
"execa": "^9.5.0",
|
|
63
63
|
"yaml": "^2.7.0",
|
|
64
64
|
"zod": "^4.1.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@gobing-ai/ts-db": "^0.3.
|
|
67
|
+
"@gobing-ai/ts-db": "^0.3.22",
|
|
68
68
|
"@types/bun": "1.3.14"
|
|
69
69
|
},
|
|
70
70
|
"publishConfig": {
|
package/src/file-system-cf.ts
CHANGED
|
@@ -61,6 +61,10 @@ export function createCfFileSystem(): FileSystem {
|
|
|
61
61
|
throw new Error(`FileSystem.deleteFile: ${UNSUPPORTED}`);
|
|
62
62
|
},
|
|
63
63
|
|
|
64
|
+
rename: (_src: string, _dest: string): never => {
|
|
65
|
+
throw new Error(`FileSystem.rename: ${UNSUPPORTED}`);
|
|
66
|
+
},
|
|
67
|
+
|
|
64
68
|
createWriteStream: (_path: string): never => {
|
|
65
69
|
throw new Error(`FileSystem.createWriteStream: ${UNSUPPORTED}`);
|
|
66
70
|
},
|
package/src/file-system-node.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
mkdirSync,
|
|
19
19
|
readdirSync,
|
|
20
20
|
readFileSync,
|
|
21
|
+
renameSync,
|
|
21
22
|
rmSync,
|
|
22
23
|
statSync,
|
|
23
24
|
writeFileSync,
|
|
@@ -63,15 +64,19 @@ export function createNodeFileSystem(root?: string): FileSystem {
|
|
|
63
64
|
rmSync(path, { recursive: true, force: true });
|
|
64
65
|
},
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return createWriteStream(path, { flags: 'a' });
|
|
67
|
+
rename: (src: string, dest: string) => {
|
|
68
|
+
renameSync(src, dest);
|
|
69
69
|
},
|
|
70
70
|
|
|
71
71
|
copy: (src: string, dest: string) => {
|
|
72
72
|
cpSync(src, dest, { recursive: true });
|
|
73
73
|
},
|
|
74
74
|
|
|
75
|
+
createWriteStream: (path: string) => {
|
|
76
|
+
ensureParentDir(path);
|
|
77
|
+
return createWriteStream(path, { flags: 'a' });
|
|
78
|
+
},
|
|
79
|
+
|
|
75
80
|
stat: (path: string) => {
|
|
76
81
|
try {
|
|
77
82
|
const s = statSync(path);
|
package/src/file-system.ts
CHANGED
|
@@ -38,9 +38,11 @@ export interface FileSystem {
|
|
|
38
38
|
/** Delete a file or directory recursively. */
|
|
39
39
|
deleteFile(path: string): void | Promise<void>;
|
|
40
40
|
|
|
41
|
+
/** Atomically rename a file or directory. */
|
|
42
|
+
rename(src: string, dest: string): void | Promise<void>;
|
|
43
|
+
|
|
41
44
|
/** Recursively copy a file or directory. */
|
|
42
45
|
copy(src: string, dest: string): void | Promise<void>;
|
|
43
|
-
|
|
44
46
|
/** Get file or directory stats. Returns `null` if the path doesn't exist. */
|
|
45
47
|
stat(path: string): FileStat | null | Promise<FileStat | null>;
|
|
46
48
|
|
package/src/fs.ts
CHANGED
|
@@ -1,315 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirnamePath,
|
|
4
|
-
|
|
5
|
-
/** Portable file stat interface — mirrors the subset of `node:fs.Stats` used by the runtime. */
|
|
6
|
-
export interface FileStat {
|
|
7
|
-
isFile(): boolean;
|
|
8
|
-
isDirectory(): boolean;
|
|
9
|
-
size: number;
|
|
10
|
-
mtimeMs: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Write-only append stream for log output. */
|
|
14
|
-
export interface LogStream {
|
|
15
|
-
write(chunk: string): void;
|
|
16
|
-
end(): void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** @deprecated Use {@link import('./file-system').FileSystem} instead — the new interface has union return types and does not require a separate SyncFileSystem. */
|
|
20
|
-
export interface FileSystem {
|
|
21
|
-
readFile(path: string): Promise<string>;
|
|
22
|
-
writeFile(path: string, content: string): Promise<void>;
|
|
23
|
-
appendFile(path: string, content: string): Promise<void>;
|
|
24
|
-
mkdir(path: string): Promise<void>;
|
|
25
|
-
exists(path: string): Promise<boolean>;
|
|
26
|
-
readDir(path: string): Promise<string[]>;
|
|
27
|
-
unlink(path: string): Promise<void>;
|
|
28
|
-
stat(path: string): Promise<FileStat | null>;
|
|
29
|
-
realpath(path: string): Promise<string>;
|
|
30
|
-
copy(src: string, dest: string): Promise<void>;
|
|
31
|
-
rename(src: string, dest: string): Promise<void>;
|
|
32
|
-
createLogStream(path: string): LogStream;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** @deprecated Use {@link import('./file-system').FileSystem} instead — its union return types make a separate sync interface unnecessary. */
|
|
36
|
-
export interface SyncFileSystem {
|
|
37
|
-
readFile(path: string): string;
|
|
38
|
-
writeFile(path: string, content: string): void;
|
|
39
|
-
mkdir(path: string): void;
|
|
40
|
-
exists(path: string): boolean;
|
|
41
|
-
readDir(path: string): string[];
|
|
42
|
-
stat(path: string): FileStat | null;
|
|
43
|
-
unlink(path: string): void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
type NodeFsPromises = typeof import('node:fs/promises');
|
|
47
|
-
type NodeFs = typeof import('node:fs');
|
|
48
|
-
|
|
49
|
-
let fsPromisesModule: Promise<NodeFsPromises> | null = null;
|
|
50
|
-
let fsModule: Promise<NodeFs> | null = null;
|
|
51
|
-
|
|
52
|
-
function nodeFsPromises(): Promise<NodeFsPromises> {
|
|
53
|
-
fsPromisesModule ??= import('node:fs/promises');
|
|
54
|
-
return fsPromisesModule;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function nodeFs(): Promise<NodeFs> {
|
|
58
|
-
fsModule ??= import('node:fs');
|
|
59
|
-
return fsModule;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** {@link FileSystem} backed by `node:fs/promises`. Lazy-loads the module to avoid top-level import cost. */
|
|
63
|
-
/** @deprecated Use createNodeFileSystem() from './file-system-node' instead. */
|
|
64
|
-
export class NodeFileSystem implements FileSystem {
|
|
65
|
-
async readFile(path: string): Promise<string> {
|
|
66
|
-
const { readFile } = await nodeFsPromises();
|
|
67
|
-
return await readFile(path, 'utf-8');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async writeFile(path: string, content: string): Promise<void> {
|
|
71
|
-
const { writeFile } = await nodeFsPromises();
|
|
72
|
-
await ensureDirForFile(path, this);
|
|
73
|
-
await writeFile(path, content, 'utf-8');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async appendFile(path: string, content: string): Promise<void> {
|
|
77
|
-
const { appendFile } = await nodeFsPromises();
|
|
78
|
-
await ensureDirForFile(path, this);
|
|
79
|
-
await appendFile(path, content, 'utf-8');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async mkdir(path: string): Promise<void> {
|
|
83
|
-
const { mkdir } = await nodeFsPromises();
|
|
84
|
-
await mkdir(path, { recursive: true });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async exists(path: string): Promise<boolean> {
|
|
88
|
-
const { access } = await nodeFsPromises();
|
|
89
|
-
try {
|
|
90
|
-
await access(path);
|
|
91
|
-
return true;
|
|
92
|
-
} catch {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async readDir(path: string): Promise<string[]> {
|
|
98
|
-
const { readdir } = await nodeFsPromises();
|
|
99
|
-
return await readdir(path);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async unlink(path: string): Promise<void> {
|
|
103
|
-
const { rm } = await nodeFsPromises();
|
|
104
|
-
await rm(path, { recursive: true, force: true });
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async stat(path: string): Promise<FileStat | null> {
|
|
108
|
-
const { stat } = await nodeFsPromises();
|
|
109
|
-
try {
|
|
110
|
-
const value = await stat(path);
|
|
111
|
-
return {
|
|
112
|
-
isFile: () => value.isFile(),
|
|
113
|
-
isDirectory: () => value.isDirectory(),
|
|
114
|
-
size: value.size,
|
|
115
|
-
mtimeMs: value.mtimeMs,
|
|
116
|
-
};
|
|
117
|
-
} catch {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async realpath(path: string): Promise<string> {
|
|
123
|
-
const { realpath } = await nodeFsPromises();
|
|
124
|
-
return await realpath(path);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async copy(src: string, dest: string): Promise<void> {
|
|
128
|
-
const { cp } = await nodeFsPromises();
|
|
129
|
-
await cp(src, dest, { recursive: true });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async rename(src: string, dest: string): Promise<void> {
|
|
133
|
-
const { rename } = await nodeFsPromises();
|
|
134
|
-
await rename(src, dest);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
createLogStream(path: string): LogStream {
|
|
138
|
-
return new LazyNodeLogStream(path);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** {@link SyncFileSystem} backed by `node:fs` synchronous APIs. */
|
|
143
|
-
/** @deprecated Use createNodeFileSystem() from './file-system-node' instead — its FileSystem interface has union return types, eliminating the need for a separate sync implementation. */
|
|
144
|
-
export class NodeSyncFileSystem implements SyncFileSystem {
|
|
145
|
-
readFile(path: string): string {
|
|
146
|
-
return readFileSync(path, 'utf-8');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
writeFile(path: string, content: string): void {
|
|
150
|
-
ensureDirForFileSync(path, this);
|
|
151
|
-
writeFileSync(path, content, 'utf-8');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
mkdir(path: string): void {
|
|
155
|
-
mkdirSync(path, { recursive: true });
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
exists(path: string): boolean {
|
|
159
|
-
try {
|
|
160
|
-
return this.stat(path) !== null;
|
|
161
|
-
} catch {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
readDir(path: string): string[] {
|
|
167
|
-
return readdirSync(path);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
stat(path: string): FileStat | null {
|
|
171
|
-
try {
|
|
172
|
-
const value = statSync(path);
|
|
173
|
-
return {
|
|
174
|
-
isFile: () => value.isFile(),
|
|
175
|
-
isDirectory: () => value.isDirectory(),
|
|
176
|
-
size: value.size,
|
|
177
|
-
mtimeMs: value.mtimeMs,
|
|
178
|
-
};
|
|
179
|
-
} catch {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
unlink(path: string): void {
|
|
185
|
-
rmSync(path, { recursive: true, force: true });
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
class LazyNodeLogStream implements LogStream {
|
|
190
|
-
private readonly ready: Promise<{
|
|
191
|
-
write: (chunk: string) => void;
|
|
192
|
-
end: () => void;
|
|
193
|
-
}>;
|
|
194
|
-
private ended = false;
|
|
195
|
-
// Single serialized chain: every write/end is appended here, so the underlying stream observes
|
|
196
|
-
// them in call order regardless of how the resolving microtasks interleave. A per-write `shift()`
|
|
197
|
-
// off a shared buffer (the previous approach) could reorder writes that arrived in the same tick.
|
|
198
|
-
private tail: Promise<unknown>;
|
|
199
|
-
|
|
200
|
-
constructor(path: string) {
|
|
201
|
-
this.ready = nodeFs().then(({ createWriteStream, mkdirSync }) => {
|
|
202
|
-
mkdirSync(dirnamePath(path), { recursive: true });
|
|
203
|
-
const stream = createWriteStream(path, { flags: 'a' });
|
|
204
|
-
return {
|
|
205
|
-
write: (chunk: string) => stream.write(chunk),
|
|
206
|
-
end: () => stream.end(),
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
this.tail = this.ready;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
write(chunk: string): void {
|
|
213
|
-
if (this.ended) return;
|
|
214
|
-
this.tail = this.tail.then(() => this.ready.then((stream) => stream.write(chunk)));
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
end(): void {
|
|
218
|
-
if (this.ended) return;
|
|
219
|
-
this.ended = true;
|
|
220
|
-
this.tail = this.tail.then(() => this.ready.then((stream) => stream.end()));
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const CLOUDFLARE_FS_ERROR = 'FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2.';
|
|
225
|
-
|
|
226
|
-
/** {@link FileSystem} stub for Cloudflare Workers — all file operations throw. Use D1, KV, or R2 instead. */
|
|
227
|
-
/** @deprecated Use createCfFileSystem() from './file-system-cf' instead. */
|
|
228
|
-
export class CloudflareFileSystem implements FileSystem {
|
|
229
|
-
async readFile(path: string): Promise<string> {
|
|
230
|
-
throw unsupportedCloudflareFs('readFile', path);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async writeFile(path: string, _content: string): Promise<void> {
|
|
234
|
-
throw unsupportedCloudflareFs('writeFile', path);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async appendFile(path: string, _content: string): Promise<void> {
|
|
238
|
-
throw unsupportedCloudflareFs('appendFile', path);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async mkdir(_path: string): Promise<void> {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async exists(_path: string): Promise<boolean> {
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async readDir(path: string): Promise<string[]> {
|
|
250
|
-
throw unsupportedCloudflareFs('readDir', path);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
async unlink(path: string): Promise<void> {
|
|
254
|
-
throw unsupportedCloudflareFs('unlink', path);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
async stat(_path: string): Promise<FileStat | null> {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async realpath(path: string): Promise<string> {
|
|
262
|
-
return resolveProjectPath(path);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
async copy(src: string, _dest: string): Promise<void> {
|
|
266
|
-
throw unsupportedCloudflareFs('copy', src);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
async rename(src: string, _dest: string): Promise<void> {
|
|
270
|
-
throw unsupportedCloudflareFs('rename', src);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
createLogStream(path: string): LogStream {
|
|
274
|
-
throw unsupportedCloudflareFs('createLogStream', path);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function unsupportedCloudflareFs(operation: string, path: string): Error {
|
|
279
|
-
return new Error(`CloudflareFileSystem.${operation} failed for "${path}": ${CLOUDFLARE_FS_ERROR}`);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let activeFileSystem: FileSystem = new NodeFileSystem();
|
|
283
|
-
|
|
284
|
-
/** Swaps the active global file system, returning a restore function for the previous instance. */
|
|
285
|
-
/** @deprecated Use RuntimeFactory.createFileSystem() or ctx.require('fileSystem') instead. The global swap is replaced by factory-based DI. */
|
|
286
|
-
export function setFileSystem(fileSystem: FileSystem): () => void {
|
|
287
|
-
const previous = activeFileSystem;
|
|
288
|
-
activeFileSystem = fileSystem;
|
|
289
|
-
return () => {
|
|
290
|
-
activeFileSystem = previous;
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/** Returns the currently active global {@link FileSystem} instance. */
|
|
295
|
-
/** @deprecated Use RuntimeFactory.createFileSystem() or ctx.require('fileSystem') instead. */
|
|
296
|
-
export function getFs(): FileSystem {
|
|
297
|
-
return activeFileSystem;
|
|
298
|
-
}
|
|
299
|
-
|
|
1
|
+
import type { FileSystem as CanonicalFileSystem } from './file-system';
|
|
2
|
+
import { createNodeFileSystem } from './file-system-node';
|
|
3
|
+
import { dirnamePath, joinPath } from './path';
|
|
300
4
|
/** Creates parent directories for a file path before writing. */
|
|
301
|
-
export async function ensureDirForFile(path: string, fs =
|
|
302
|
-
await fs.
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/** Synchronous variant of {@link ensureDirForFile}. */
|
|
306
|
-
/** @deprecated The new createNodeFileSystem() handles parent-directory creation internally. */
|
|
307
|
-
export function ensureDirForFileSync(path: string, fs: SyncFileSystem): void {
|
|
308
|
-
fs.mkdir(dirnamePath(path));
|
|
5
|
+
export async function ensureDirForFile(path: string, fs: CanonicalFileSystem = createNodeFileSystem()): Promise<void> {
|
|
6
|
+
await fs.ensureDir(dirnamePath(path));
|
|
309
7
|
}
|
|
310
8
|
|
|
311
9
|
/** Atomically writes a file by writing to a temp path then renaming, avoiding partial writes on crash. */
|
|
312
|
-
export async function atomicWriteFile(
|
|
10
|
+
export async function atomicWriteFile(
|
|
11
|
+
path: string,
|
|
12
|
+
content: string,
|
|
13
|
+
fs: CanonicalFileSystem = createNodeFileSystem(),
|
|
14
|
+
): Promise<void> {
|
|
313
15
|
await ensureDirForFile(path, fs);
|
|
314
16
|
const tempPath = `${path}.${getProcessPid()}.${uniqueToken()}.tmp`;
|
|
315
17
|
await fs.writeFile(tempPath, content);
|
|
@@ -317,22 +19,40 @@ export async function atomicWriteFile(path: string, content: string, fs = getFs(
|
|
|
317
19
|
}
|
|
318
20
|
|
|
319
21
|
/** Atomically writes a value as JSON with trailing newline. */
|
|
320
|
-
export async function atomicWriteJson(
|
|
22
|
+
export async function atomicWriteJson(
|
|
23
|
+
path: string,
|
|
24
|
+
value: unknown,
|
|
25
|
+
fs: CanonicalFileSystem = createNodeFileSystem(),
|
|
26
|
+
): Promise<void> {
|
|
321
27
|
await atomicWriteFile(path, `${JSON.stringify(value, null, 2)}\n`, fs);
|
|
322
28
|
}
|
|
323
29
|
|
|
324
30
|
/** Reads and parses a JSON file. */
|
|
325
|
-
export async function readJsonFile<T = unknown>(
|
|
31
|
+
export async function readJsonFile<T = unknown>(
|
|
32
|
+
path: string,
|
|
33
|
+
fs: CanonicalFileSystem = createNodeFileSystem(),
|
|
34
|
+
): Promise<T> {
|
|
326
35
|
return JSON.parse(await fs.readFile(path)) as T;
|
|
327
36
|
}
|
|
328
37
|
|
|
329
38
|
/** Writes a value as JSON with 2-space indentation and trailing newline. */
|
|
330
|
-
export async function writeJsonFile(
|
|
39
|
+
export async function writeJsonFile(
|
|
40
|
+
path: string,
|
|
41
|
+
value: unknown,
|
|
42
|
+
fs: CanonicalFileSystem = createNodeFileSystem(),
|
|
43
|
+
): Promise<void> {
|
|
331
44
|
await fs.writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
332
45
|
}
|
|
333
46
|
|
|
334
|
-
/**
|
|
335
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Recursively walks a directory, returning sorted paths to all files,
|
|
49
|
+
* optionally excluding entries by name.
|
|
50
|
+
*/
|
|
51
|
+
export async function walkDir(
|
|
52
|
+
path: string,
|
|
53
|
+
fs: CanonicalFileSystem = createNodeFileSystem(),
|
|
54
|
+
exclude?: Set<string>,
|
|
55
|
+
): Promise<string[]> {
|
|
336
56
|
const entries = (await fs.readDir(path)).sort();
|
|
337
57
|
const result: string[] = [];
|
|
338
58
|
for (const entry of entries) {
|
|
@@ -349,31 +69,19 @@ export async function walkDir(path: string, fs = getFs(), exclude?: Set<string>)
|
|
|
349
69
|
}
|
|
350
70
|
|
|
351
71
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* @deprecated Use {@link import('./file-system-node').findProjectRoot} (or `createNodeFileSystem().getProjectRoot()`).
|
|
355
|
-
* This delegates to the single shared implementation.
|
|
72
|
+
* Creates a writable stream for append-only output at the given path.
|
|
356
73
|
*/
|
|
357
|
-
export function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
export function resolveProjectPath(...segments: string[]): string {
|
|
363
|
-
return resolvePath(getProjectRoot(), ...segments);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/** Creates a {@link LogStream} at the given path using the active file system. */
|
|
367
|
-
export function createLogStream(path: string, fs = getFs()): LogStream {
|
|
368
|
-
return fs.createLogStream(path);
|
|
74
|
+
export function createLogStream(
|
|
75
|
+
path: string,
|
|
76
|
+
fs: CanonicalFileSystem = createNodeFileSystem(),
|
|
77
|
+
): { write(chunk: string): void; end(): void } {
|
|
78
|
+
return fs.createWriteStream(path);
|
|
369
79
|
}
|
|
370
80
|
|
|
371
81
|
function getProcessPid(): number {
|
|
372
82
|
return (globalThis as { process?: { pid?: number } }).process?.pid ?? 0;
|
|
373
83
|
}
|
|
374
84
|
|
|
375
|
-
// Two writers to the same path in the same millisecond must not share a temp name, or one clobbers
|
|
376
|
-
// the other before rename. randomUUID disambiguates; Date.now keeps names sortable for debugging.
|
|
377
85
|
function uniqueToken(): string {
|
|
378
86
|
return `${Date.now()}.${crypto.randomUUID()}`;
|
|
379
87
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,19 +8,9 @@ export { createNodeFileSystem, findProjectRoot } from './file-system-node';
|
|
|
8
8
|
export {
|
|
9
9
|
atomicWriteFile,
|
|
10
10
|
atomicWriteJson,
|
|
11
|
-
CloudflareFileSystem,
|
|
12
11
|
createLogStream,
|
|
13
12
|
ensureDirForFile,
|
|
14
|
-
ensureDirForFileSync,
|
|
15
|
-
type FileSystem as LegacyFileSystem,
|
|
16
|
-
getFs,
|
|
17
|
-
getProjectRoot,
|
|
18
|
-
NodeFileSystem,
|
|
19
|
-
NodeSyncFileSystem,
|
|
20
13
|
readJsonFile,
|
|
21
|
-
resolveProjectPath,
|
|
22
|
-
type SyncFileSystem,
|
|
23
|
-
setFileSystem,
|
|
24
14
|
walkDir,
|
|
25
15
|
writeJsonFile,
|
|
26
16
|
} from './fs';
|
package/src/process-executor.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface ProcessOptions {
|
|
|
26
26
|
label?: string;
|
|
27
27
|
rejectOnError?: boolean;
|
|
28
28
|
forceBuffered?: boolean;
|
|
29
|
+
/** AbortSignal forwarded to execa as `cancelSignal` — aborts the child process when fired. */
|
|
30
|
+
signal?: AbortSignal;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/** Result of a completed child process, including exit code, captured output, and duration. */
|
|
@@ -130,6 +132,7 @@ export class ProcessExecutor {
|
|
|
130
132
|
rejectOnError: options.rejectOnError ?? false,
|
|
131
133
|
outputPolicy: this.config.output,
|
|
132
134
|
forceBuffered: options.forceBuffered ?? false,
|
|
135
|
+
signal: options.signal,
|
|
133
136
|
});
|
|
134
137
|
const startedAt = Date.now();
|
|
135
138
|
this.emitProcessEvent('process.started', {
|
|
@@ -442,6 +445,7 @@ function buildExecaOptions(opts: {
|
|
|
442
445
|
rejectOnError: boolean;
|
|
443
446
|
outputPolicy: OutputPolicy | undefined;
|
|
444
447
|
forceBuffered: boolean;
|
|
448
|
+
signal?: AbortSignal;
|
|
445
449
|
}): ExecaOptions {
|
|
446
450
|
const canStream =
|
|
447
451
|
!opts.forceBuffered &&
|
|
@@ -457,6 +461,7 @@ function buildExecaOptions(opts: {
|
|
|
457
461
|
...(opts.env !== undefined ? { env: opts.env } : {}),
|
|
458
462
|
...(opts.timeout !== undefined ? { timeout: opts.timeout } : {}),
|
|
459
463
|
...(opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {}),
|
|
464
|
+
...(opts.signal !== undefined ? { cancelSignal: opts.signal } : {}),
|
|
460
465
|
};
|
|
461
466
|
}
|
|
462
467
|
|
package/src/schema-validation.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parse as parseYaml } from 'yaml';
|
|
2
2
|
import type { FileSystem } from './file-system';
|
|
3
|
-
import {
|
|
3
|
+
import { createNodeFileSystem } from './file-system-node';
|
|
4
4
|
import { dirnamePath, getProcessCwd, isAbsolutePath, joinPath } from './path';
|
|
5
5
|
|
|
6
6
|
/** Default time budget for a single remote schema fetch. */
|
|
@@ -47,10 +47,10 @@ export interface StructuredConfigLoadOptions {
|
|
|
47
47
|
*/
|
|
48
48
|
resolve?: (specifier: string, from: string) => string;
|
|
49
49
|
/**
|
|
50
|
-
* File system used to read the config and local schema files. Defaults to
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
50
|
+
* File system used to read the config and local schema files. Defaults to
|
|
51
|
+
* `createNodeFileSystem()`; supply a {@link FileSystem} from the runtime factory
|
|
52
|
+
* to route reads through a different adapter or to inject a virtual file system
|
|
53
|
+
* in tests.
|
|
54
54
|
*/
|
|
55
55
|
fileSystem?: Pick<FileSystem, 'readFile'>;
|
|
56
56
|
}
|
|
@@ -68,7 +68,7 @@ export class StructuredConfigSchemaError extends Error {
|
|
|
68
68
|
|
|
69
69
|
/** Reads a config file from disk, parses it (YAML or JSON), and validates against its declared `$schema`. */
|
|
70
70
|
export async function loadStructuredConfig(path: string, options: StructuredConfigLoadOptions = {}): Promise<unknown> {
|
|
71
|
-
const content = await (options.fileSystem ??
|
|
71
|
+
const content = await (options.fileSystem ?? createNodeFileSystem()).readFile(path);
|
|
72
72
|
return await parseStructuredConfig(content, path, options);
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -360,7 +360,7 @@ async function readSchema(schemaLocation: string, options: StructuredConfigLoadO
|
|
|
360
360
|
}
|
|
361
361
|
return await readBoundedBody(response, schemaLocation);
|
|
362
362
|
}
|
|
363
|
-
return await (options.fileSystem ??
|
|
363
|
+
return await (options.fileSystem ?? createNodeFileSystem()).readFile(schemaLocation);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
/**
|