@fuzdev/fuz_util 0.48.1 → 0.48.3
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/hash.d.ts +27 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +66 -0
- package/dist/process.d.ts +37 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +44 -0
- package/package.json +7 -7
- package/src/lib/hash.ts +73 -0
- package/src/lib/process.ts +56 -0
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash utilities for content comparison and cache invalidation.
|
|
3
|
+
*
|
|
4
|
+
* Provides both secure (cryptographic) and insecure (fast) hash functions.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Computes a cryptographic hash using Web Crypto API.
|
|
10
|
+
*
|
|
11
|
+
* @param data - String or binary data to hash. Strings are UTF-8 encoded.
|
|
12
|
+
* @param algorithm - Hash algorithm. Defaults to SHA-256.
|
|
13
|
+
* @returns Hexadecimal hash string.
|
|
14
|
+
*/
|
|
15
|
+
export declare const hash_secure: (data: BufferSource | string, algorithm?: "SHA-256" | "SHA-384" | "SHA-512") => Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Computes a fast non-cryptographic hash using DJB2 algorithm.
|
|
18
|
+
* Use for content comparison and cache keys, not security.
|
|
19
|
+
*
|
|
20
|
+
* Note: Strings use UTF-16 code units, buffers use raw bytes.
|
|
21
|
+
* For non-ASCII, `hash_insecure(str) !== hash_insecure(encoder.encode(str))`.
|
|
22
|
+
*
|
|
23
|
+
* @param data - String or binary data to hash.
|
|
24
|
+
* @returns 8-character hex-encoded unsigned 32-bit hash.
|
|
25
|
+
*/
|
|
26
|
+
export declare const hash_insecure: (data: BufferSource | string) => string;
|
|
27
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACvB,MAAM,YAAY,GAAG,MAAM,EAC3B,YAAW,SAAS,GAAG,SAAS,GAAG,SAAqB,KACtD,OAAO,CAAC,MAAM,CAUhB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,YAAY,GAAG,MAAM,KAAG,MAkB3D,CAAC"}
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash utilities for content comparison and cache invalidation.
|
|
3
|
+
*
|
|
4
|
+
* Provides both secure (cryptographic) and insecure (fast) hash functions.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
const encoder = new TextEncoder();
|
|
9
|
+
// Lazily computed lookup table for byte to hex conversion
|
|
10
|
+
let byte_to_hex;
|
|
11
|
+
const get_byte_to_hex = () => {
|
|
12
|
+
if (byte_to_hex === undefined) {
|
|
13
|
+
byte_to_hex = new Array(256); // 256 possible byte values (0x00-0xff)
|
|
14
|
+
for (let i = 0; i < 256; i++) {
|
|
15
|
+
byte_to_hex[i] = i.toString(16).padStart(2, '0');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return byte_to_hex;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Computes a cryptographic hash using Web Crypto API.
|
|
22
|
+
*
|
|
23
|
+
* @param data - String or binary data to hash. Strings are UTF-8 encoded.
|
|
24
|
+
* @param algorithm - Hash algorithm. Defaults to SHA-256.
|
|
25
|
+
* @returns Hexadecimal hash string.
|
|
26
|
+
*/
|
|
27
|
+
export const hash_secure = async (data, algorithm = 'SHA-256') => {
|
|
28
|
+
const buffer = typeof data === 'string' ? encoder.encode(data) : data;
|
|
29
|
+
const digested = await crypto.subtle.digest(algorithm, buffer);
|
|
30
|
+
const bytes = new Uint8Array(digested);
|
|
31
|
+
const lookup = get_byte_to_hex();
|
|
32
|
+
let hex = '';
|
|
33
|
+
for (const byte of bytes) {
|
|
34
|
+
hex += lookup[byte];
|
|
35
|
+
}
|
|
36
|
+
return hex;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Computes a fast non-cryptographic hash using DJB2 algorithm.
|
|
40
|
+
* Use for content comparison and cache keys, not security.
|
|
41
|
+
*
|
|
42
|
+
* Note: Strings use UTF-16 code units, buffers use raw bytes.
|
|
43
|
+
* For non-ASCII, `hash_insecure(str) !== hash_insecure(encoder.encode(str))`.
|
|
44
|
+
*
|
|
45
|
+
* @param data - String or binary data to hash.
|
|
46
|
+
* @returns 8-character hex-encoded unsigned 32-bit hash.
|
|
47
|
+
*/
|
|
48
|
+
export const hash_insecure = (data) => {
|
|
49
|
+
let hash = 5381; // DJB2 initial value, chosen empirically for good distribution
|
|
50
|
+
if (typeof data === 'string') {
|
|
51
|
+
for (let i = 0; i < data.length; i++) {
|
|
52
|
+
hash = (hash << 5) - hash + data.charCodeAt(i);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const bytes = data instanceof Uint8Array
|
|
57
|
+
? data
|
|
58
|
+
: data instanceof ArrayBuffer
|
|
59
|
+
? new Uint8Array(data)
|
|
60
|
+
: new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
61
|
+
for (const byte of bytes) {
|
|
62
|
+
hash = (hash << 5) - hash + byte;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
66
|
+
};
|
package/dist/process.d.ts
CHANGED
|
@@ -84,6 +84,16 @@ export interface DespawnOptions {
|
|
|
84
84
|
*/
|
|
85
85
|
timeout_ms?: number;
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Result of spawning a detached process.
|
|
89
|
+
*/
|
|
90
|
+
export type SpawnDetachedResult = {
|
|
91
|
+
ok: true;
|
|
92
|
+
child: ChildProcess;
|
|
93
|
+
} | {
|
|
94
|
+
ok: false;
|
|
95
|
+
message: string;
|
|
96
|
+
};
|
|
87
97
|
/**
|
|
88
98
|
* Handle for a spawned process with access to the child and completion promise.
|
|
89
99
|
*/
|
|
@@ -249,6 +259,33 @@ export declare const despawn_all: (options?: DespawnOptions) => Promise<Array<Sp
|
|
|
249
259
|
* @see ProcessRegistry.attach_error_handler
|
|
250
260
|
*/
|
|
251
261
|
export declare const attach_process_error_handler: (options?: Parameters<ProcessRegistry["attach_error_handler"]>[0]) => (() => void);
|
|
262
|
+
/**
|
|
263
|
+
* Spawns a detached process that continues after parent exits.
|
|
264
|
+
*
|
|
265
|
+
* Unlike other spawn functions, this is NOT tracked in any ProcessRegistry.
|
|
266
|
+
* The spawned process is meant to outlive the parent (e.g., daemon processes).
|
|
267
|
+
*
|
|
268
|
+
* @param command - The command to run
|
|
269
|
+
* @param args - Arguments to pass to the command
|
|
270
|
+
* @param options - Spawn options (use `stdio` to redirect output to file descriptors)
|
|
271
|
+
* @returns Result with pid on success, or error message on failure
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* // Simple detached process
|
|
276
|
+
* const result = spawn_detached('node', ['daemon.js'], {cwd: '/app'});
|
|
277
|
+
*
|
|
278
|
+
* // With log file (caller handles file opening)
|
|
279
|
+
* import {openSync, closeSync} from 'node:fs';
|
|
280
|
+
* const log_fd = openSync('/var/log/daemon.log', 'a');
|
|
281
|
+
* const result = spawn_detached('node', ['daemon.js'], {
|
|
282
|
+
* cwd: '/app',
|
|
283
|
+
* stdio: ['ignore', log_fd, log_fd],
|
|
284
|
+
* });
|
|
285
|
+
* closeSync(log_fd);
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
export declare const spawn_detached: (command: string, args?: ReadonlyArray<string>, options?: SpawnOptions) => SpawnDetachedResult;
|
|
252
289
|
/**
|
|
253
290
|
* Formats a child process for display.
|
|
254
291
|
*
|
package/dist/process.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/process.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,IAAI,wBAAwB,EACjC,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,MAAM,oBAAoB,CAAC;AAa5B;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,IAAI,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAMrF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,WAAW,KAAG,MAAM,IAAI,gBAChD,CAAC;AAEvB;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,QAAQ,WAAW,KAAG,MAAM,IAAI,mBAClD,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,WAAW,KAAG,MAAM,IAAI,iBAClD,CAAC;AAMtB;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACxD;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,wBAAwB,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/process.ts"],"names":[],"mappings":"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"}
|
package/dist/process.js
CHANGED
|
@@ -351,6 +351,50 @@ export const despawn_all = (options) => process_registry_default.despawn_all(opt
|
|
|
351
351
|
* @see ProcessRegistry.attach_error_handler
|
|
352
352
|
*/
|
|
353
353
|
export const attach_process_error_handler = (options) => process_registry_default.attach_error_handler(options);
|
|
354
|
+
/**
|
|
355
|
+
* Spawns a detached process that continues after parent exits.
|
|
356
|
+
*
|
|
357
|
+
* Unlike other spawn functions, this is NOT tracked in any ProcessRegistry.
|
|
358
|
+
* The spawned process is meant to outlive the parent (e.g., daemon processes).
|
|
359
|
+
*
|
|
360
|
+
* @param command - The command to run
|
|
361
|
+
* @param args - Arguments to pass to the command
|
|
362
|
+
* @param options - Spawn options (use `stdio` to redirect output to file descriptors)
|
|
363
|
+
* @returns Result with pid on success, or error message on failure
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* // Simple detached process
|
|
368
|
+
* const result = spawn_detached('node', ['daemon.js'], {cwd: '/app'});
|
|
369
|
+
*
|
|
370
|
+
* // With log file (caller handles file opening)
|
|
371
|
+
* import {openSync, closeSync} from 'node:fs';
|
|
372
|
+
* const log_fd = openSync('/var/log/daemon.log', 'a');
|
|
373
|
+
* const result = spawn_detached('node', ['daemon.js'], {
|
|
374
|
+
* cwd: '/app',
|
|
375
|
+
* stdio: ['ignore', log_fd, log_fd],
|
|
376
|
+
* });
|
|
377
|
+
* closeSync(log_fd);
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
export const spawn_detached = (command, args = [], options) => {
|
|
381
|
+
try {
|
|
382
|
+
const child = node_spawn_child_process(command, args, {
|
|
383
|
+
stdio: 'ignore',
|
|
384
|
+
...options,
|
|
385
|
+
detached: true,
|
|
386
|
+
});
|
|
387
|
+
// Allow parent to exit independently
|
|
388
|
+
child.unref();
|
|
389
|
+
if (child.pid === undefined) {
|
|
390
|
+
return { ok: false, message: 'Failed to get child PID' };
|
|
391
|
+
}
|
|
392
|
+
return { ok: true, child };
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
return { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
396
|
+
}
|
|
397
|
+
};
|
|
354
398
|
//
|
|
355
399
|
// Formatting Utilities
|
|
356
400
|
//
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_util",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.3",
|
|
4
4
|
"description": "utility belt for JS",
|
|
5
5
|
"glyph": "🦕",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@changesets/changelog-git": "^0.2.1",
|
|
64
|
-
"@fuzdev/fuz_code": "^0.
|
|
65
|
-
"@fuzdev/fuz_css": "^0.
|
|
66
|
-
"@fuzdev/fuz_ui": "^0.
|
|
64
|
+
"@fuzdev/fuz_code": "^0.41.0",
|
|
65
|
+
"@fuzdev/fuz_css": "^0.45.0",
|
|
66
|
+
"@fuzdev/fuz_ui": "^0.180.0",
|
|
67
67
|
"@ryanatkn/eslint-config": "^0.9.0",
|
|
68
|
-
"@ryanatkn/gro": "^0.189.
|
|
68
|
+
"@ryanatkn/gro": "^0.189.3",
|
|
69
69
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
70
70
|
"@sveltejs/kit": "^2.50.1",
|
|
71
71
|
"@sveltejs/package": "^2.5.7",
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"fast-deep-equal": "^3.1.3",
|
|
80
80
|
"prettier": "^3.7.4",
|
|
81
81
|
"prettier-plugin-svelte": "^3.4.1",
|
|
82
|
-
"svelte": "^5.48.
|
|
83
|
-
"svelte-check": "^4.3.
|
|
82
|
+
"svelte": "^5.48.5",
|
|
83
|
+
"svelte-check": "^4.3.5",
|
|
84
84
|
"tslib": "^2.8.1",
|
|
85
85
|
"typescript": "^5.9.3",
|
|
86
86
|
"typescript-eslint": "^8.48.1",
|
package/src/lib/hash.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash utilities for content comparison and cache invalidation.
|
|
3
|
+
*
|
|
4
|
+
* Provides both secure (cryptographic) and insecure (fast) hash functions.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
|
|
11
|
+
// Lazily computed lookup table for byte to hex conversion
|
|
12
|
+
let byte_to_hex: Array<string> | undefined;
|
|
13
|
+
const get_byte_to_hex = (): Array<string> => {
|
|
14
|
+
if (byte_to_hex === undefined) {
|
|
15
|
+
byte_to_hex = new Array(256); // 256 possible byte values (0x00-0xff)
|
|
16
|
+
for (let i = 0; i < 256; i++) {
|
|
17
|
+
byte_to_hex[i] = i.toString(16).padStart(2, '0');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return byte_to_hex;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Computes a cryptographic hash using Web Crypto API.
|
|
25
|
+
*
|
|
26
|
+
* @param data - String or binary data to hash. Strings are UTF-8 encoded.
|
|
27
|
+
* @param algorithm - Hash algorithm. Defaults to SHA-256.
|
|
28
|
+
* @returns Hexadecimal hash string.
|
|
29
|
+
*/
|
|
30
|
+
export const hash_secure = async (
|
|
31
|
+
data: BufferSource | string,
|
|
32
|
+
algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512' = 'SHA-256',
|
|
33
|
+
): Promise<string> => {
|
|
34
|
+
const buffer = typeof data === 'string' ? encoder.encode(data) : data;
|
|
35
|
+
const digested = await crypto.subtle.digest(algorithm, buffer);
|
|
36
|
+
const bytes = new Uint8Array(digested);
|
|
37
|
+
const lookup = get_byte_to_hex();
|
|
38
|
+
let hex = '';
|
|
39
|
+
for (const byte of bytes) {
|
|
40
|
+
hex += lookup[byte];
|
|
41
|
+
}
|
|
42
|
+
return hex;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Computes a fast non-cryptographic hash using DJB2 algorithm.
|
|
47
|
+
* Use for content comparison and cache keys, not security.
|
|
48
|
+
*
|
|
49
|
+
* Note: Strings use UTF-16 code units, buffers use raw bytes.
|
|
50
|
+
* For non-ASCII, `hash_insecure(str) !== hash_insecure(encoder.encode(str))`.
|
|
51
|
+
*
|
|
52
|
+
* @param data - String or binary data to hash.
|
|
53
|
+
* @returns 8-character hex-encoded unsigned 32-bit hash.
|
|
54
|
+
*/
|
|
55
|
+
export const hash_insecure = (data: BufferSource | string): string => {
|
|
56
|
+
let hash = 5381; // DJB2 initial value, chosen empirically for good distribution
|
|
57
|
+
if (typeof data === 'string') {
|
|
58
|
+
for (let i = 0; i < data.length; i++) {
|
|
59
|
+
hash = (hash << 5) - hash + data.charCodeAt(i);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
const bytes: Uint8Array =
|
|
63
|
+
data instanceof Uint8Array
|
|
64
|
+
? data
|
|
65
|
+
: data instanceof ArrayBuffer
|
|
66
|
+
? new Uint8Array(data)
|
|
67
|
+
: new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
68
|
+
for (const byte of bytes) {
|
|
69
|
+
hash = (hash << 5) - hash + byte;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
73
|
+
};
|
package/src/lib/process.ts
CHANGED
|
@@ -120,6 +120,11 @@ export interface DespawnOptions {
|
|
|
120
120
|
timeout_ms?: number;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Result of spawning a detached process.
|
|
125
|
+
*/
|
|
126
|
+
export type SpawnDetachedResult = {ok: true; child: ChildProcess} | {ok: false; message: string};
|
|
127
|
+
|
|
123
128
|
//
|
|
124
129
|
// Process Handle Types
|
|
125
130
|
//
|
|
@@ -553,6 +558,57 @@ export const attach_process_error_handler = (
|
|
|
553
558
|
options?: Parameters<ProcessRegistry['attach_error_handler']>[0],
|
|
554
559
|
): (() => void) => process_registry_default.attach_error_handler(options);
|
|
555
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Spawns a detached process that continues after parent exits.
|
|
563
|
+
*
|
|
564
|
+
* Unlike other spawn functions, this is NOT tracked in any ProcessRegistry.
|
|
565
|
+
* The spawned process is meant to outlive the parent (e.g., daemon processes).
|
|
566
|
+
*
|
|
567
|
+
* @param command - The command to run
|
|
568
|
+
* @param args - Arguments to pass to the command
|
|
569
|
+
* @param options - Spawn options (use `stdio` to redirect output to file descriptors)
|
|
570
|
+
* @returns Result with pid on success, or error message on failure
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```ts
|
|
574
|
+
* // Simple detached process
|
|
575
|
+
* const result = spawn_detached('node', ['daemon.js'], {cwd: '/app'});
|
|
576
|
+
*
|
|
577
|
+
* // With log file (caller handles file opening)
|
|
578
|
+
* import {openSync, closeSync} from 'node:fs';
|
|
579
|
+
* const log_fd = openSync('/var/log/daemon.log', 'a');
|
|
580
|
+
* const result = spawn_detached('node', ['daemon.js'], {
|
|
581
|
+
* cwd: '/app',
|
|
582
|
+
* stdio: ['ignore', log_fd, log_fd],
|
|
583
|
+
* });
|
|
584
|
+
* closeSync(log_fd);
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
export const spawn_detached = (
|
|
588
|
+
command: string,
|
|
589
|
+
args: ReadonlyArray<string> = [],
|
|
590
|
+
options?: SpawnOptions,
|
|
591
|
+
): SpawnDetachedResult => {
|
|
592
|
+
try {
|
|
593
|
+
const child = node_spawn_child_process(command, args, {
|
|
594
|
+
stdio: 'ignore',
|
|
595
|
+
...options,
|
|
596
|
+
detached: true,
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// Allow parent to exit independently
|
|
600
|
+
child.unref();
|
|
601
|
+
|
|
602
|
+
if (child.pid === undefined) {
|
|
603
|
+
return {ok: false, message: 'Failed to get child PID'};
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return {ok: true, child};
|
|
607
|
+
} catch (error) {
|
|
608
|
+
return {ok: false, message: error instanceof Error ? error.message : String(error)};
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
556
612
|
//
|
|
557
613
|
// Formatting Utilities
|
|
558
614
|
//
|