@orkestrel/process 0.0.1 → 0.0.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/README.md +21 -17
- package/dist/src/core/index.cjs +58 -4
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +250 -51
- package/dist/src/core/index.d.ts +250 -51
- package/dist/src/core/index.js +53 -5
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +865 -220
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +494 -88
- package/dist/src/server/index.d.ts +494 -88
- package/dist/src/server/index.js +850 -221
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -2
package/dist/src/core/index.d.ts
CHANGED
|
@@ -10,13 +10,66 @@ import { EmitterInterface } from '@orkestrel/emitter';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function createDuplicateError(id: string): ProcessError;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Creates the failure raised when a public input is refused before anything is spawned.
|
|
15
|
+
*
|
|
16
|
+
* @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`
|
|
17
|
+
* @param value - The rejected value, carried as `context.value`
|
|
18
|
+
* @returns A typed validation failure
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* createInvalidError("option 'grace'", -1).code // 'invalid'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function createInvalidError(subject: string, value: unknown): ProcessError;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates the failure raised when a launch is attempted on a registry that is being destroyed.
|
|
29
|
+
*
|
|
30
|
+
* @param id - The id the refused launch asked for
|
|
31
|
+
* @returns A typed protocol failure
|
|
32
|
+
*/
|
|
33
|
+
export declare function createProtocolError(id: string): ProcessError;
|
|
34
|
+
|
|
13
35
|
/**
|
|
14
36
|
* Creates the failure raised when a run does not complete successfully and rejection is requested.
|
|
15
37
|
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* The category is `timeout` only when the run's own timeout elapsed; every other failure, including
|
|
40
|
+
* an abort and an output overflow, is a `spawn` failure carrying its {@link RunResult}.
|
|
41
|
+
*
|
|
16
42
|
* @param result - The buffered run outcome that failed
|
|
43
|
+
* @param cause - The underlying host fault, when one ended the run
|
|
17
44
|
* @returns A typed run failure carrying its {@link RunResult}
|
|
18
45
|
*/
|
|
19
|
-
export declare function createRunError(result: RunResult): ProcessError;
|
|
46
|
+
export declare function createRunError(result: RunResult, cause?: unknown): ProcessError;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Options for a detached fire-and-forget spawn.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* A detached child owns no stdio and is never awaited, so the contract carries only the directory it
|
|
53
|
+
* starts in. Its environment comes from the command's own `environment` and `isolated`.
|
|
54
|
+
*/
|
|
55
|
+
export declare interface DetachOptions {
|
|
56
|
+
/** The working directory. Default: the current working directory. */
|
|
57
|
+
readonly workspace?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Lookup inputs for resolving a command file to an executable path.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* `workspace` is searched before `PATH`, matching Windows command semantics. `environment` is the
|
|
65
|
+
* child's effective environment, so the lookup reads the same `PATH` and `PATHEXT` the child will.
|
|
66
|
+
*/
|
|
67
|
+
export declare interface ExecutableOptions {
|
|
68
|
+
/** The directory searched first. Default: the current working directory. */
|
|
69
|
+
readonly workspace?: string;
|
|
70
|
+
/** The child's effective environment. Default: the parent process environment. */
|
|
71
|
+
readonly environment?: Readonly<Record<string, string | undefined>>;
|
|
72
|
+
}
|
|
20
73
|
|
|
21
74
|
/**
|
|
22
75
|
* Checks whether an unknown value is a {@link ProcessError}.
|
|
@@ -32,46 +85,63 @@ export declare function createRunError(result: RunResult): ProcessError;
|
|
|
32
85
|
*/
|
|
33
86
|
export declare function isProcessError(value: unknown): value is ProcessError;
|
|
34
87
|
|
|
88
|
+
/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */
|
|
89
|
+
export declare const PROCESS_BACKLOG = 10485760;
|
|
90
|
+
|
|
91
|
+
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
92
|
+
export declare const PROCESS_CONFIRMATION = 5000;
|
|
93
|
+
|
|
35
94
|
/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
|
|
36
95
|
export declare const PROCESS_EVIDENCE = 2048;
|
|
37
96
|
|
|
38
|
-
/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */
|
|
97
|
+
/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */
|
|
39
98
|
export declare const PROCESS_GRACE = 5000;
|
|
40
99
|
|
|
41
100
|
/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */
|
|
42
101
|
export declare const PROCESS_OUTPUT = 10485760;
|
|
43
102
|
|
|
103
|
+
/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */
|
|
104
|
+
export declare const PROCESS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
105
|
+
|
|
106
|
+
/** The largest timer delay in milliseconds the host schedules without truncating it to one. */
|
|
107
|
+
export declare const PROCESS_TIMER = 2147483647;
|
|
108
|
+
|
|
44
109
|
/**
|
|
45
110
|
* The public contracts for `@orkestrel/process`: a typed child-process toolkit.
|
|
46
111
|
*
|
|
47
112
|
* @remarks
|
|
48
113
|
* Three tiers, smallest to largest:
|
|
49
114
|
*
|
|
50
|
-
* - **{@link ProcessInterface}** — one supervised child with
|
|
51
|
-
* byte-bounded stderr tail, a live stderr event, a writable stdin channel, a typed
|
|
52
|
-
* {@link ProcessEventMap} emitter, and bounded
|
|
53
|
-
*
|
|
115
|
+
* - **{@link ProcessInterface}** — one supervised child with framed stdout lines under a bounded
|
|
116
|
+
* backlog, a byte-bounded stderr tail, a live stderr event, a writable stdin channel, a typed
|
|
117
|
+
* lifecycle {@link ProcessEventMap} emitter, and bounded termination. The low-level streaming
|
|
118
|
+
* primitive.
|
|
54
119
|
* - **{@link RunResult} / run** — a one-shot runner that buffers a child to completion and settles
|
|
55
120
|
* with its output and exit, the ergonomic layer for fire-and-collect commands.
|
|
56
121
|
* - **{@link ProcessManagerInterface}** — a keyed registry of live {@link ProcessInterface}
|
|
57
122
|
* children, launched and stopped by id, observed through its own {@link ProcessManagerEventMap}.
|
|
58
123
|
*
|
|
59
|
-
* Every contract here is host-independent (`AbortSignal`, `AsyncIterable`, `Promise`,
|
|
60
|
-
* `string` signal names). The Node implementations live in `@orkestrel/process/server`.
|
|
124
|
+
* Every contract here is host-independent (`AbortSignal`, `AsyncIterable`, `Promise`, `Uint8Array`,
|
|
125
|
+
* and plain `string` signal names). The Node implementations live in `@orkestrel/process/server`.
|
|
61
126
|
*/
|
|
62
127
|
/**
|
|
63
128
|
* One spawnable command: the executable, its argument vector, and optional environment overrides
|
|
64
129
|
* and initial standard input.
|
|
65
130
|
*
|
|
66
131
|
* @remarks
|
|
67
|
-
* `environment` merges over the parent process environment
|
|
68
|
-
* `
|
|
132
|
+
* `environment` merges over the parent process environment unless `isolated` is `true`, and an
|
|
133
|
+
* `undefined` value unsets a key. On Windows the merge is case-insensitive and the last writer
|
|
134
|
+
* wins, matching how the host resolves an environment variable. `input` is written to the child's
|
|
135
|
+
* stdin immediately after spawn. Every string here is spawn-bound: an empty `file`, or a NUL
|
|
136
|
+
* character anywhere, is refused with a {@link ProcessError} coded `invalid`.
|
|
69
137
|
*/
|
|
70
138
|
export declare interface ProcessCommand {
|
|
71
139
|
readonly file: string;
|
|
72
140
|
readonly arguments: readonly string[];
|
|
73
141
|
readonly environment?: Readonly<Record<string, string | undefined>>;
|
|
74
142
|
readonly input?: string;
|
|
143
|
+
/** If `true`, the child environment is `environment` alone; if `false` or omitted, it merges over the parent environment. */
|
|
144
|
+
readonly isolated?: boolean;
|
|
75
145
|
}
|
|
76
146
|
|
|
77
147
|
/** A child-process failure with a stable machine-readable category. */
|
|
@@ -91,7 +161,7 @@ export declare class ProcessError extends Error {
|
|
|
91
161
|
}
|
|
92
162
|
|
|
93
163
|
/** The machine-readable {@link ProcessError} categories. */
|
|
94
|
-
export declare type ProcessErrorCode = 'spawn' | 'timeout' | 'duplicate' | 'protocol';
|
|
164
|
+
export declare type ProcessErrorCode = 'spawn' | 'timeout' | 'duplicate' | 'protocol' | 'invalid';
|
|
95
165
|
|
|
96
166
|
/** Structured context carried by a {@link ProcessError}. */
|
|
97
167
|
export declare interface ProcessErrorContext {
|
|
@@ -124,12 +194,16 @@ export declare interface ProcessErrorOptions {
|
|
|
124
194
|
* observer subscribes to, alongside the `lines` stream and the `exit` promise.
|
|
125
195
|
*
|
|
126
196
|
* @remarks
|
|
127
|
-
* Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally.
|
|
128
|
-
*
|
|
197
|
+
* Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. The
|
|
198
|
+
* `error` event carries a child fault — a failure to spawn or a process-level error. It is distinct
|
|
199
|
+
* from the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
|
|
200
|
+
* routed to that handler, never emitted as this `error` event.
|
|
129
201
|
*/
|
|
130
202
|
export declare type ProcessEventMap = {
|
|
131
203
|
/** A decoded standard-error chunk arrived. */
|
|
132
204
|
readonly stderr: readonly [chunk: string];
|
|
205
|
+
/** The child emitted an error — a spawn fault or process-level failure — carrying its cause. */
|
|
206
|
+
readonly error: readonly [error: unknown];
|
|
133
207
|
/** The child settled — its terminal state, delivered once. */
|
|
134
208
|
readonly exit: readonly [exit: ProcessExit];
|
|
135
209
|
};
|
|
@@ -143,19 +217,25 @@ export declare interface ProcessExit {
|
|
|
143
217
|
}
|
|
144
218
|
|
|
145
219
|
/**
|
|
146
|
-
* One supervised child process with
|
|
220
|
+
* One supervised child process with framed output, a bounded backlog, and bounded termination.
|
|
147
221
|
*
|
|
148
222
|
* @remarks
|
|
149
|
-
* `lines` is pumped
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
223
|
+
* `lines` is pumped as soon as the child writes, and it is a single-consumer stream: each line goes
|
|
224
|
+
* to exactly one waiting iterator, so two iterators split the output between them rather than each
|
|
225
|
+
* receiving all of it. The policy for an unconsumed backlog follows
|
|
226
|
+
* consumer intent. Once an iterator has been requested, stdout pauses at the `backlog` mark and
|
|
227
|
+
* resumes at half of it, so the consumer loses nothing and the child feels real backpressure. While
|
|
228
|
+
* no iterator has ever been requested, stdout keeps draining so `exit` still resolves, and retention
|
|
229
|
+
* stops at the mark: a consumer attaching after that point receives the retained head, a gap, then
|
|
230
|
+
* the live stream. `evidence` is the decoded, byte-bounded stderr tail — the diagnostic to attach to
|
|
231
|
+
* a failed exit. The typed `emitter` carries the live `stderr` chunks, the child `error` cause, and
|
|
232
|
+
* the terminal `exit`, alongside the `exit` promise. `stop` and `destroy` are idempotent and never
|
|
233
|
+
* reject.
|
|
154
234
|
*/
|
|
155
235
|
export declare interface ProcessInterface {
|
|
156
236
|
/** The typed lifecycle observation surface. */
|
|
157
237
|
readonly emitter: EmitterInterface<ProcessEventMap>;
|
|
158
|
-
/** The
|
|
238
|
+
/** The captured stdout lines, in arrival order, for one consumer, ending when the child's stdout closes. */
|
|
159
239
|
readonly lines: AsyncIterable<string>;
|
|
160
240
|
/** The decoded byte-bounded stderr tail. */
|
|
161
241
|
readonly evidence: string;
|
|
@@ -164,18 +244,35 @@ export declare interface ProcessInterface {
|
|
|
164
244
|
/**
|
|
165
245
|
* Write one line to the open standard-input channel.
|
|
166
246
|
*
|
|
247
|
+
* @remarks
|
|
248
|
+
* Never rejects. The promise settles when the host reports the line handled, so a line written
|
|
249
|
+
* to a child that is not reading stays pending until the child drains it or the channel closes.
|
|
250
|
+
* Every terminal teardown path destroys stdin, so a pending write always settles by teardown. A
|
|
251
|
+
* caller that needs its own deadline arms a timer and calls `stop`.
|
|
252
|
+
*
|
|
167
253
|
* @param text - The line text without its trailing newline
|
|
168
|
-
* @returns True when the
|
|
254
|
+
* @returns True when the line reached the host without error; false when the channel was closed, destroyed, ended, or the write failed
|
|
169
255
|
*/
|
|
170
|
-
send(text: string): boolean
|
|
256
|
+
send(text: string): Promise<boolean>;
|
|
171
257
|
/**
|
|
172
258
|
* Terminate the child process tree and await its observed exit.
|
|
173
259
|
*
|
|
174
|
-
* @
|
|
260
|
+
* @remarks
|
|
261
|
+
* Never rejects. On Windows the whole tree is killed immediately through `taskkill`, with a
|
|
262
|
+
* direct kill as the fallback; on a POSIX host the process group receives `SIGTERM`, then
|
|
263
|
+
* `SIGKILL` after `grace`. No signal is initiated after the child's native exit is observed; the
|
|
264
|
+
* window between initiating a signal and the host delivering it belongs to the operating system.
|
|
265
|
+
*
|
|
266
|
+
* @returns True when the child's native exit was observed; false when the confirmation deadline elapsed without it
|
|
175
267
|
*/
|
|
176
|
-
stop(): Promise<
|
|
268
|
+
stop(): Promise<boolean>;
|
|
177
269
|
/**
|
|
178
|
-
* Stop the child and destroy the observation emitter.
|
|
270
|
+
* Stop the child, close its standard-input channel, and destroy the observation emitter.
|
|
271
|
+
*
|
|
272
|
+
* @remarks
|
|
273
|
+
* Always resolves, including when termination was never confirmed. Resolving does not imply the
|
|
274
|
+
* child's stdio has closed: a descendant holding an inherited pipe keeps the close event pending,
|
|
275
|
+
* so `exit` and `lines` can still be outstanding after this barrier settles.
|
|
179
276
|
*
|
|
180
277
|
* @returns The stable barrier shared by every call
|
|
181
278
|
*/
|
|
@@ -201,10 +298,12 @@ export declare type ProcessManagerEventMap = {
|
|
|
201
298
|
*
|
|
202
299
|
* @remarks
|
|
203
300
|
* A child that settles removes itself from the registry, so `count` and `processes` reflect only
|
|
204
|
-
* live children, and its departure emits `exit`.
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
301
|
+
* live children, and its departure emits `exit`. Eviction follows the child's own `exit` promise,
|
|
302
|
+
* which no listener can forge, so it lands one microtask after the child's public `exit` event: a
|
|
303
|
+
* listener on that event still sees the child registered. `launch` throws a {@link ProcessError}
|
|
304
|
+
* coded `duplicate` when the id is already live and `protocol` after `destroy` has begun — spawn
|
|
305
|
+
* faults surface through the returned child's `exit`, not from `launch`. The typed `emitter` carries
|
|
306
|
+
* the `launch` and `exit` moments across every child.
|
|
208
307
|
*/
|
|
209
308
|
export declare interface ProcessManagerInterface {
|
|
210
309
|
/** The typed fleet-level observation surface. */
|
|
@@ -227,35 +326,49 @@ export declare interface ProcessManagerInterface {
|
|
|
227
326
|
/**
|
|
228
327
|
* Spawn and register one child under `id`.
|
|
229
328
|
*
|
|
329
|
+
* @remarks
|
|
330
|
+
* Every option is read before the child is spawned, so a caller's own option getter runs while
|
|
331
|
+
* nothing has started and a throw from one strands no process. A getter that begins `destroy`
|
|
332
|
+
* without throwing is the one remaining race, and it leaves a bounded residual: the child is
|
|
333
|
+
* already spawned, so the launch is refused with `protocol` and that child is torn down
|
|
334
|
+
* asynchronously, bounded by `grace` plus the confirmation window. The `destroy` barrier settled
|
|
335
|
+
* before the refusal, so awaiting it does not cover that teardown.
|
|
336
|
+
*
|
|
230
337
|
* @param id - The registry key, unique among live children
|
|
231
338
|
* @param options - The child construction options
|
|
232
339
|
* @returns The launched child
|
|
233
|
-
* @throws A {@link ProcessError} coded `duplicate` when `id` is already live
|
|
340
|
+
* @throws A {@link ProcessError} coded `duplicate` when `id` is already live, `protocol` when the registry is being destroyed, or `invalid` when an option or command string is malformed
|
|
234
341
|
*/
|
|
235
342
|
launch(id: string, options: ProcessOptions): ProcessInterface;
|
|
236
343
|
/**
|
|
237
344
|
* Terminate the named children and await their exit.
|
|
238
345
|
*
|
|
239
346
|
* @param ids - The registry keys to stop
|
|
240
|
-
* @returns True when every named child
|
|
347
|
+
* @returns True when every named child was live and its exit was confirmed; false otherwise
|
|
241
348
|
*/
|
|
242
349
|
stop(ids: readonly string[]): Promise<boolean>;
|
|
243
350
|
/**
|
|
244
351
|
* Terminate one child and await its exit.
|
|
245
352
|
*
|
|
246
353
|
* @param id - The registry key to stop
|
|
247
|
-
* @returns True when the child was live and
|
|
354
|
+
* @returns True when the child was live and its exit was confirmed; false when the id was not live or the confirmation deadline elapsed
|
|
248
355
|
*/
|
|
249
356
|
stop(id: string): Promise<boolean>;
|
|
250
357
|
/**
|
|
251
358
|
* Terminate every live child and await their exit.
|
|
252
359
|
*
|
|
253
|
-
* @returns A promise that resolves after
|
|
360
|
+
* @returns A promise that resolves after every child stops
|
|
254
361
|
*/
|
|
255
362
|
stop(): Promise<void>;
|
|
256
363
|
/**
|
|
257
364
|
* Stop every child, then destroy the registry emitter last.
|
|
258
365
|
*
|
|
366
|
+
* @remarks
|
|
367
|
+
* Always resolves, and refuses a later `launch` with a {@link ProcessError} coded `protocol`.
|
|
368
|
+
* Every child is destroyed, not merely stopped, so each child's own observation emitter is
|
|
369
|
+
* destroyed too and every subscription on it goes silently inert. The registry emitter is
|
|
370
|
+
* destroyed last.
|
|
371
|
+
*
|
|
259
372
|
* @returns The stable barrier shared by every call
|
|
260
373
|
*/
|
|
261
374
|
destroy(): Promise<void>;
|
|
@@ -277,10 +390,13 @@ export declare interface ProcessManagerOptions {
|
|
|
277
390
|
* Construction options for one supervised child process.
|
|
278
391
|
*
|
|
279
392
|
* @remarks
|
|
280
|
-
* `grace` is the
|
|
281
|
-
*
|
|
282
|
-
* one arms its own timer and calls `stop`. `
|
|
283
|
-
* and `error` receives isolated listener
|
|
393
|
+
* `grace` is the cooperative window between `SIGTERM` and `SIGKILL` on a POSIX host; Windows has no
|
|
394
|
+
* cooperative phase, so the value is unused there. There is no completion deadline — a caller that
|
|
395
|
+
* wants one arms its own timer and calls `stop`. `backlog` bounds the unconsumed `lines` backlog.
|
|
396
|
+
* `on` installs initial {@link ProcessEventMap} listeners and `error` receives isolated listener
|
|
397
|
+
* failures. Every numeric option is validated at construction: a timer value outside
|
|
398
|
+
* `[0, PROCESS_TIMER]`, a negative or fractional byte value, or a `backlog` below `1` throws a
|
|
399
|
+
* {@link ProcessError} coded `invalid`.
|
|
284
400
|
*/
|
|
285
401
|
export declare interface ProcessOptions {
|
|
286
402
|
readonly on?: EmitterHooks<ProcessEventMap>;
|
|
@@ -288,24 +404,58 @@ export declare interface ProcessOptions {
|
|
|
288
404
|
readonly command: ProcessCommand;
|
|
289
405
|
/** The working directory the child runs in. */
|
|
290
406
|
readonly workspace: string;
|
|
291
|
-
/** Cooperative
|
|
292
|
-
readonly grace
|
|
407
|
+
/** Cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL`. Default: {@link PROCESS_GRACE}. */
|
|
408
|
+
readonly grace?: number;
|
|
293
409
|
/** Maximum retained stderr tail in bytes. Default: {@link PROCESS_EVIDENCE}. */
|
|
294
410
|
readonly evidence?: number;
|
|
411
|
+
/** Soft high-water mark in bytes for the unconsumed `lines` backlog. Default: {@link PROCESS_BACKLOG}. */
|
|
412
|
+
readonly backlog?: number;
|
|
295
413
|
/** If `true`, stdin stays open for {@link ProcessInterface.send}; if `false` or omitted, stdin closes after any initial `input`. */
|
|
296
414
|
readonly writable?: boolean;
|
|
297
415
|
/** Aborting this signal terminates the child through the same bounded `stop`. */
|
|
298
416
|
readonly signal?: AbortSignal;
|
|
299
417
|
}
|
|
300
418
|
|
|
419
|
+
/**
|
|
420
|
+
* The captured bytes and terminal facts one settled {@link RunResult} is built from.
|
|
421
|
+
*
|
|
422
|
+
* @remarks
|
|
423
|
+
* Both byte fields are trimmed to `limit` on a code-point boundary when the result is built, so a
|
|
424
|
+
* caller passes the raw retained head and never a decoded string. `cause` carries the host fault
|
|
425
|
+
* that ended the run, when one did; its presence alone marks the run failed.
|
|
426
|
+
*/
|
|
427
|
+
export declare interface RunInput {
|
|
428
|
+
/** The diagnostic command line. */
|
|
429
|
+
readonly command: string;
|
|
430
|
+
/** The retained standard-output bytes. */
|
|
431
|
+
readonly stdout: Uint8Array;
|
|
432
|
+
/** The retained standard-error bytes. */
|
|
433
|
+
readonly stderr: Uint8Array;
|
|
434
|
+
readonly code: number | null;
|
|
435
|
+
readonly signal: string | null;
|
|
436
|
+
/** If `true`, the run's own timeout elapsed; if `false`, it did not. */
|
|
437
|
+
readonly expired: boolean;
|
|
438
|
+
/** If `true`, the caller's signal aborted the run; if `false`, it did not. */
|
|
439
|
+
readonly aborted: boolean;
|
|
440
|
+
/** If `true`, a stream exceeded `limit`; if `false`, both fit. */
|
|
441
|
+
readonly truncated: boolean;
|
|
442
|
+
/** The maximum retained bytes for each stream. */
|
|
443
|
+
readonly limit: number;
|
|
444
|
+
/** The host fault that ended the run, when one did. */
|
|
445
|
+
readonly cause?: unknown;
|
|
446
|
+
}
|
|
447
|
+
|
|
301
448
|
/**
|
|
302
449
|
* Options for a one-shot run.
|
|
303
450
|
*
|
|
304
451
|
* @remarks
|
|
305
|
-
* A run is a fire-and-collect function, not a lifecycle entity, so it carries no emitter. `
|
|
306
|
-
*
|
|
307
|
-
* {@link ProcessError} carrying the {@link RunResult}; `
|
|
308
|
-
* instead, so the caller inspects `failed`.
|
|
452
|
+
* A run is a fire-and-collect function, not a lifecycle entity, so it carries no emitter. `strict`
|
|
453
|
+
* decides how a failure is delivered: by default a non-zero exit rejects with a
|
|
454
|
+
* {@link ProcessError} carrying the {@link RunResult}; `strict: false` resolves with the result
|
|
455
|
+
* instead, so the caller inspects `failed`. An invalid option or command rejects before the child is
|
|
456
|
+
* spawned, with a {@link ProcessError} coded `invalid`. An unbounded run awaits stdio completion
|
|
457
|
+
* rather than process exit, so give `timeout` a value wherever a descendant may inherit the child's
|
|
458
|
+
* stdio and hold the pipe open past the child's own exit.
|
|
309
459
|
*/
|
|
310
460
|
export declare interface RunOptions {
|
|
311
461
|
/** The working directory. Default: the current working directory. */
|
|
@@ -316,12 +466,12 @@ export declare interface RunOptions {
|
|
|
316
466
|
readonly input?: string;
|
|
317
467
|
/** Milliseconds before the child is terminated. `0` or omitted disables the timeout. */
|
|
318
468
|
readonly timeout?: number;
|
|
319
|
-
/** Cooperative window between `SIGTERM` and `SIGKILL` when terminating. Default: {@link PROCESS_GRACE}. */
|
|
469
|
+
/** Cooperative POSIX window between `SIGTERM` and `SIGKILL` when terminating. Default: {@link PROCESS_GRACE}. */
|
|
320
470
|
readonly grace?: number;
|
|
321
|
-
/** Aborting this signal terminates the run
|
|
471
|
+
/** Aborting this signal terminates the run and reports `aborted`. */
|
|
322
472
|
readonly signal?: AbortSignal;
|
|
323
473
|
/** If `false`, resolve with the result on failure instead of rejecting. Default: `true`. */
|
|
324
|
-
readonly
|
|
474
|
+
readonly strict?: boolean;
|
|
325
475
|
/** Maximum captured bytes for stdout and for stderr, each. Default: {@link PROCESS_OUTPUT}. */
|
|
326
476
|
readonly limit?: number;
|
|
327
477
|
}
|
|
@@ -330,22 +480,71 @@ export declare interface RunOptions {
|
|
|
330
480
|
* The settled outcome of a one-shot run: the buffered output and the terminal state.
|
|
331
481
|
*
|
|
332
482
|
* @remarks
|
|
333
|
-
* `failed` is `true` when the child exited non-zero, was killed by a signal,
|
|
334
|
-
* spawn. `
|
|
483
|
+
* `failed` is `true` when the child exited non-zero, was killed by a signal, expired, was aborted, or
|
|
484
|
+
* failed to spawn. `expired` and `aborted` are the two ways the run ended the child rather than the
|
|
485
|
+
* child ending itself, and only the first of them observed is recorded. `truncated` is independent of
|
|
486
|
+
* both: it reports that a stream exceeded `limit`, which fails a synchronous run and does not fail an
|
|
487
|
+
* asynchronous one.
|
|
335
488
|
*/
|
|
336
489
|
export declare interface RunResult {
|
|
337
490
|
/** The command line that was run, for diagnostics. */
|
|
338
491
|
readonly command: string;
|
|
339
|
-
/** The
|
|
492
|
+
/** The captured standard output, byte-bounded by `limit`. */
|
|
340
493
|
readonly stdout: string;
|
|
341
|
-
/** The
|
|
494
|
+
/** The captured standard error, byte-bounded by `limit`. */
|
|
342
495
|
readonly stderr: string;
|
|
343
496
|
readonly code: number | null;
|
|
344
497
|
readonly signal: string | null;
|
|
345
498
|
/** True if the run did not complete successfully. */
|
|
346
499
|
readonly failed: boolean;
|
|
347
500
|
/** True if the run's `timeout` elapsed before completion. */
|
|
348
|
-
readonly
|
|
501
|
+
readonly expired: boolean;
|
|
502
|
+
/** True if the caller's `signal` aborted the run before completion. */
|
|
503
|
+
readonly aborted: boolean;
|
|
504
|
+
/** True if either stream exceeded `limit`, so the captured text is the retained head. */
|
|
505
|
+
readonly truncated: boolean;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Options for a synchronous one-shot run.
|
|
510
|
+
*
|
|
511
|
+
* @remarks
|
|
512
|
+
* The synchronous host offers neither a cooperative termination window nor in-flight cancellation,
|
|
513
|
+
* so this contract carries no `grace` and no `signal`. A `timeout` and an output overflow both end
|
|
514
|
+
* the child with `SIGKILL`; an overflow reports `truncated` and `failed` together, which is where
|
|
515
|
+
* the synchronous and asynchronous runners genuinely differ.
|
|
516
|
+
*/
|
|
517
|
+
export declare interface RunSyncOptions {
|
|
518
|
+
/** The working directory. Default: the current working directory. */
|
|
519
|
+
readonly workspace?: string;
|
|
520
|
+
/** Environment overrides merged over the parent environment; an `undefined` value unsets a key. */
|
|
521
|
+
readonly environment?: Readonly<Record<string, string | undefined>>;
|
|
522
|
+
/** Standard input written to the child. */
|
|
523
|
+
readonly input?: string;
|
|
524
|
+
/** Milliseconds before the host kills the child. `0` or omitted disables the timeout. */
|
|
525
|
+
readonly timeout?: number;
|
|
526
|
+
/** If `false`, return the result on failure instead of throwing. Default: `true`. */
|
|
527
|
+
readonly strict?: boolean;
|
|
528
|
+
/** Maximum captured bytes for stdout and for stderr, each. Default: {@link PROCESS_OUTPUT}. */
|
|
529
|
+
readonly limit?: number;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* The resolved spawn form of one command: the executable to launch, the argument vector to pass,
|
|
534
|
+
* and whether the host receives that vector verbatim.
|
|
535
|
+
*
|
|
536
|
+
* @remarks
|
|
537
|
+
* No spawn in this package uses an implicit shell. `verbatim` is `true` only for a Windows `.cmd` or
|
|
538
|
+
* `.bat` script, which runs through an explicitly quoted `cmd.exe /d /s /c` command line, and an
|
|
539
|
+
* argument that batch target could corrupt — one carrying `%`, which `cmd.exe` expands before it
|
|
540
|
+
* parses quotes — is refused rather than passed. A shell metacharacter in an argument is therefore
|
|
541
|
+
* data rather than syntax on every path.
|
|
542
|
+
*/
|
|
543
|
+
export declare interface SpawnInput {
|
|
544
|
+
readonly file: string;
|
|
545
|
+
readonly arguments: readonly string[];
|
|
546
|
+
/** If `true`, pass the argument vector to the host verbatim; if `false`, let the host quote it. */
|
|
547
|
+
readonly verbatim: boolean;
|
|
349
548
|
}
|
|
350
549
|
|
|
351
550
|
export { }
|
package/dist/src/core/index.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { holds } from "@orkestrel/contract";
|
|
2
2
|
//#region src/core/constants.ts
|
|
3
|
-
/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */
|
|
3
|
+
/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */
|
|
4
4
|
var PROCESS_GRACE = 5e3;
|
|
5
|
+
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
6
|
+
var PROCESS_CONFIRMATION = 5e3;
|
|
5
7
|
/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
|
|
6
8
|
var PROCESS_EVIDENCE = 2048;
|
|
9
|
+
/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */
|
|
10
|
+
var PROCESS_BACKLOG = 10485760;
|
|
7
11
|
/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */
|
|
8
12
|
var PROCESS_OUTPUT = 10485760;
|
|
13
|
+
/** The largest timer delay in milliseconds the host schedules without truncating it to one. */
|
|
14
|
+
var PROCESS_TIMER = 2147483647;
|
|
15
|
+
/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */
|
|
16
|
+
var PROCESS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
9
17
|
//#endregion
|
|
10
18
|
//#region src/core/errors.ts
|
|
11
19
|
/** A child-process failure with a stable machine-readable category. */
|
|
@@ -57,24 +65,64 @@ function createDuplicateError(id) {
|
|
|
57
65
|
});
|
|
58
66
|
}
|
|
59
67
|
/**
|
|
68
|
+
* Creates the failure raised when a launch is attempted on a registry that is being destroyed.
|
|
69
|
+
*
|
|
70
|
+
* @param id - The id the refused launch asked for
|
|
71
|
+
* @returns A typed protocol failure
|
|
72
|
+
*/
|
|
73
|
+
function createProtocolError(id) {
|
|
74
|
+
return new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {
|
|
75
|
+
code: "protocol",
|
|
76
|
+
context: { id }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates the failure raised when a public input is refused before anything is spawned.
|
|
81
|
+
*
|
|
82
|
+
* @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`
|
|
83
|
+
* @param value - The rejected value, carried as `context.value`
|
|
84
|
+
* @returns A typed validation failure
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* createInvalidError("option 'grace'", -1).code // 'invalid'
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
function createInvalidError(subject, value) {
|
|
92
|
+
return new ProcessError(`Invalid ${subject}`, {
|
|
93
|
+
code: "invalid",
|
|
94
|
+
context: { value }
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
60
98
|
* Creates the failure raised when a run does not complete successfully and rejection is requested.
|
|
61
99
|
*
|
|
100
|
+
* @remarks
|
|
101
|
+
* The category is `timeout` only when the run's own timeout elapsed; every other failure, including
|
|
102
|
+
* an abort and an output overflow, is a `spawn` failure carrying its {@link RunResult}.
|
|
103
|
+
*
|
|
62
104
|
* @param result - The buffered run outcome that failed
|
|
105
|
+
* @param cause - The underlying host fault, when one ended the run
|
|
63
106
|
* @returns A typed run failure carrying its {@link RunResult}
|
|
64
107
|
*/
|
|
65
|
-
function createRunError(result) {
|
|
66
|
-
|
|
108
|
+
function createRunError(result, cause) {
|
|
109
|
+
let reason = `exited with code ${String(result.code)}`;
|
|
110
|
+
if (result.signal !== null) reason = `was killed by ${result.signal}`;
|
|
111
|
+
if (result.truncated) reason = "exceeded its output limit";
|
|
112
|
+
if (result.aborted) reason = "was aborted";
|
|
113
|
+
if (result.expired) reason = "timed out";
|
|
67
114
|
return new ProcessError(`Command '${result.command}' ${reason}`, {
|
|
68
|
-
code: result.
|
|
115
|
+
code: result.expired ? "timeout" : "spawn",
|
|
69
116
|
context: {
|
|
70
117
|
command: result.command,
|
|
71
118
|
code: result.code,
|
|
72
119
|
signal: result.signal
|
|
73
120
|
},
|
|
121
|
+
...cause === void 0 ? {} : { cause },
|
|
74
122
|
result
|
|
75
123
|
});
|
|
76
124
|
}
|
|
77
125
|
//#endregion
|
|
78
|
-
export { PROCESS_EVIDENCE, PROCESS_GRACE, PROCESS_OUTPUT, ProcessError, createDuplicateError, createRunError, isProcessError };
|
|
126
|
+
export { PROCESS_BACKLOG, PROCESS_CONFIRMATION, PROCESS_EVIDENCE, PROCESS_GRACE, PROCESS_OUTPUT, PROCESS_PATHEXT, PROCESS_TIMER, ProcessError, createDuplicateError, createInvalidError, createProtocolError, createRunError, isProcessError };
|
|
79
127
|
|
|
80
128
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n","import { holds } from '@orkestrel/contract'\nimport type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tRunResult,\n} from './types.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: RunResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\treturn holds(() => value instanceof ProcessError)\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @param result - The buffered run outcome that failed\n * @returns A typed run failure carrying its {@link RunResult}\n */\nexport function createRunError(result: RunResult): ProcessError {\n\
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n","import { holds } from '@orkestrel/contract'\nimport type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tRunResult,\n} from './types.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: RunResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\treturn holds(() => value instanceof ProcessError)\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure, including\n * an abort and an output overflow, is a `spawn` failure carrying its {@link RunResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link RunResult}\n */\nexport function createRunError(result: RunResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;AAGpC,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;;;ACV/B,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;AAcA,SAAgB,eAAe,OAAuC;CACrE,OAAO,YAAY,iBAAiB,YAAY;AACjD;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;AAaA,SAAgB,eAAe,QAAmB,OAA+B;CAChF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
|