@gobing-ai/ts-runtime 0.2.7 → 0.2.9
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/README.md +159 -20
- package/dist/config.d.ts +10 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +35 -58
- package/dist/context.d.ts +1 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -2
- package/dist/fs.d.ts +15 -0
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +97 -13
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/path.d.ts +7 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +55 -0
- package/dist/process-executor.d.ts +36 -0
- package/dist/process-executor.d.ts.map +1 -1
- package/dist/process-executor.js +74 -0
- package/dist/schema-validation.d.ts +42 -0
- package/dist/schema-validation.d.ts.map +1 -0
- package/dist/schema-validation.js +304 -0
- package/dist/types.d.ts +0 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/config.ts +41 -57
- package/src/context.ts +2 -4
- package/src/fs.ts +124 -25
- package/src/index.ts +2 -0
- package/src/path.ts +54 -0
- package/src/process-executor.ts +128 -0
- package/src/schema-validation.ts +442 -0
- package/src/types.ts +0 -10
package/dist/fs.js
CHANGED
|
@@ -1,22 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirnamePath, getProcessCwd, joinPath, resolvePath } from './path.js';
|
|
3
|
+
let fsPromisesModule = null;
|
|
4
|
+
let fsModule = null;
|
|
5
|
+
function nodeFsPromises() {
|
|
6
|
+
fsPromisesModule ??= import('node:fs/promises');
|
|
7
|
+
return fsPromisesModule;
|
|
8
|
+
}
|
|
9
|
+
function nodeFs() {
|
|
10
|
+
fsModule ??= import('node:fs');
|
|
11
|
+
return fsModule;
|
|
12
|
+
}
|
|
4
13
|
export class NodeFileSystem {
|
|
5
14
|
async readFile(path) {
|
|
15
|
+
const { readFile } = await nodeFsPromises();
|
|
6
16
|
return await readFile(path, 'utf-8');
|
|
7
17
|
}
|
|
8
18
|
async writeFile(path, content) {
|
|
19
|
+
const { writeFile } = await nodeFsPromises();
|
|
9
20
|
await ensureDirForFile(path, this);
|
|
10
21
|
await writeFile(path, content, 'utf-8');
|
|
11
22
|
}
|
|
12
23
|
async appendFile(path, content) {
|
|
24
|
+
const { appendFile } = await nodeFsPromises();
|
|
13
25
|
await ensureDirForFile(path, this);
|
|
14
26
|
await appendFile(path, content, 'utf-8');
|
|
15
27
|
}
|
|
16
28
|
async mkdir(path) {
|
|
29
|
+
const { mkdir } = await nodeFsPromises();
|
|
17
30
|
await mkdir(path, { recursive: true });
|
|
18
31
|
}
|
|
19
32
|
async exists(path) {
|
|
33
|
+
const { access } = await nodeFsPromises();
|
|
20
34
|
try {
|
|
21
35
|
await access(path);
|
|
22
36
|
return true;
|
|
@@ -26,12 +40,15 @@ export class NodeFileSystem {
|
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
async readDir(path) {
|
|
43
|
+
const { readdir } = await nodeFsPromises();
|
|
29
44
|
return await readdir(path);
|
|
30
45
|
}
|
|
31
46
|
async unlink(path) {
|
|
47
|
+
const { rm } = await nodeFsPromises();
|
|
32
48
|
await rm(path, { recursive: true, force: true });
|
|
33
49
|
}
|
|
34
50
|
async stat(path) {
|
|
51
|
+
const { stat } = await nodeFsPromises();
|
|
35
52
|
try {
|
|
36
53
|
const value = await stat(path);
|
|
37
54
|
return {
|
|
@@ -46,17 +63,67 @@ export class NodeFileSystem {
|
|
|
46
63
|
}
|
|
47
64
|
}
|
|
48
65
|
async realpath(path) {
|
|
66
|
+
const { realpath } = await nodeFsPromises();
|
|
49
67
|
return await realpath(path);
|
|
50
68
|
}
|
|
51
69
|
async copy(src, dest) {
|
|
70
|
+
const { cp } = await nodeFsPromises();
|
|
52
71
|
await cp(src, dest, { recursive: true });
|
|
53
72
|
}
|
|
54
73
|
async rename(src, dest) {
|
|
74
|
+
const { rename } = await nodeFsPromises();
|
|
55
75
|
await rename(src, dest);
|
|
56
76
|
}
|
|
57
77
|
createLogStream(path) {
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
return new LazyNodeLogStream(path);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export class NodeSyncFileSystem {
|
|
82
|
+
readFile(path) {
|
|
83
|
+
return readFileSync(path, 'utf-8');
|
|
84
|
+
}
|
|
85
|
+
writeFile(path, content) {
|
|
86
|
+
ensureDirForFileSync(path, this);
|
|
87
|
+
writeFileSync(path, content, 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
mkdir(path) {
|
|
90
|
+
mkdirSync(path, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
readDir(path) {
|
|
93
|
+
return readdirSync(path);
|
|
94
|
+
}
|
|
95
|
+
unlink(path) {
|
|
96
|
+
rmSync(path, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class LazyNodeLogStream {
|
|
100
|
+
ready;
|
|
101
|
+
ended = false;
|
|
102
|
+
// Single serialized chain: every write/end is appended here, so the underlying stream observes
|
|
103
|
+
// them in call order regardless of how the resolving microtasks interleave. A per-write `shift()`
|
|
104
|
+
// off a shared buffer (the previous approach) could reorder writes that arrived in the same tick.
|
|
105
|
+
tail;
|
|
106
|
+
constructor(path) {
|
|
107
|
+
this.ready = nodeFs().then(({ createWriteStream, mkdirSync }) => {
|
|
108
|
+
mkdirSync(dirnamePath(path), { recursive: true });
|
|
109
|
+
const stream = createWriteStream(path, { flags: 'a' });
|
|
110
|
+
return {
|
|
111
|
+
write: (chunk) => stream.write(chunk),
|
|
112
|
+
end: () => stream.end(),
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
this.tail = this.ready;
|
|
116
|
+
}
|
|
117
|
+
write(chunk) {
|
|
118
|
+
if (this.ended)
|
|
119
|
+
return;
|
|
120
|
+
this.tail = this.tail.then(() => this.ready.then((stream) => stream.write(chunk)));
|
|
121
|
+
}
|
|
122
|
+
end() {
|
|
123
|
+
if (this.ended)
|
|
124
|
+
return;
|
|
125
|
+
this.ended = true;
|
|
126
|
+
this.tail = this.tail.then(() => this.ready.then((stream) => stream.end()));
|
|
60
127
|
}
|
|
61
128
|
}
|
|
62
129
|
const CLOUDFLARE_FS_ERROR = 'FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2.';
|
|
@@ -113,11 +180,14 @@ export function getFs() {
|
|
|
113
180
|
return activeFileSystem;
|
|
114
181
|
}
|
|
115
182
|
export async function ensureDirForFile(path, fs = getFs()) {
|
|
116
|
-
await fs.mkdir(
|
|
183
|
+
await fs.mkdir(dirnamePath(path));
|
|
184
|
+
}
|
|
185
|
+
export function ensureDirForFileSync(path, fs) {
|
|
186
|
+
fs.mkdir(dirnamePath(path));
|
|
117
187
|
}
|
|
118
188
|
export async function atomicWriteFile(path, content, fs = getFs()) {
|
|
119
189
|
await ensureDirForFile(path, fs);
|
|
120
|
-
const tempPath = `${path}.${
|
|
190
|
+
const tempPath = `${path}.${getProcessPid()}.${uniqueToken()}.tmp`;
|
|
121
191
|
await fs.writeFile(tempPath, content);
|
|
122
192
|
await fs.rename(tempPath, path);
|
|
123
193
|
}
|
|
@@ -134,7 +204,7 @@ export async function walkDir(path, fs = getFs()) {
|
|
|
134
204
|
const entries = (await fs.readDir(path)).sort();
|
|
135
205
|
const result = [];
|
|
136
206
|
for (const entry of entries) {
|
|
137
|
-
const fullPath =
|
|
207
|
+
const fullPath = joinPath(path, entry);
|
|
138
208
|
const entryStat = await fs.stat(fullPath);
|
|
139
209
|
if (entryStat?.isDirectory()) {
|
|
140
210
|
result.push(...(await walkDir(fullPath, fs)));
|
|
@@ -145,13 +215,13 @@ export async function walkDir(path, fs = getFs()) {
|
|
|
145
215
|
}
|
|
146
216
|
return result;
|
|
147
217
|
}
|
|
148
|
-
export function getProjectRoot(startDir =
|
|
149
|
-
let current =
|
|
218
|
+
export function getProjectRoot(startDir = getProcessCwd()) {
|
|
219
|
+
let current = resolvePath(startDir);
|
|
150
220
|
for (let i = 0; i < 12; i++) {
|
|
151
|
-
if (
|
|
221
|
+
if (hasBunFile(joinPath(current, 'bun.lock')) || hasBunFile(joinPath(current, 'package.json'))) {
|
|
152
222
|
return current;
|
|
153
223
|
}
|
|
154
|
-
const parent =
|
|
224
|
+
const parent = dirnamePath(current);
|
|
155
225
|
if (parent === current)
|
|
156
226
|
return startDir;
|
|
157
227
|
current = parent;
|
|
@@ -159,8 +229,22 @@ export function getProjectRoot(startDir = process.cwd()) {
|
|
|
159
229
|
return startDir;
|
|
160
230
|
}
|
|
161
231
|
export function resolveProjectPath(...segments) {
|
|
162
|
-
return
|
|
232
|
+
return resolvePath(getProjectRoot(), ...segments);
|
|
163
233
|
}
|
|
164
234
|
export function createLogStream(path, fs = getFs()) {
|
|
165
235
|
return fs.createLogStream(path);
|
|
166
236
|
}
|
|
237
|
+
function hasBunFile(path) {
|
|
238
|
+
const bun = globalThis.Bun;
|
|
239
|
+
if (bun === undefined)
|
|
240
|
+
return false;
|
|
241
|
+
return bun.file(path).size !== 0;
|
|
242
|
+
}
|
|
243
|
+
function getProcessPid() {
|
|
244
|
+
return globalThis.process?.pid ?? 0;
|
|
245
|
+
}
|
|
246
|
+
// Two writers to the same path in the same millisecond must not share a temp name, or one clobbers
|
|
247
|
+
// the other before rename. randomUUID disambiguates; Date.now keeps names sortable for debugging.
|
|
248
|
+
function uniqueToken() {
|
|
249
|
+
return `${Date.now()}.${crypto.randomUUID()}`;
|
|
250
|
+
}
|
package/dist/index.d.ts
CHANGED
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,cAAc,MAAM,CAAC;AACrB,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,MAAM,CAAC;AACrB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/path.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function normalizeSeparators(path: string): string;
|
|
2
|
+
export declare function isAbsolutePath(path: string): boolean;
|
|
3
|
+
export declare function dirnamePath(path: string): string;
|
|
4
|
+
export declare function joinPath(...segments: string[]): string;
|
|
5
|
+
export declare function resolvePath(...segments: string[]): string;
|
|
6
|
+
export declare function getProcessCwd(): string;
|
|
7
|
+
//# sourceMappingURL=path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAIA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAShD;AAED,wBAAgB,QAAQ,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAMtD;AAED,wBAAgB,WAAW,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAkBzD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
package/dist/path.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Runtime-portable path math. Deliberately avoids `node:path` so the same logic works on
|
|
2
|
+
// `cloudflare-workers` (no `node:*`) as on node-bun (ADR-008). POSIX-style separators throughout;
|
|
3
|
+
// Windows drive paths (`C:/...`) are normalized and treated as absolute.
|
|
4
|
+
export function normalizeSeparators(path) {
|
|
5
|
+
return path.replaceAll('\\', '/');
|
|
6
|
+
}
|
|
7
|
+
export function isAbsolutePath(path) {
|
|
8
|
+
return path.startsWith('/') || /^[A-Za-z]:\//.test(normalizeSeparators(path));
|
|
9
|
+
}
|
|
10
|
+
export function dirnamePath(path) {
|
|
11
|
+
const input = normalizeSeparators(path);
|
|
12
|
+
if (/^\/+$/.test(input))
|
|
13
|
+
return '/';
|
|
14
|
+
const normalized = input.replace(/\/+$/, '');
|
|
15
|
+
if (normalized === '' || normalized === '/')
|
|
16
|
+
return normalized || '.';
|
|
17
|
+
const index = normalized.lastIndexOf('/');
|
|
18
|
+
if (index < 0)
|
|
19
|
+
return '.';
|
|
20
|
+
if (index === 0)
|
|
21
|
+
return '/';
|
|
22
|
+
return normalized.slice(0, index);
|
|
23
|
+
}
|
|
24
|
+
export function joinPath(...segments) {
|
|
25
|
+
const filtered = segments.filter((segment) => segment.length > 0).map(normalizeSeparators);
|
|
26
|
+
if (filtered.length === 0)
|
|
27
|
+
return '.';
|
|
28
|
+
const absolute = isAbsolutePath(filtered[0] ?? '');
|
|
29
|
+
const joined = filtered.join('/').replace(/\/+/g, '/');
|
|
30
|
+
return absolute ? joined : joined.replace(/^\//, '');
|
|
31
|
+
}
|
|
32
|
+
export function resolvePath(...segments) {
|
|
33
|
+
const candidates = segments.length === 0 ? [getProcessCwd()] : segments;
|
|
34
|
+
let resolved = '';
|
|
35
|
+
for (const segment of candidates.map(normalizeSeparators)) {
|
|
36
|
+
if (segment.length === 0)
|
|
37
|
+
continue;
|
|
38
|
+
resolved = isAbsolutePath(segment) ? segment : joinPath(resolved || getProcessCwd(), segment);
|
|
39
|
+
}
|
|
40
|
+
const parts = [];
|
|
41
|
+
const absolute = isAbsolutePath(resolved);
|
|
42
|
+
for (const part of resolved.split('/')) {
|
|
43
|
+
if (part === '' || part === '.')
|
|
44
|
+
continue;
|
|
45
|
+
if (part === '..') {
|
|
46
|
+
parts.pop();
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
parts.push(part);
|
|
50
|
+
}
|
|
51
|
+
return `${absolute ? '/' : ''}${parts.join('/')}` || (absolute ? '/' : '.');
|
|
52
|
+
}
|
|
53
|
+
export function getProcessCwd() {
|
|
54
|
+
return globalThis.process?.cwd?.() ?? '/';
|
|
55
|
+
}
|
|
@@ -13,6 +13,11 @@ export interface ProcessOptions {
|
|
|
13
13
|
command: string;
|
|
14
14
|
args?: string[];
|
|
15
15
|
cwd?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Environment forwarded verbatim to the child process. Caller-controlled — when launching an
|
|
18
|
+
* untrusted command, pass an explicit allowlist rather than the parent's full environment, so
|
|
19
|
+
* inherited secrets are not leaked into the subprocess.
|
|
20
|
+
*/
|
|
16
21
|
env?: Record<string, string>;
|
|
17
22
|
timeout?: number;
|
|
18
23
|
/** Maximum output buffer size in bytes (maps to execa `maxBuffer`). */
|
|
@@ -33,9 +38,40 @@ export interface ProcessResult {
|
|
|
33
38
|
export interface ProcessExecutor {
|
|
34
39
|
run(options: ProcessOptions): Promise<ProcessResult>;
|
|
35
40
|
}
|
|
41
|
+
export interface SyncProcessExecutor {
|
|
42
|
+
runSync(options: Omit<ProcessOptions, 'timeout'>): ProcessResult;
|
|
43
|
+
}
|
|
44
|
+
export interface PipeProcessOptions {
|
|
45
|
+
command: string;
|
|
46
|
+
args?: string[];
|
|
47
|
+
cwd?: string;
|
|
48
|
+
/** Forwarded verbatim to the child — pass an allowlist for untrusted commands (see {@link ProcessOptions.env}). */
|
|
49
|
+
env?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
export interface PipeProcess {
|
|
52
|
+
readonly pid: number | null;
|
|
53
|
+
readonly stdout: ReadableStream<Uint8Array> | null;
|
|
54
|
+
readonly stderr: ReadableStream<Uint8Array> | null;
|
|
55
|
+
readonly exited: Promise<number | null>;
|
|
56
|
+
writeStdin(input: string | Uint8Array): void;
|
|
57
|
+
endStdin(): void;
|
|
58
|
+
kill(signal?: ProcessSignal): void;
|
|
59
|
+
}
|
|
60
|
+
export interface PipeProcessSpawner {
|
|
61
|
+
spawn(options: PipeProcessOptions): PipeProcess;
|
|
62
|
+
}
|
|
36
63
|
export declare class NodeProcessExecutor implements ProcessExecutor {
|
|
37
64
|
private readonly config;
|
|
38
65
|
constructor(config?: ProcessExecutorConfig);
|
|
39
66
|
run(options: ProcessOptions): Promise<ProcessResult>;
|
|
40
67
|
}
|
|
68
|
+
export declare class BunSyncProcessExecutor implements SyncProcessExecutor {
|
|
69
|
+
runSync(options: Omit<ProcessOptions, 'timeout'>): ProcessResult;
|
|
70
|
+
}
|
|
71
|
+
export declare class BunPipeProcessSpawner implements PipeProcessSpawner {
|
|
72
|
+
spawn(options: PipeProcessOptions): PipeProcess;
|
|
73
|
+
}
|
|
74
|
+
type BunSubprocess = ReturnType<typeof Bun.spawn>;
|
|
75
|
+
type ProcessSignal = Parameters<BunSubprocess['kill']>[0];
|
|
76
|
+
export {};
|
|
41
77
|
//# sourceMappingURL=process-executor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-executor.d.ts","sourceRoot":"","sources":["../src/process-executor.ts"],"names":[],"mappings":"AAGA,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,MAAM,WAAW,qBAAqB;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,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,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC5B,GAAG,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACxD;AAED,qBAAa,mBAAoB,YAAW,eAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,GAAE,qBAA0B;IAEzD,GAAG,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA2C7D"}
|
|
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
|
@@ -44,6 +44,74 @@ export class NodeProcessExecutor {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
export class BunSyncProcessExecutor {
|
|
48
|
+
runSync(options) {
|
|
49
|
+
const args = options.args ?? [];
|
|
50
|
+
const startedAt = Date.now();
|
|
51
|
+
const result = Bun.spawnSync({
|
|
52
|
+
cmd: [options.command, ...args],
|
|
53
|
+
stdout: 'pipe',
|
|
54
|
+
stderr: 'pipe',
|
|
55
|
+
stdin: 'ignore',
|
|
56
|
+
...(options.cwd !== undefined ? { cwd: options.cwd } : {}),
|
|
57
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
58
|
+
});
|
|
59
|
+
if (options.rejectOnError === true && result.exitCode !== 0) {
|
|
60
|
+
throw new Error(`${options.command} ${args.join(' ')} failed with exit code ${result.exitCode}: ${stripFinalNewline(asString(result.stderr))}`);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
command: options.command,
|
|
64
|
+
args,
|
|
65
|
+
exitCode: result.exitCode,
|
|
66
|
+
stdout: stripFinalNewline(asString(result.stdout)),
|
|
67
|
+
stderr: stripFinalNewline(asString(result.stderr)),
|
|
68
|
+
durationMs: Date.now() - startedAt,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export class BunPipeProcessSpawner {
|
|
73
|
+
spawn(options) {
|
|
74
|
+
const subprocess = Bun.spawn({
|
|
75
|
+
cmd: [options.command, ...(options.args ?? [])],
|
|
76
|
+
stdin: 'pipe',
|
|
77
|
+
stdout: 'pipe',
|
|
78
|
+
stderr: 'pipe',
|
|
79
|
+
...(options.cwd !== undefined ? { cwd: options.cwd } : {}),
|
|
80
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
81
|
+
});
|
|
82
|
+
return new BunPipeProcess(subprocess);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
class BunPipeProcess {
|
|
86
|
+
subprocess;
|
|
87
|
+
writer;
|
|
88
|
+
constructor(subprocess) {
|
|
89
|
+
this.subprocess = subprocess;
|
|
90
|
+
this.writer = subprocess.stdin;
|
|
91
|
+
}
|
|
92
|
+
get pid() {
|
|
93
|
+
return this.subprocess.pid ?? null;
|
|
94
|
+
}
|
|
95
|
+
get stdout() {
|
|
96
|
+
return isReadableStream(this.subprocess.stdout) ? this.subprocess.stdout : null;
|
|
97
|
+
}
|
|
98
|
+
get stderr() {
|
|
99
|
+
return isReadableStream(this.subprocess.stderr) ? this.subprocess.stderr : null;
|
|
100
|
+
}
|
|
101
|
+
get exited() {
|
|
102
|
+
return this.subprocess.exited;
|
|
103
|
+
}
|
|
104
|
+
writeStdin(input) {
|
|
105
|
+
this.writer.write(input);
|
|
106
|
+
this.writer.flush?.();
|
|
107
|
+
}
|
|
108
|
+
endStdin() {
|
|
109
|
+
this.writer.end?.();
|
|
110
|
+
}
|
|
111
|
+
kill(signal) {
|
|
112
|
+
this.subprocess.kill(signal);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
47
115
|
function buildExecaOptions(opts) {
|
|
48
116
|
const canStream = !opts.forceBuffered &&
|
|
49
117
|
opts.outputPolicy?.mode === 'stream' &&
|
|
@@ -68,3 +136,9 @@ function asString(value) {
|
|
|
68
136
|
return value.map(String).join('');
|
|
69
137
|
return '';
|
|
70
138
|
}
|
|
139
|
+
function stripFinalNewline(value) {
|
|
140
|
+
return value.endsWith('\r\n') ? value.slice(0, -2) : value.endsWith('\n') ? value.slice(0, -1) : value;
|
|
141
|
+
}
|
|
142
|
+
function isReadableStream(value) {
|
|
143
|
+
return value instanceof ReadableStream;
|
|
144
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface JsonSchemaViolation {
|
|
2
|
+
path: string;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
export interface JsonSchema {
|
|
6
|
+
type?: string | string[];
|
|
7
|
+
required?: string[];
|
|
8
|
+
properties?: Record<string, JsonSchema>;
|
|
9
|
+
additionalProperties?: boolean | JsonSchema;
|
|
10
|
+
items?: JsonSchema;
|
|
11
|
+
enum?: unknown[];
|
|
12
|
+
const?: unknown;
|
|
13
|
+
oneOf?: JsonSchema[];
|
|
14
|
+
anyOf?: JsonSchema[];
|
|
15
|
+
$ref?: string;
|
|
16
|
+
$defs?: Record<string, JsonSchema>;
|
|
17
|
+
}
|
|
18
|
+
export interface StructuredConfigLoadOptions {
|
|
19
|
+
validateSchema?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Allow `http(s)://` `$schema` refs. Off by default: remote fetches are an SSRF/DoS surface when
|
|
22
|
+
* configs are authored by third parties. Prefer bundled package-specifier refs (resolved from
|
|
23
|
+
* `node_modules`). Supplying `fetch` explicitly also opts into remote resolution.
|
|
24
|
+
*/
|
|
25
|
+
allowRemote?: boolean;
|
|
26
|
+
fetch?: (input: string) => Promise<Response>;
|
|
27
|
+
/**
|
|
28
|
+
* Module resolver for bare package-specifier `$schema` refs (e.g.
|
|
29
|
+
* `@gobing-ai/ts-rule-engine/schemas/rule-file.schema.json`). Defaults to `Bun.resolveSync`.
|
|
30
|
+
* Injectable for testing.
|
|
31
|
+
*/
|
|
32
|
+
resolve?: (specifier: string, from: string) => string;
|
|
33
|
+
}
|
|
34
|
+
export declare class StructuredConfigSchemaError extends Error {
|
|
35
|
+
readonly violations: readonly JsonSchemaViolation[];
|
|
36
|
+
constructor(message: string, violations?: readonly JsonSchemaViolation[]);
|
|
37
|
+
}
|
|
38
|
+
export declare function loadStructuredConfig(path: string, options?: StructuredConfigLoadOptions): Promise<unknown>;
|
|
39
|
+
export declare function parseStructuredConfig(content: string, source: string, options?: StructuredConfigLoadOptions): Promise<unknown>;
|
|
40
|
+
export declare function validateDeclaredJsonSchema(value: unknown, source: string, options?: StructuredConfigLoadOptions): Promise<void>;
|
|
41
|
+
export declare function validateJsonSchema(value: unknown, schema: JsonSchema, path?: string, defs?: Record<string, JsonSchema>, seenRefs?: ReadonlySet<string>): JsonSchemaViolation[];
|
|
42
|
+
//# sourceMappingURL=schema-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validation.d.ts","sourceRoot":"","sources":["../src/schema-validation.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,2BAA2B;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACzD;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IAG9C,QAAQ,CAAC,UAAU,EAAE,SAAS,mBAAmB,EAAE;gBADnD,OAAO,EAAE,MAAM,EACN,UAAU,GAAE,SAAS,mBAAmB,EAAO;CAK/D;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAAC,OAAO,CAAC,CAGpH;AAED,wBAAsB,qBAAqB,CACvC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,2BAAgC,GAC1C,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,wBAAsB,0BAA0B,CAC5C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,2BAAgC,GAC1C,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,SAAK,EACT,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAM,EACrC,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa,GAC1C,mBAAmB,EAAE,CAwDvB"}
|