@push.rocks/smartrust 1.4.1 → 1.6.0
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.rustbridge.d.ts +19 -8
- package/dist_ts/classes.rustbridge.js +302 -78
- package/dist_ts/classes.sockettransport.d.ts +16 -2
- package/dist_ts/classes.sockettransport.js +234 -61
- package/dist_ts/classes.stdiotransport.d.ts +15 -3
- package/dist_ts/classes.stdiotransport.js +253 -59
- package/dist_ts/interfaces/config.d.ts +8 -0
- package/dist_ts/interfaces/transport.d.ts +9 -0
- package/package.json +1 -1
- package/readme.md +30 -5
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.rustbridge.ts +363 -83
- package/ts/classes.sockettransport.ts +304 -60
- package/ts/classes.stdiotransport.ts +345 -58
- package/ts/interfaces/config.ts +9 -0
- package/ts/interfaces/transport.ts +12 -0
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { LineScanner } from './classes.linescanner.js';
|
|
3
3
|
import type { IRustBridgeLogger } from './interfaces/index.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
IRustTransport,
|
|
6
|
+
IRustTransportDisconnectOptions,
|
|
7
|
+
} from './interfaces/transport.js';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_DISCONNECT_GRACE_PERIOD_MS = 5000;
|
|
10
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
5
11
|
|
|
6
12
|
export interface IStdioTransportOptions {
|
|
7
13
|
binaryPath: string;
|
|
@@ -11,6 +17,22 @@ export interface IStdioTransportOptions {
|
|
|
11
17
|
logger: IRustBridgeLogger;
|
|
12
18
|
}
|
|
13
19
|
|
|
20
|
+
interface IStdioTransportGeneration {
|
|
21
|
+
childProcess: plugins.childProcess.ChildProcess;
|
|
22
|
+
stderrRemainder: string;
|
|
23
|
+
exited: boolean;
|
|
24
|
+
closed: boolean;
|
|
25
|
+
closePromise: Promise<void>;
|
|
26
|
+
resolveClose: () => void;
|
|
27
|
+
disconnectPromise: Promise<void> | null;
|
|
28
|
+
rejectDisconnect: ((error: Error) => void) | null;
|
|
29
|
+
forceKillTimer: ReturnType<typeof setTimeout> | null;
|
|
30
|
+
sigtermAttempted: boolean;
|
|
31
|
+
sigkillAttempted: boolean;
|
|
32
|
+
sigtermError: Error | null;
|
|
33
|
+
stdioDestroyError: Error | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
14
36
|
/**
|
|
15
37
|
* Transport that spawns a child process and communicates via stdin/stdout.
|
|
16
38
|
* Extracted from the original RustBridge process management logic.
|
|
@@ -18,8 +40,8 @@ export interface IStdioTransportOptions {
|
|
|
18
40
|
export class StdioTransport extends plugins.events.EventEmitter implements IRustTransport {
|
|
19
41
|
private options: IStdioTransportOptions;
|
|
20
42
|
private childProcess: plugins.childProcess.ChildProcess | null = null;
|
|
43
|
+
private generation: IStdioTransportGeneration | null = null;
|
|
21
44
|
private lineScanner: LineScanner;
|
|
22
|
-
private stderrRemainder: string = '';
|
|
23
45
|
private _connected: boolean = false;
|
|
24
46
|
|
|
25
47
|
constructor(options: IStdioTransportOptions) {
|
|
@@ -35,22 +57,60 @@ export class StdioTransport extends plugins.events.EventEmitter implements IRust
|
|
|
35
57
|
/**
|
|
36
58
|
* Spawn the child process. Resolves when the process is running (not necessarily ready).
|
|
37
59
|
*/
|
|
38
|
-
public
|
|
60
|
+
public connect(): Promise<void> {
|
|
61
|
+
if (this.generation && !this.generation.closed) {
|
|
62
|
+
return Promise.reject(
|
|
63
|
+
new Error('Stdio transport cannot connect until the previous child has closed'),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
39
67
|
const env = this.options.env
|
|
40
68
|
? { ...process.env, ...this.options.env }
|
|
41
69
|
: { ...process.env };
|
|
42
70
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
let childProcess: plugins.childProcess.ChildProcess;
|
|
72
|
+
try {
|
|
73
|
+
childProcess = plugins.childProcess.spawn(
|
|
74
|
+
this.options.binaryPath,
|
|
75
|
+
this.options.cliArgs,
|
|
76
|
+
{ stdio: ['pipe', 'pipe', 'pipe'], env },
|
|
77
|
+
);
|
|
78
|
+
} catch (error: unknown) {
|
|
79
|
+
return Promise.reject(this.toError(error, 'Failed to spawn child process'));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let resolveClose!: () => void;
|
|
83
|
+
const closePromise = new Promise<void>((resolve) => {
|
|
84
|
+
resolveClose = resolve;
|
|
85
|
+
});
|
|
86
|
+
const generation: IStdioTransportGeneration = {
|
|
87
|
+
childProcess,
|
|
88
|
+
stderrRemainder: '',
|
|
89
|
+
exited: false,
|
|
90
|
+
closed: false,
|
|
91
|
+
closePromise,
|
|
92
|
+
resolveClose,
|
|
93
|
+
disconnectPromise: null,
|
|
94
|
+
rejectDisconnect: null,
|
|
95
|
+
forceKillTimer: null,
|
|
96
|
+
sigtermAttempted: false,
|
|
97
|
+
sigkillAttempted: false,
|
|
98
|
+
sigtermError: null,
|
|
99
|
+
stdioDestroyError: null,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
this.generation = generation;
|
|
103
|
+
this.childProcess = childProcess;
|
|
104
|
+
|
|
105
|
+
// Handle stderr with cross-chunk buffering.
|
|
106
|
+
childProcess.stderr?.on('data', (data: Buffer) => {
|
|
107
|
+
if (this.generation !== generation || generation.closed) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
48
110
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const lines = this.stderrRemainder.split('\n');
|
|
53
|
-
this.stderrRemainder = lines.pop()!;
|
|
111
|
+
generation.stderrRemainder += data.toString();
|
|
112
|
+
const lines = generation.stderrRemainder.split('\n');
|
|
113
|
+
generation.stderrRemainder = lines.pop()!;
|
|
54
114
|
for (const line of lines) {
|
|
55
115
|
const trimmed = line.trim();
|
|
56
116
|
if (trimmed) {
|
|
@@ -59,53 +119,125 @@ export class StdioTransport extends plugins.events.EventEmitter implements IRust
|
|
|
59
119
|
}
|
|
60
120
|
});
|
|
61
121
|
|
|
62
|
-
// Handle stdout via LineScanner
|
|
63
|
-
|
|
122
|
+
// Handle stdout via LineScanner.
|
|
123
|
+
childProcess.stdout!.on('data', (chunk: Buffer) => {
|
|
124
|
+
if (this.generation !== generation || generation.closed) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
64
128
|
this.lineScanner.push(chunk, (line) => {
|
|
65
|
-
this.
|
|
129
|
+
if (this.generation === generation && !generation.closed) {
|
|
130
|
+
this.emit('line', line);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
childProcess.on('exit', () => {
|
|
136
|
+
generation.exited = true;
|
|
137
|
+
if (this.generation === generation) {
|
|
138
|
+
this._connected = false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// A descendant may inherit the child's stdio descriptors and otherwise
|
|
142
|
+
// keep ChildProcess 'close' from ever firing after the owned child exits.
|
|
143
|
+
// Allow already-queued data events through this poll turn, then close our
|
|
144
|
+
// endpoints so confirmation cannot retain that pipe graph indefinitely.
|
|
145
|
+
setImmediate(() => {
|
|
146
|
+
if (generation.closed) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.destroyOwnedStdioEndpoint(generation, 'stdin');
|
|
150
|
+
this.destroyOwnedStdioEndpoint(generation, 'stdout');
|
|
151
|
+
this.destroyOwnedStdioEndpoint(generation, 'stderr');
|
|
66
152
|
});
|
|
67
153
|
});
|
|
68
154
|
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
155
|
+
// ChildProcess 'close' is the termination confirmation: the process has exited
|
|
156
|
+
// and its stdio streams have closed.
|
|
157
|
+
childProcess.on('close', (code: number | null, signal: NodeJS.Signals | null) => {
|
|
158
|
+
if (generation.closed) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Flush stderr while this generation is still considered active so a
|
|
163
|
+
// synchronous listener cannot start a replacement child mid-cleanup.
|
|
164
|
+
if (
|
|
165
|
+
this.generation === generation
|
|
166
|
+
&& generation.stderrRemainder.trim()
|
|
167
|
+
) {
|
|
168
|
+
this.emit('stderr', generation.stderrRemainder.trim());
|
|
169
|
+
}
|
|
170
|
+
generation.stderrRemainder = '';
|
|
171
|
+
generation.exited = true;
|
|
172
|
+
generation.closed = true;
|
|
173
|
+
if (generation.forceKillTimer) {
|
|
174
|
+
clearTimeout(generation.forceKillTimer);
|
|
175
|
+
generation.forceKillTimer = null;
|
|
176
|
+
}
|
|
177
|
+
generation.resolveClose();
|
|
178
|
+
|
|
179
|
+
if (this.generation !== generation) {
|
|
180
|
+
return;
|
|
74
181
|
}
|
|
182
|
+
|
|
75
183
|
this._connected = false;
|
|
184
|
+
this.childProcess = null;
|
|
76
185
|
this.lineScanner.clear();
|
|
77
|
-
this.stderrRemainder = '';
|
|
78
186
|
this.emit('close', code, signal);
|
|
79
187
|
});
|
|
80
188
|
|
|
81
|
-
|
|
189
|
+
childProcess.on('error', (error: Error) => {
|
|
190
|
+
if (this.generation !== generation || generation.closed) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
82
194
|
this._connected = false;
|
|
83
|
-
|
|
195
|
+
if (generation.sigkillAttempted && generation.rejectDisconnect) {
|
|
196
|
+
generation.rejectDisconnect(
|
|
197
|
+
this.toError(error, 'Unable to confirm child termination after SIGKILL'),
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (this.listenerCount('error') > 0) {
|
|
202
|
+
this.emit('error', error);
|
|
203
|
+
} else if (generation.disconnectPromise) {
|
|
204
|
+
this.options.logger.log('error', 'Stdio transport error during disconnect: ' + error.message);
|
|
205
|
+
} else {
|
|
206
|
+
this.emit('error', error);
|
|
207
|
+
}
|
|
84
208
|
});
|
|
85
209
|
|
|
86
210
|
this._connected = true;
|
|
211
|
+
return Promise.resolve();
|
|
87
212
|
}
|
|
88
213
|
|
|
89
214
|
/**
|
|
90
215
|
* Write data to stdin with backpressure support.
|
|
91
216
|
*/
|
|
92
|
-
public
|
|
217
|
+
public write(data: string): Promise<void> {
|
|
93
218
|
return new Promise<void>((resolve, reject) => {
|
|
94
|
-
|
|
219
|
+
const generation = this.generation;
|
|
220
|
+
if (
|
|
221
|
+
!generation
|
|
222
|
+
|| generation.closed
|
|
223
|
+
|| generation.childProcess !== this.childProcess
|
|
224
|
+
|| !this._connected
|
|
225
|
+
|| !generation.childProcess.stdin
|
|
226
|
+
) {
|
|
95
227
|
reject(new Error('stdin not available'));
|
|
96
228
|
return;
|
|
97
229
|
}
|
|
98
230
|
|
|
99
|
-
const canContinue =
|
|
100
|
-
if (
|
|
101
|
-
reject(
|
|
231
|
+
const canContinue = generation.childProcess.stdin.write(data, 'utf8', (error) => {
|
|
232
|
+
if (error) {
|
|
233
|
+
reject(error);
|
|
102
234
|
}
|
|
103
235
|
});
|
|
104
236
|
|
|
105
237
|
if (canContinue) {
|
|
106
238
|
resolve();
|
|
107
239
|
} else {
|
|
108
|
-
|
|
240
|
+
generation.childProcess.stdin.once('drain', () => {
|
|
109
241
|
resolve();
|
|
110
242
|
});
|
|
111
243
|
}
|
|
@@ -113,37 +245,192 @@ export class StdioTransport extends plugins.events.EventEmitter implements IRust
|
|
|
113
245
|
}
|
|
114
246
|
|
|
115
247
|
/**
|
|
116
|
-
* Kill the child process
|
|
248
|
+
* Kill the child process without waiting for confirmation.
|
|
117
249
|
*/
|
|
118
250
|
public disconnect(): void {
|
|
119
|
-
|
|
251
|
+
try {
|
|
252
|
+
void this.disconnectAndWait().catch((error: unknown) => {
|
|
253
|
+
this.options.logger.log(
|
|
254
|
+
'error',
|
|
255
|
+
'Failed to disconnect stdio transport: ' + this.getErrorMessage(error),
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
} catch (error: unknown) {
|
|
259
|
+
this.options.logger.log(
|
|
260
|
+
'error',
|
|
261
|
+
'Failed to disconnect stdio transport: ' + this.getErrorMessage(error),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Send SIGTERM, escalate to SIGKILL after the grace period, and wait for
|
|
268
|
+
* ChildProcess 'close' to confirm termination.
|
|
269
|
+
*/
|
|
270
|
+
public disconnectAndWait(
|
|
271
|
+
options: IRustTransportDisconnectOptions = {},
|
|
272
|
+
): Promise<void> {
|
|
273
|
+
const gracePeriodMs = options.gracePeriodMs ?? DEFAULT_DISCONNECT_GRACE_PERIOD_MS;
|
|
274
|
+
if (!Number.isSafeInteger(gracePeriodMs) || gracePeriodMs < 0) {
|
|
275
|
+
throw new RangeError(
|
|
276
|
+
'StdioTransport gracePeriodMs must be a non-negative safe integer',
|
|
277
|
+
);
|
|
278
|
+
}
|
|
120
279
|
|
|
121
|
-
const
|
|
122
|
-
|
|
280
|
+
const generation = this.generation;
|
|
281
|
+
if (!generation) {
|
|
282
|
+
return Promise.resolve();
|
|
283
|
+
}
|
|
284
|
+
if (generation.disconnectPromise) {
|
|
285
|
+
return generation.disconnectPromise;
|
|
286
|
+
}
|
|
287
|
+
if (generation.stdioDestroyError) {
|
|
288
|
+
generation.disconnectPromise = Promise.reject(generation.stdioDestroyError);
|
|
289
|
+
return generation.disconnectPromise;
|
|
290
|
+
}
|
|
291
|
+
if (generation.closed) {
|
|
292
|
+
generation.disconnectPromise = generation.closePromise;
|
|
293
|
+
return generation.disconnectPromise;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let rejectDisconnect!: (error: Error) => void;
|
|
297
|
+
const disconnectPromise = new Promise<void>((resolve, reject) => {
|
|
298
|
+
rejectDisconnect = reject;
|
|
299
|
+
void generation.closePromise.then(resolve);
|
|
300
|
+
});
|
|
301
|
+
generation.disconnectPromise = disconnectPromise;
|
|
302
|
+
generation.rejectDisconnect = rejectDisconnect;
|
|
123
303
|
this._connected = false;
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
304
|
+
|
|
305
|
+
if (this.hasExited(generation)) {
|
|
306
|
+
return disconnectPromise;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
generation.sigtermAttempted = true;
|
|
310
|
+
generation.sigtermError = this.sendSignal(generation, 'SIGTERM');
|
|
311
|
+
|
|
312
|
+
if (generation.closed || this.hasExited(generation)) {
|
|
313
|
+
return disconnectPromise;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (gracePeriodMs === 0) {
|
|
317
|
+
this.forceKill(generation);
|
|
318
|
+
} else {
|
|
319
|
+
this.scheduleForceKill(generation, gracePeriodMs);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return disconnectPromise;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private scheduleForceKill(
|
|
326
|
+
generation: IStdioTransportGeneration,
|
|
327
|
+
remainingMs: number,
|
|
328
|
+
): void {
|
|
329
|
+
if (generation.closed || this.hasExited(generation)) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const delayMs = Math.min(remainingMs, MAX_TIMER_DELAY_MS);
|
|
334
|
+
const scheduledAt = Date.now();
|
|
335
|
+
try {
|
|
336
|
+
generation.forceKillTimer = setTimeout(() => {
|
|
337
|
+
generation.forceKillTimer = null;
|
|
338
|
+
if (generation.closed || this.hasExited(generation)) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const elapsedMs = Math.max(1, Date.now() - scheduledAt);
|
|
343
|
+
const nextRemainingMs = remainingMs - elapsedMs;
|
|
344
|
+
if (nextRemainingMs > 0) {
|
|
345
|
+
this.scheduleForceKill(generation, nextRemainingMs);
|
|
346
|
+
} else {
|
|
347
|
+
this.forceKill(generation);
|
|
348
|
+
}
|
|
349
|
+
}, delayMs);
|
|
350
|
+
} catch (error: unknown) {
|
|
351
|
+
generation.rejectDisconnect?.(
|
|
352
|
+
this.toError(error, 'Unable to schedule SIGKILL escalation'),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private forceKill(generation: IStdioTransportGeneration): void {
|
|
358
|
+
if (
|
|
359
|
+
generation.closed
|
|
360
|
+
|| generation.sigkillAttempted
|
|
361
|
+
|| this.hasExited(generation)
|
|
362
|
+
) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
generation.sigkillAttempted = true;
|
|
367
|
+
const sigkillError = this.sendSignal(generation, 'SIGKILL');
|
|
368
|
+
if (sigkillError && !generation.closed && !this.hasExited(generation)) {
|
|
369
|
+
const sigtermDetail = generation.sigtermError
|
|
370
|
+
? ' SIGTERM also failed: ' + generation.sigtermError.message + '.'
|
|
371
|
+
: '';
|
|
372
|
+
generation.rejectDisconnect?.(
|
|
373
|
+
new Error(sigkillError.message + '.' + sigtermDetail),
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private sendSignal(
|
|
379
|
+
generation: IStdioTransportGeneration,
|
|
380
|
+
signal: NodeJS.Signals,
|
|
381
|
+
): Error | null {
|
|
382
|
+
try {
|
|
383
|
+
const delivered = generation.childProcess.kill(signal);
|
|
384
|
+
if (delivered || generation.closed || this.hasExited(generation)) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
return new Error('Failed to send ' + signal + ' to child process');
|
|
388
|
+
} catch (error: unknown) {
|
|
389
|
+
if (generation.closed || this.hasExited(generation)) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
return this.toError(error, 'Failed to send ' + signal + ' to child process');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private hasExited(generation: IStdioTransportGeneration): boolean {
|
|
397
|
+
return (
|
|
398
|
+
generation.exited
|
|
399
|
+
|| generation.childProcess.exitCode !== null
|
|
400
|
+
|| generation.childProcess.signalCode !== null
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private destroyOwnedStdioEndpoint(
|
|
405
|
+
generation: IStdioTransportGeneration,
|
|
406
|
+
endpointName: 'stdin' | 'stdout' | 'stderr',
|
|
407
|
+
): void {
|
|
408
|
+
const endpoint = generation.childProcess[endpointName];
|
|
409
|
+
if (!endpoint || endpoint.destroyed) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
endpoint.destroy();
|
|
415
|
+
} catch (error: unknown) {
|
|
416
|
+
const endpointError = this.toError(
|
|
417
|
+
error,
|
|
418
|
+
'Failed to close inherited child ' + endpointName,
|
|
419
|
+
);
|
|
420
|
+
generation.stdioDestroyError ??= endpointError;
|
|
421
|
+
generation.rejectDisconnect?.(endpointError);
|
|
422
|
+
this.options.logger.log('error', endpointError.message);
|
|
423
|
+
if (this.listenerCount('error') > 0) {
|
|
424
|
+
this.emit('error', endpointError);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private toError(error: unknown, prefix: string): Error {
|
|
430
|
+
return new Error(prefix + ': ' + this.getErrorMessage(error));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private getErrorMessage(error: unknown): string {
|
|
434
|
+
return error instanceof Error ? error.message : String(error);
|
|
148
435
|
}
|
|
149
436
|
}
|
package/ts/interfaces/config.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface IRustBridgeRequestOptions {
|
|
|
15
15
|
timeoutMs?: number;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/** Options for confirmed bridge termination. */
|
|
19
|
+
export interface IRustBridgeTerminateOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Time to allow an owned stdio child to exit after SIGTERM before SIGKILL.
|
|
22
|
+
* Zero requests immediate SIGKILL. Defaults to 5000 milliseconds.
|
|
23
|
+
*/
|
|
24
|
+
gracePeriodMs?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
/**
|
|
19
28
|
* Options for locating a Rust binary.
|
|
20
29
|
*/
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type * as plugins from '../plugins.js';
|
|
2
2
|
|
|
3
|
+
/** Options for confirmed transport disconnection. */
|
|
4
|
+
export interface IRustTransportDisconnectOptions {
|
|
5
|
+
/** Grace period before escalating process termination, in milliseconds. */
|
|
6
|
+
gracePeriodMs?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
/**
|
|
4
10
|
* Abstract transport for communicating with a Rust process.
|
|
5
11
|
* Both stdio and socket transports implement this interface.
|
|
@@ -21,6 +27,12 @@ export interface IRustTransport extends plugins.events.EventEmitter {
|
|
|
21
27
|
/** Disconnect the transport. For stdio: kills the process. For socket: closes the connection. */
|
|
22
28
|
disconnect(): void;
|
|
23
29
|
|
|
30
|
+
/** Disconnect and resolve only after the local transport has confirmed closure. */
|
|
31
|
+
disconnectAndWait?(options?: IRustTransportDisconnectOptions): Promise<void>;
|
|
32
|
+
|
|
24
33
|
/** Whether the transport is currently connected and writable. */
|
|
25
34
|
readonly connected: boolean;
|
|
35
|
+
|
|
36
|
+
/** Whether the transport is preserving its lifecycle for an automatic reconnect. */
|
|
37
|
+
readonly reconnecting?: boolean;
|
|
26
38
|
}
|