@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.
@@ -1,5 +1,7 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { LineScanner } from './classes.linescanner.js';
3
+ const DEFAULT_DISCONNECT_GRACE_PERIOD_MS = 5000;
4
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
3
5
  /**
4
6
  * Transport that spawns a child process and communicates via stdin/stdout.
5
7
  * Extracted from the original RustBridge process management logic.
@@ -7,8 +9,8 @@ import { LineScanner } from './classes.linescanner.js';
7
9
  export class StdioTransport extends plugins.events.EventEmitter {
8
10
  options;
9
11
  childProcess = null;
12
+ generation = null;
10
13
  lineScanner;
11
- stderrRemainder = '';
12
14
  _connected = false;
13
15
  constructor(options) {
14
16
  super();
@@ -21,16 +23,49 @@ export class StdioTransport extends plugins.events.EventEmitter {
21
23
  /**
22
24
  * Spawn the child process. Resolves when the process is running (not necessarily ready).
23
25
  */
24
- async connect() {
26
+ connect() {
27
+ if (this.generation && !this.generation.closed) {
28
+ return Promise.reject(new Error('Stdio transport cannot connect until the previous child has closed'));
29
+ }
25
30
  const env = this.options.env
26
31
  ? { ...process.env, ...this.options.env }
27
32
  : { ...process.env };
28
- this.childProcess = plugins.childProcess.spawn(this.options.binaryPath, this.options.cliArgs, { stdio: ['pipe', 'pipe', 'pipe'], env });
29
- // Handle stderr with cross-chunk buffering
30
- this.childProcess.stderr?.on('data', (data) => {
31
- this.stderrRemainder += data.toString();
32
- const lines = this.stderrRemainder.split('\n');
33
- this.stderrRemainder = lines.pop();
33
+ let childProcess;
34
+ try {
35
+ childProcess = plugins.childProcess.spawn(this.options.binaryPath, this.options.cliArgs, { stdio: ['pipe', 'pipe', 'pipe'], env });
36
+ }
37
+ catch (error) {
38
+ return Promise.reject(this.toError(error, 'Failed to spawn child process'));
39
+ }
40
+ let resolveClose;
41
+ const closePromise = new Promise((resolve) => {
42
+ resolveClose = resolve;
43
+ });
44
+ const generation = {
45
+ childProcess,
46
+ stderrRemainder: '',
47
+ exited: false,
48
+ closed: false,
49
+ closePromise,
50
+ resolveClose,
51
+ disconnectPromise: null,
52
+ rejectDisconnect: null,
53
+ forceKillTimer: null,
54
+ sigtermAttempted: false,
55
+ sigkillAttempted: false,
56
+ sigtermError: null,
57
+ stdioDestroyError: null,
58
+ };
59
+ this.generation = generation;
60
+ this.childProcess = childProcess;
61
+ // Handle stderr with cross-chunk buffering.
62
+ childProcess.stderr?.on('data', (data) => {
63
+ if (this.generation !== generation || generation.closed) {
64
+ return;
65
+ }
66
+ generation.stderrRemainder += data.toString();
67
+ const lines = generation.stderrRemainder.split('\n');
68
+ generation.stderrRemainder = lines.pop();
34
69
  for (const line of lines) {
35
70
  const trimmed = line.trim();
36
71
  if (trimmed) {
@@ -38,99 +73,258 @@ export class StdioTransport extends plugins.events.EventEmitter {
38
73
  }
39
74
  }
40
75
  });
41
- // Handle stdout via LineScanner
42
- this.childProcess.stdout.on('data', (chunk) => {
76
+ // Handle stdout via LineScanner.
77
+ childProcess.stdout.on('data', (chunk) => {
78
+ if (this.generation !== generation || generation.closed) {
79
+ return;
80
+ }
43
81
  this.lineScanner.push(chunk, (line) => {
44
- this.emit('line', line);
82
+ if (this.generation === generation && !generation.closed) {
83
+ this.emit('line', line);
84
+ }
85
+ });
86
+ });
87
+ childProcess.on('exit', () => {
88
+ generation.exited = true;
89
+ if (this.generation === generation) {
90
+ this._connected = false;
91
+ }
92
+ // A descendant may inherit the child's stdio descriptors and otherwise
93
+ // keep ChildProcess 'close' from ever firing after the owned child exits.
94
+ // Allow already-queued data events through this poll turn, then close our
95
+ // endpoints so confirmation cannot retain that pipe graph indefinitely.
96
+ setImmediate(() => {
97
+ if (generation.closed) {
98
+ return;
99
+ }
100
+ this.destroyOwnedStdioEndpoint(generation, 'stdin');
101
+ this.destroyOwnedStdioEndpoint(generation, 'stdout');
102
+ this.destroyOwnedStdioEndpoint(generation, 'stderr');
45
103
  });
46
104
  });
47
- // Handle process exit
48
- this.childProcess.on('exit', (code, signal) => {
49
- // Flush remaining stderr
50
- if (this.stderrRemainder.trim()) {
51
- this.emit('stderr', this.stderrRemainder.trim());
105
+ // ChildProcess 'close' is the termination confirmation: the process has exited
106
+ // and its stdio streams have closed.
107
+ childProcess.on('close', (code, signal) => {
108
+ if (generation.closed) {
109
+ return;
110
+ }
111
+ // Flush stderr while this generation is still considered active so a
112
+ // synchronous listener cannot start a replacement child mid-cleanup.
113
+ if (this.generation === generation
114
+ && generation.stderrRemainder.trim()) {
115
+ this.emit('stderr', generation.stderrRemainder.trim());
116
+ }
117
+ generation.stderrRemainder = '';
118
+ generation.exited = true;
119
+ generation.closed = true;
120
+ if (generation.forceKillTimer) {
121
+ clearTimeout(generation.forceKillTimer);
122
+ generation.forceKillTimer = null;
123
+ }
124
+ generation.resolveClose();
125
+ if (this.generation !== generation) {
126
+ return;
52
127
  }
53
128
  this._connected = false;
129
+ this.childProcess = null;
54
130
  this.lineScanner.clear();
55
- this.stderrRemainder = '';
56
131
  this.emit('close', code, signal);
57
132
  });
58
- this.childProcess.on('error', (err) => {
133
+ childProcess.on('error', (error) => {
134
+ if (this.generation !== generation || generation.closed) {
135
+ return;
136
+ }
59
137
  this._connected = false;
60
- this.emit('error', err);
138
+ if (generation.sigkillAttempted && generation.rejectDisconnect) {
139
+ generation.rejectDisconnect(this.toError(error, 'Unable to confirm child termination after SIGKILL'));
140
+ }
141
+ if (this.listenerCount('error') > 0) {
142
+ this.emit('error', error);
143
+ }
144
+ else if (generation.disconnectPromise) {
145
+ this.options.logger.log('error', 'Stdio transport error during disconnect: ' + error.message);
146
+ }
147
+ else {
148
+ this.emit('error', error);
149
+ }
61
150
  });
62
151
  this._connected = true;
152
+ return Promise.resolve();
63
153
  }
64
154
  /**
65
155
  * Write data to stdin with backpressure support.
66
156
  */
67
- async write(data) {
157
+ write(data) {
68
158
  return new Promise((resolve, reject) => {
69
- if (!this.childProcess?.stdin) {
159
+ const generation = this.generation;
160
+ if (!generation
161
+ || generation.closed
162
+ || generation.childProcess !== this.childProcess
163
+ || !this._connected
164
+ || !generation.childProcess.stdin) {
70
165
  reject(new Error('stdin not available'));
71
166
  return;
72
167
  }
73
- const canContinue = this.childProcess.stdin.write(data, 'utf8', (err) => {
74
- if (err) {
75
- reject(err);
168
+ const canContinue = generation.childProcess.stdin.write(data, 'utf8', (error) => {
169
+ if (error) {
170
+ reject(error);
76
171
  }
77
172
  });
78
173
  if (canContinue) {
79
174
  resolve();
80
175
  }
81
176
  else {
82
- this.childProcess.stdin.once('drain', () => {
177
+ generation.childProcess.stdin.once('drain', () => {
83
178
  resolve();
84
179
  });
85
180
  }
86
181
  });
87
182
  }
88
183
  /**
89
- * Kill the child process. Sends SIGTERM, then SIGKILL after 5s.
184
+ * Kill the child process without waiting for confirmation.
90
185
  */
91
186
  disconnect() {
92
- if (!this.childProcess)
93
- return;
94
- const proc = this.childProcess;
95
- this.childProcess = null;
96
- this._connected = false;
97
- this.lineScanner.clear();
98
- this.stderrRemainder = '';
99
- // Remove all listeners
100
- proc.removeAllListeners();
101
- proc.stdout?.removeAllListeners();
102
- proc.stderr?.removeAllListeners();
103
- proc.stdin?.removeAllListeners();
104
- // Kill the process
105
187
  try {
106
- proc.kill('SIGTERM');
188
+ void this.disconnectAndWait().catch((error) => {
189
+ this.options.logger.log('error', 'Failed to disconnect stdio transport: ' + this.getErrorMessage(error));
190
+ });
107
191
  }
108
- catch { /* already dead */ }
109
- // Destroy stdio pipes
110
- try {
111
- proc.stdin?.destroy();
192
+ catch (error) {
193
+ this.options.logger.log('error', 'Failed to disconnect stdio transport: ' + this.getErrorMessage(error));
194
+ }
195
+ }
196
+ /**
197
+ * Send SIGTERM, escalate to SIGKILL after the grace period, and wait for
198
+ * ChildProcess 'close' to confirm termination.
199
+ */
200
+ disconnectAndWait(options = {}) {
201
+ const gracePeriodMs = options.gracePeriodMs ?? DEFAULT_DISCONNECT_GRACE_PERIOD_MS;
202
+ if (!Number.isSafeInteger(gracePeriodMs) || gracePeriodMs < 0) {
203
+ throw new RangeError('StdioTransport gracePeriodMs must be a non-negative safe integer');
204
+ }
205
+ const generation = this.generation;
206
+ if (!generation) {
207
+ return Promise.resolve();
208
+ }
209
+ if (generation.disconnectPromise) {
210
+ return generation.disconnectPromise;
211
+ }
212
+ if (generation.stdioDestroyError) {
213
+ generation.disconnectPromise = Promise.reject(generation.stdioDestroyError);
214
+ return generation.disconnectPromise;
215
+ }
216
+ if (generation.closed) {
217
+ generation.disconnectPromise = generation.closePromise;
218
+ return generation.disconnectPromise;
219
+ }
220
+ let rejectDisconnect;
221
+ const disconnectPromise = new Promise((resolve, reject) => {
222
+ rejectDisconnect = reject;
223
+ void generation.closePromise.then(resolve);
224
+ });
225
+ generation.disconnectPromise = disconnectPromise;
226
+ generation.rejectDisconnect = rejectDisconnect;
227
+ this._connected = false;
228
+ if (this.hasExited(generation)) {
229
+ return disconnectPromise;
230
+ }
231
+ generation.sigtermAttempted = true;
232
+ generation.sigtermError = this.sendSignal(generation, 'SIGTERM');
233
+ if (generation.closed || this.hasExited(generation)) {
234
+ return disconnectPromise;
112
235
  }
113
- catch { /* ignore */ }
236
+ if (gracePeriodMs === 0) {
237
+ this.forceKill(generation);
238
+ }
239
+ else {
240
+ this.scheduleForceKill(generation, gracePeriodMs);
241
+ }
242
+ return disconnectPromise;
243
+ }
244
+ scheduleForceKill(generation, remainingMs) {
245
+ if (generation.closed || this.hasExited(generation)) {
246
+ return;
247
+ }
248
+ const delayMs = Math.min(remainingMs, MAX_TIMER_DELAY_MS);
249
+ const scheduledAt = Date.now();
114
250
  try {
115
- proc.stdout?.destroy();
251
+ generation.forceKillTimer = setTimeout(() => {
252
+ generation.forceKillTimer = null;
253
+ if (generation.closed || this.hasExited(generation)) {
254
+ return;
255
+ }
256
+ const elapsedMs = Math.max(1, Date.now() - scheduledAt);
257
+ const nextRemainingMs = remainingMs - elapsedMs;
258
+ if (nextRemainingMs > 0) {
259
+ this.scheduleForceKill(generation, nextRemainingMs);
260
+ }
261
+ else {
262
+ this.forceKill(generation);
263
+ }
264
+ }, delayMs);
116
265
  }
117
- catch { /* ignore */ }
266
+ catch (error) {
267
+ generation.rejectDisconnect?.(this.toError(error, 'Unable to schedule SIGKILL escalation'));
268
+ }
269
+ }
270
+ forceKill(generation) {
271
+ if (generation.closed
272
+ || generation.sigkillAttempted
273
+ || this.hasExited(generation)) {
274
+ return;
275
+ }
276
+ generation.sigkillAttempted = true;
277
+ const sigkillError = this.sendSignal(generation, 'SIGKILL');
278
+ if (sigkillError && !generation.closed && !this.hasExited(generation)) {
279
+ const sigtermDetail = generation.sigtermError
280
+ ? ' SIGTERM also failed: ' + generation.sigtermError.message + '.'
281
+ : '';
282
+ generation.rejectDisconnect?.(new Error(sigkillError.message + '.' + sigtermDetail));
283
+ }
284
+ }
285
+ sendSignal(generation, signal) {
118
286
  try {
119
- proc.stderr?.destroy();
287
+ const delivered = generation.childProcess.kill(signal);
288
+ if (delivered || generation.closed || this.hasExited(generation)) {
289
+ return null;
290
+ }
291
+ return new Error('Failed to send ' + signal + ' to child process');
292
+ }
293
+ catch (error) {
294
+ if (generation.closed || this.hasExited(generation)) {
295
+ return null;
296
+ }
297
+ return this.toError(error, 'Failed to send ' + signal + ' to child process');
298
+ }
299
+ }
300
+ hasExited(generation) {
301
+ return (generation.exited
302
+ || generation.childProcess.exitCode !== null
303
+ || generation.childProcess.signalCode !== null);
304
+ }
305
+ destroyOwnedStdioEndpoint(generation, endpointName) {
306
+ const endpoint = generation.childProcess[endpointName];
307
+ if (!endpoint || endpoint.destroyed) {
308
+ return;
120
309
  }
121
- catch { /* ignore */ }
122
- // Unref so Node doesn't wait
123
310
  try {
124
- proc.unref();
311
+ endpoint.destroy();
125
312
  }
126
- catch { /* ignore */ }
127
- // Force kill after 5 seconds
128
- setTimeout(() => {
129
- try {
130
- proc.kill('SIGKILL');
313
+ catch (error) {
314
+ const endpointError = this.toError(error, 'Failed to close inherited child ' + endpointName);
315
+ generation.stdioDestroyError ??= endpointError;
316
+ generation.rejectDisconnect?.(endpointError);
317
+ this.options.logger.log('error', endpointError.message);
318
+ if (this.listenerCount('error') > 0) {
319
+ this.emit('error', endpointError);
131
320
  }
132
- catch { /* already dead */ }
133
- }, 5000).unref();
321
+ }
322
+ }
323
+ toError(error, prefix) {
324
+ return new Error(prefix + ': ' + this.getErrorMessage(error));
325
+ }
326
+ getErrorMessage(error) {
327
+ return error instanceof Error ? error.message : String(error);
134
328
  }
135
329
  }
136
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zdGRpb3RyYW5zcG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMuc3RkaW90cmFuc3BvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBWXZEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFlLFNBQVEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxZQUFZO0lBQ3JELE9BQU8sQ0FBeUI7SUFDaEMsWUFBWSxHQUE2QyxJQUFJLENBQUM7SUFDOUQsV0FBVyxDQUFjO0lBQ3pCLGVBQWUsR0FBVyxFQUFFLENBQUM7SUFDN0IsVUFBVSxHQUFZLEtBQUssQ0FBQztJQUVwQyxZQUFZLE9BQStCO1FBQ3pDLEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLFdBQVcsQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM3RSxDQUFDO0lBRUQsSUFBVyxTQUFTO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsT0FBTztRQUNsQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUc7WUFDMUIsQ0FBQyxDQUFDLEVBQUUsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDekMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFdkIsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FDNUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxV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
330
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zdGRpb3RyYW5zcG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMuc3RkaW90cmFuc3BvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBT3ZELE1BQU0sa0NBQWtDLEdBQUcsSUFBSSxDQUFDO0FBQ2hELE1BQU0sa0JBQWtCLEdBQUcsYUFBYSxDQUFDO0FBMEJ6Qzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sY0FBZSxTQUFRLE9BQU8sQ0FBQyxNQUFNLENBQUMsWUFBWTtJQUNyRCxPQUFPLENBQXlCO0lBQ2hDLFlBQVksR0FBNkMsSUFBSSxDQUFDO0lBQzlELFVBQVUsR0FBcUMsSUFBSSxDQUFDO0lBQ3BELFdBQVcsQ0FBYztJQUN6QixVQUFVLEdBQVksS0FBSyxDQUFDO0lBRXBDLFlBQVksT0FBK0I7UUFDekMsS0FBSyxFQUFFLENBQUM7UUFDUixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksV0FBVyxDQUFDLE9BQU8sQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFFRCxJQUFXLFNBQVM7UUFDbEIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7T0FFRztJQUNJLE9BQU87UUFDWixJQUFJLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQy9DLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FDbkIsSUFBSSxLQUFLLENBQUMsb0VBQW9FLENBQUMsQ0FDaEYsQ0FBQztRQUNKLENBQUM7UUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUc7WUFD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
@@ -13,6 +13,14 @@ export interface IRustBridgeRequestOptions {
13
13
  /** Override the bridge request timeout for this call, in milliseconds. */
14
14
  timeoutMs?: number;
15
15
  }
16
+ /** Options for confirmed bridge termination. */
17
+ export interface IRustBridgeTerminateOptions {
18
+ /**
19
+ * Time to allow an owned stdio child to exit after SIGTERM before SIGKILL.
20
+ * Zero requests immediate SIGKILL. Defaults to 5000 milliseconds.
21
+ */
22
+ gracePeriodMs?: number;
23
+ }
16
24
  /**
17
25
  * Options for locating a Rust binary.
18
26
  */
@@ -1,4 +1,9 @@
1
1
  import type * as plugins from '../plugins.js';
2
+ /** Options for confirmed transport disconnection. */
3
+ export interface IRustTransportDisconnectOptions {
4
+ /** Grace period before escalating process termination, in milliseconds. */
5
+ gracePeriodMs?: number;
6
+ }
2
7
  /**
3
8
  * Abstract transport for communicating with a Rust process.
4
9
  * Both stdio and socket transports implement this interface.
@@ -17,6 +22,10 @@ export interface IRustTransport extends plugins.events.EventEmitter {
17
22
  write(data: string): Promise<void>;
18
23
  /** Disconnect the transport. For stdio: kills the process. For socket: closes the connection. */
19
24
  disconnect(): void;
25
+ /** Disconnect and resolve only after the local transport has confirmed closure. */
26
+ disconnectAndWait?(options?: IRustTransportDisconnectOptions): Promise<void>;
20
27
  /** Whether the transport is currently connected and writable. */
21
28
  readonly connected: boolean;
29
+ /** Whether the transport is preserving its lifecycle for an automatic reconnect. */
30
+ readonly reconnecting?: boolean;
22
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartrust",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
4
4
  "private": false,
5
5
  "description": "A type-safe bridge between JavaScript engines and Rust binaries.",
6
6
  "main": "dist_ts/index.js",
package/readme.md CHANGED
@@ -99,8 +99,8 @@ bridge.on('management:configChanged', (data) => {
99
99
  console.log('Config was changed:', data);
100
100
  });
101
101
 
102
- // Clean shutdown (SIGTERM → SIGKILL after 5s)
103
- bridge.kill();
102
+ // Confirmed shutdown (SIGTERM → SIGKILL after 5s)
103
+ await bridge.terminate();
104
104
  ```
105
105
 
106
106
  ### Per-Command Deadlines and Cancellation
@@ -154,6 +154,25 @@ programmatic handling:
154
154
  | `ERR_RUST_BRIDGE_REQUEST_TRANSPORT` | The transport exited, disconnected, or was killed |
155
155
  | `ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE` | Rust returned `success: false` |
156
156
 
157
+ ### Confirmed Termination
158
+
159
+ Use `await bridge.terminate()` when later work must not overlap the current
160
+ worker. In stdio mode it sends SIGTERM, escalates to SIGKILL after five seconds,
161
+ and resolves only after the child process and its stdio handles have closed.
162
+ The grace period can be changed per termination; zero requests immediate
163
+ SIGKILL:
164
+
165
+ ```typescript
166
+ await bridge.terminate({ gracePeriodMs: 250 });
167
+ ```
168
+
169
+ Concurrent calls share one termination operation, and calls after confirmed
170
+ closure are no-ops. The first concurrent caller's grace period applies. In
171
+ socket mode, termination confirms only the local socket closure; the remote
172
+ daemon is never signaled. `kill()` remains available for compatibility as a
173
+ fire-and-forget wrapper, but callers that require confirmed ownership release
174
+ must await `terminate()`.
175
+
157
176
  ### Socket Mode — Connect to a Running Daemon 🔗
158
177
 
159
178
  When the Rust binary runs as a system service (e.g., via `systemd`, `launchd`, or a Windows Service), use `connect()` to talk to it over a Unix socket or named pipe:
@@ -174,8 +193,8 @@ if (!ok) {
174
193
  const { pid } = await bridge.sendCommand('start', { port: 8080, host: '0.0.0.0' });
175
194
  const metrics = await bridge.sendCommand('getMetrics', {});
176
195
 
177
- // kill() closes the socket — it does NOT kill the daemon
178
- bridge.kill();
196
+ // terminate() confirms local socket closure — it does NOT kill the daemon
197
+ await bridge.terminate();
179
198
  ```
180
199
 
181
200
  #### Auto-Reconnect
@@ -539,7 +558,8 @@ fn main() {
539
558
  | `connect(socketPath, options?)` | `Promise<boolean>` | **Socket mode**: Connect to a running daemon; returns `false` on failure |
540
559
  | `sendCommand(method, params, options?)` | `Promise<TCommands[K]['result']>` | Send a typed command with an optional per-call timeout or abort signal |
541
560
  | `sendCommandStreaming(method, params)` | `StreamingResponse<TChunk, TResult>` | Send a streaming command; returns immediately |
542
- | `kill()` | `void` | Stdio: SIGTERM the process, SIGKILL after 5s. Socket: close the connection (daemon stays alive) |
561
+ | `terminate(options?)` | `Promise<void>` | Confirm stdio child exit or local socket closure; `gracePeriodMs` controls SIGKILL escalation |
562
+ | `kill()` | `void` | Fire-and-forget compatibility wrapper around `terminate()` |
543
563
  | `running` | `boolean` | Whether the bridge is currently connected and ready |
544
564
 
545
565
  ### `StreamingResponse<TChunk, TResult>`
@@ -565,6 +585,7 @@ fn main() {
565
585
  | `connect()` | `Promise<void>` | Spawn the child process |
566
586
  | `write(data)` | `Promise<void>` | Write to stdin with backpressure handling |
567
587
  | `disconnect()` | `void` | Kill the process (SIGTERM → SIGKILL after 5s) |
588
+ | `disconnectAndWait(options?)` | `Promise<void>` | Confirm child close after SIGTERM/SIGKILL escalation |
568
589
  | `connected` | `boolean` | Whether the process is running |
569
590
 
570
591
  ### `SocketTransport`
@@ -575,7 +596,9 @@ fn main() {
575
596
  | `connect()` | `Promise<void>` | Connect to the Unix socket / named pipe |
576
597
  | `write(data)` | `Promise<void>` | Write to socket with backpressure handling |
577
598
  | `disconnect()` | `void` | Close the socket (does not kill the daemon) |
599
+ | `disconnectAndWait()` | `Promise<void>` | Confirm local socket closure without signaling the daemon |
578
600
  | `connected` | `boolean` | Whether the socket is connected |
601
+ | `reconnecting` | `boolean` | Whether an automatic reconnect timer or attempt owns the socket lifecycle |
579
602
 
580
603
  ### `LineScanner`
581
604
 
@@ -591,6 +614,8 @@ fn main() {
591
614
  |---|---|
592
615
  | `IRustBridgeOptions` | Full configuration for `RustBridge` |
593
616
  | `IRustBridgeRequestOptions` | Per-command options: `{ signal?, timeoutMs? }` |
617
+ | `IRustBridgeTerminateOptions` | Confirmed termination options: `{ gracePeriodMs? }` |
618
+ | `IRustTransportDisconnectOptions` | Optional confirmed-disconnect grace period for owned process transports |
594
619
  | `IBinaryLocatorOptions` | Configuration for `RustBinaryLocator` |
595
620
  | `ISocketConnectOptions` | Socket connection options (reconnect settings) |
596
621
  | `IRustBridgeLogger` | Logger interface: `{ log(level, message, data?) }` |
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartrust',
6
- version: '1.4.1',
6
+ version: '1.6.0',
7
7
  description: 'A type-safe bridge between JavaScript engines and Rust binaries.'
8
8
  }