@mcp-z/client 2.2.3 → 2.3.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.
Files changed (111) hide show
  1. package/README.md +12 -1
  2. package/dist/cjs/auth/capability-discovery.d.cts +3 -1
  3. package/dist/cjs/auth/capability-discovery.d.ts +3 -1
  4. package/dist/cjs/auth/capability-discovery.js +46 -32
  5. package/dist/cjs/auth/capability-discovery.js.map +1 -1
  6. package/dist/cjs/auth/discovery-fetch.d.cts +2 -0
  7. package/dist/cjs/auth/discovery-fetch.d.ts +2 -0
  8. package/dist/cjs/auth/discovery-fetch.js +85 -31
  9. package/dist/cjs/auth/discovery-fetch.js.map +1 -1
  10. package/dist/cjs/auth/interactive-oauth-flow.d.cts +1 -1
  11. package/dist/cjs/auth/interactive-oauth-flow.d.ts +1 -1
  12. package/dist/cjs/auth/interactive-oauth-flow.js +13 -6
  13. package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
  14. package/dist/cjs/auth/oauth-callback-listener.d.cts +1 -1
  15. package/dist/cjs/auth/oauth-callback-listener.d.ts +1 -1
  16. package/dist/cjs/auth/oauth-callback-listener.js +40 -4
  17. package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
  18. package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -2
  19. package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -2
  20. package/dist/cjs/auth/rfc9728-discovery.js +120 -88
  21. package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
  22. package/dist/cjs/auth/types.d.cts +4 -0
  23. package/dist/cjs/auth/types.d.ts +4 -0
  24. package/dist/cjs/auth/types.js.map +1 -1
  25. package/dist/cjs/connection/connect-client.d.cts +9 -1
  26. package/dist/cjs/connection/connect-client.d.ts +9 -1
  27. package/dist/cjs/connection/connect-client.js +282 -75
  28. package/dist/cjs/connection/connect-client.js.map +1 -1
  29. package/dist/cjs/connection/existing-process-transport.d.cts +18 -18
  30. package/dist/cjs/connection/existing-process-transport.d.ts +18 -18
  31. package/dist/cjs/connection/existing-process-transport.js +187 -82
  32. package/dist/cjs/connection/existing-process-transport.js.map +1 -1
  33. package/dist/cjs/connection/wait-for-http-ready.d.cts +1 -1
  34. package/dist/cjs/connection/wait-for-http-ready.d.ts +1 -1
  35. package/dist/cjs/connection/wait-for-http-ready.js +30 -5
  36. package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
  37. package/dist/cjs/dcr/dcr-authenticator.d.cts +1 -1
  38. package/dist/cjs/dcr/dcr-authenticator.d.ts +1 -1
  39. package/dist/cjs/dcr/dcr-authenticator.js +29 -18
  40. package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
  41. package/dist/cjs/dcr/dynamic-client-registrar.js +2 -1
  42. package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
  43. package/dist/cjs/index.d.cts +1 -1
  44. package/dist/cjs/index.d.ts +1 -1
  45. package/dist/cjs/index.js.map +1 -1
  46. package/dist/cjs/spawn/owned-process-tree.d.cts +12 -0
  47. package/dist/cjs/spawn/owned-process-tree.d.ts +12 -0
  48. package/dist/cjs/spawn/owned-process-tree.js +93 -0
  49. package/dist/cjs/spawn/owned-process-tree.js.map +1 -0
  50. package/dist/cjs/spawn/spawn-server.d.cts +8 -32
  51. package/dist/cjs/spawn/spawn-server.d.ts +8 -32
  52. package/dist/cjs/spawn/spawn-server.js +406 -97
  53. package/dist/cjs/spawn/spawn-server.js.map +1 -1
  54. package/dist/cjs/spawn/spawn-servers.d.cts +18 -8
  55. package/dist/cjs/spawn/spawn-servers.d.ts +18 -8
  56. package/dist/cjs/spawn/spawn-servers.js +342 -113
  57. package/dist/cjs/spawn/spawn-servers.js.map +1 -1
  58. package/dist/cjs/types.d.cts +1 -1
  59. package/dist/cjs/types.d.ts +1 -1
  60. package/dist/cjs/types.js.map +1 -1
  61. package/dist/cjs/utils/path-utils.js +7 -4
  62. package/dist/cjs/utils/path-utils.js.map +1 -1
  63. package/dist/esm/auth/capability-discovery.d.ts +3 -1
  64. package/dist/esm/auth/capability-discovery.js +21 -11
  65. package/dist/esm/auth/capability-discovery.js.map +1 -1
  66. package/dist/esm/auth/discovery-fetch.d.ts +2 -0
  67. package/dist/esm/auth/discovery-fetch.js +64 -23
  68. package/dist/esm/auth/discovery-fetch.js.map +1 -1
  69. package/dist/esm/auth/interactive-oauth-flow.d.ts +1 -1
  70. package/dist/esm/auth/interactive-oauth-flow.js +13 -6
  71. package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
  72. package/dist/esm/auth/oauth-callback-listener.d.ts +1 -1
  73. package/dist/esm/auth/oauth-callback-listener.js +34 -4
  74. package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
  75. package/dist/esm/auth/rfc9728-discovery.d.ts +7 -2
  76. package/dist/esm/auth/rfc9728-discovery.js +40 -16
  77. package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
  78. package/dist/esm/auth/types.d.ts +4 -0
  79. package/dist/esm/auth/types.js.map +1 -1
  80. package/dist/esm/connection/connect-client.d.ts +9 -1
  81. package/dist/esm/connection/connect-client.js +97 -35
  82. package/dist/esm/connection/connect-client.js.map +1 -1
  83. package/dist/esm/connection/existing-process-transport.d.ts +18 -18
  84. package/dist/esm/connection/existing-process-transport.js +132 -70
  85. package/dist/esm/connection/existing-process-transport.js.map +1 -1
  86. package/dist/esm/connection/wait-for-http-ready.d.ts +1 -1
  87. package/dist/esm/connection/wait-for-http-ready.js +19 -4
  88. package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
  89. package/dist/esm/dcr/dcr-authenticator.d.ts +1 -1
  90. package/dist/esm/dcr/dcr-authenticator.js +26 -15
  91. package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
  92. package/dist/esm/dcr/dynamic-client-registrar.js +2 -1
  93. package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
  94. package/dist/esm/index.d.ts +1 -1
  95. package/dist/esm/index.js.map +1 -1
  96. package/dist/esm/spawn/owned-process-tree.d.ts +12 -0
  97. package/dist/esm/spawn/owned-process-tree.js +39 -0
  98. package/dist/esm/spawn/owned-process-tree.js.map +1 -0
  99. package/dist/esm/spawn/spawn-server.d.ts +8 -32
  100. package/dist/esm/spawn/spawn-server.js +228 -99
  101. package/dist/esm/spawn/spawn-server.js.map +1 -1
  102. package/dist/esm/spawn/spawn-servers.d.ts +18 -8
  103. package/dist/esm/spawn/spawn-servers.js +180 -55
  104. package/dist/esm/spawn/spawn-servers.js.map +1 -1
  105. package/dist/esm/types.d.ts +1 -1
  106. package/dist/esm/types.js.map +1 -1
  107. package/dist/esm/utils/path-utils.js +7 -4
  108. package/dist/esm/utils/path-utils.js.map +1 -1
  109. package/package.json +8 -3
  110. package/schemas/servers.d.ts +24 -0
  111. package/schemas/servers.schema.json +31 -0
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "createServerRegistry", {
14
14
  }
15
15
  });
16
16
  var _fs = /*#__PURE__*/ _interop_require_wildcard(require("fs"));
17
+ var _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
17
18
  var _process = /*#__PURE__*/ _interop_require_wildcard(require("process"));
18
19
  var _clienthelpersts = require("../client-helpers.js");
19
20
  var _validateconfigts = require("../config/validate-config.js");
@@ -29,6 +30,9 @@ function _array_like_to_array(arr, len) {
29
30
  function _array_with_holes(arr) {
30
31
  if (Array.isArray(arr)) return arr;
31
32
  }
33
+ function _array_without_holes(arr) {
34
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
35
+ }
32
36
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
33
37
  try {
34
38
  var info = gen[key](arg);
@@ -102,6 +106,11 @@ function _interop_require_wildcard(obj, nodeInterop) {
102
106
  if (cache) cache.set(obj, newObj);
103
107
  return newObj;
104
108
  }
109
+ function _iterable_to_array(iter) {
110
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) {
111
+ return Array.from(iter);
112
+ }
113
+ }
105
114
  function _iterable_to_array_limit(arr, i) {
106
115
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
107
116
  if (_i == null) return;
@@ -129,6 +138,9 @@ function _iterable_to_array_limit(arr, i) {
129
138
  function _non_iterable_rest() {
130
139
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
131
140
  }
141
+ function _non_iterable_spread() {
142
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
143
+ }
132
144
  function _object_spread(target) {
133
145
  for(var i = 1; i < arguments.length; i++){
134
146
  var source = arguments[i] != null ? arguments[i] : {};
@@ -170,6 +182,9 @@ function _object_spread_props(target, source) {
170
182
  function _sliced_to_array(arr, i) {
171
183
  return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
172
184
  }
185
+ function _to_consumable_array(arr) {
186
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
187
+ }
173
188
  function _ts_generator(thisArg, body) {
174
189
  var f, y, t, _ = {
175
190
  label: 0,
@@ -277,6 +292,47 @@ function _unsupported_iterable_to_array(o, minLen) {
277
292
  if (n === "Map" || n === "Set") return Array.from(n);
278
293
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
279
294
  }
295
+ var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;
296
+ function asError(error) {
297
+ return _instanceof(error, Error) ? error : new Error(String(error));
298
+ }
299
+ function withCause(message, cause) {
300
+ return new Error(message, {
301
+ cause: cause
302
+ });
303
+ }
304
+ function settleWithin(promise, timeoutMs) {
305
+ return _async_to_generator(function() {
306
+ var timer;
307
+ return _ts_generator(this, function(_state) {
308
+ return [
309
+ 2,
310
+ Promise.race([
311
+ promise.then(function(value) {
312
+ return {
313
+ settled: true,
314
+ value: value
315
+ };
316
+ }, function(error) {
317
+ return {
318
+ settled: true,
319
+ error: error
320
+ };
321
+ }),
322
+ new Promise(function(resolve) {
323
+ timer = setTimeout(function() {
324
+ return resolve({
325
+ settled: false
326
+ });
327
+ }, timeoutMs);
328
+ })
329
+ ]).finally(function() {
330
+ if (timer) clearTimeout(timer);
331
+ })
332
+ ];
333
+ });
334
+ })();
335
+ }
280
336
  /**
281
337
  * Infer transport type: explicit type > URL protocol > default 'stdio'
282
338
  */ function inferTransportType(config) {
@@ -319,6 +375,21 @@ function _unsupported_iterable_to_array(o, minLen) {
319
375
  }
320
376
  return filtered;
321
377
  }
378
+ function getStartSpawnConfig(start, transportType, baseEnv) {
379
+ var stop = start.stop && (transportType === 'http' || transportType === 'sse-ide') ? _object_spread_props(_object_spread({}, start.stop), {
380
+ cwd: start.stop.cwd,
381
+ env: filterEnv(_object_spread({}, baseEnv, start.env, start.stop.env))
382
+ }) : undefined;
383
+ return _object_spread({
384
+ shouldSpawn: true,
385
+ command: start.command,
386
+ args: start.args || [],
387
+ env: filterEnv(_object_spread({}, baseEnv, start.env)),
388
+ cwd: start.cwd
389
+ }, stop && {
390
+ stop: stop
391
+ });
392
+ }
322
393
  /**
323
394
  * Determine spawn behavior based on dialects and config structure.
324
395
  *
@@ -334,6 +405,11 @@ function _unsupported_iterable_to_array(o, minLen) {
334
405
  * When baseEnv is not provided, process.env is used as the base.
335
406
  */ function getSpawnConfig(entry, dialects, baseEnv) {
336
407
  var transportType = inferTransportType(entry);
408
+ if (dialects.length === 0) {
409
+ return {
410
+ shouldSpawn: false
411
+ };
412
+ }
337
413
  var hasServers = dialects.includes('servers');
338
414
  var hasStart = dialects.includes('start');
339
415
  // If only 'servers' dialect: Claude Code compatible (ignore start blocks, stdio only)
@@ -353,12 +429,7 @@ function _unsupported_iterable_to_array(o, minLen) {
353
429
  // If only 'start' dialect: Only spawn servers with start blocks
354
430
  if (hasStart && !hasServers) {
355
431
  if (entry.start) {
356
- return {
357
- shouldSpawn: true,
358
- command: entry.start.command,
359
- args: entry.start.args || [],
360
- env: filterEnv(_object_spread({}, baseEnv, entry.start.env))
361
- };
432
+ return getStartSpawnConfig(entry.start, transportType, baseEnv);
362
433
  }
363
434
  return {
364
435
  shouldSpawn: false
@@ -367,12 +438,7 @@ function _unsupported_iterable_to_array(o, minLen) {
367
438
  // Both dialects: Spawn both start blocks and stdio servers
368
439
  // Priority: start blocks first, then stdio
369
440
  if (entry.start) {
370
- return {
371
- shouldSpawn: true,
372
- command: entry.start.command,
373
- args: entry.start.args || [],
374
- env: filterEnv(_object_spread({}, baseEnv, entry.start.env))
375
- };
441
+ return getStartSpawnConfig(entry.start, transportType, baseEnv);
376
442
  }
377
443
  if (transportType === 'stdio' && entry.command) {
378
444
  return {
@@ -433,6 +499,22 @@ function createServerRegistry(serversConfig, options) {
433
499
  var servers = new Map();
434
500
  var clients = new Set();
435
501
  var sharedStdioClients = new Map();
502
+ var pendingConnects = new Set();
503
+ var closeAbortController = new AbortController();
504
+ var clientClosePromises = new WeakMap();
505
+ var closeRaceErrors = [];
506
+ var lifecycleState = 'open';
507
+ var closePromise;
508
+ var closeClientOnce = function closeClientOnce(client) {
509
+ var pending = clientClosePromises.get(client);
510
+ if (!pending) {
511
+ pending = Promise.resolve().then(function() {
512
+ return client.close();
513
+ });
514
+ clientClosePromises.set(client, pending);
515
+ }
516
+ return pending;
517
+ };
436
518
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
437
519
  try {
438
520
  // Start each server in the configuration
@@ -457,14 +539,13 @@ function createServerRegistry(serversConfig, options) {
457
539
  if (!spawnConfig.command) {
458
540
  throw new Error('Server "'.concat(name, '" missing command field'));
459
541
  }
460
- // All servers use the same working directory (cwd from options)
461
- var resolvedCwd = cwd;
542
+ var resolvedCwd = spawnConfig.cwd ? _path.resolve(cwd, spawnConfig.cwd) : cwd;
462
543
  // Start the server
463
544
  _loggerts.logger.info("[".concat(name, "] starting ").concat(transportType, " server (").concat(spawnConfig.command, " ").concat((spawnConfig.args || []).join(' '), ")"));
464
545
  // stdio servers need 'pipe' for MCP communication over stdin/stdout
465
546
  // network servers use 'inherit' so we see their logs
466
547
  var stdio = transportType === 'stdio' ? 'pipe' : 'inherit';
467
- var handle = (0, _spawnserverts.spawnProcess)(_object_spread_props(_object_spread(_object_spread_props(_object_spread({
548
+ var handle = (0, _spawnserverts.spawnProcess)(_object_spread_props(_object_spread(_object_spread_props(_object_spread(_object_spread_props(_object_spread({
468
549
  name: name,
469
550
  command: spawnConfig.command
470
551
  }, spawnConfig.args !== undefined && {
@@ -473,6 +554,12 @@ function createServerRegistry(serversConfig, options) {
473
554
  cwd: resolvedCwd
474
555
  }), spawnConfig.env && Object.keys(spawnConfig.env).length > 0 && {
475
556
  env: spawnConfig.env
557
+ }), {
558
+ inheritEnv: (options === null || options === void 0 ? void 0 : options.env) === undefined
559
+ }), spawnConfig.stop && {
560
+ stop: _object_spread_props(_object_spread({}, spawnConfig.stop), {
561
+ cwd: spawnConfig.stop.cwd ? _path.resolve(resolvedCwd, spawnConfig.stop.cwd) : resolvedCwd
562
+ })
476
563
  }), {
477
564
  stdio: stdio
478
565
  }));
@@ -497,10 +584,10 @@ function createServerRegistry(serversConfig, options) {
497
584
  }
498
585
  }
499
586
  }
500
- // Create connect function that tracks clients
501
- var connect = function connect(name, options) {
587
+ // Connect internally, then track each returned lease for registry shutdown.
588
+ var connectInternal = function connectInternal(name, options) {
502
589
  return _async_to_generator(function() {
503
- var serverEntry, available, transportType, entry, released, lease, registryLike, rawClient, decorated;
590
+ var serverEntry, available, transportType, entry, releasePromise, lease, registryLike, rawClient, decorated;
504
591
  return _ts_generator(this, function(_state) {
505
592
  switch(_state.label){
506
593
  case 0:
@@ -540,7 +627,9 @@ function createServerRegistry(serversConfig, options) {
540
627
  };
541
628
  return [
542
629
  4,
543
- (0, _connectclientts.connectMcpClient)(registryLike, name, options)
630
+ (0, _connectclientts.connectMcpClient)(registryLike, name, _object_spread_props(_object_spread({}, options), {
631
+ signal: closeAbortController.signal
632
+ }))
544
633
  ];
545
634
  case 1:
546
635
  rawClient = _state.sent();
@@ -573,40 +662,55 @@ function createServerRegistry(serversConfig, options) {
573
662
  throw new Error("Failed to connect to stdio server '".concat(name, "'"));
574
663
  }
575
664
  entry.refs += 1;
576
- released = false;
577
665
  lease = new Proxy(entry.client, {
578
666
  get: function get(target, prop) {
579
667
  if (prop === 'close') {
580
668
  return function() {
581
- return _async_to_generator(function() {
582
- return _ts_generator(this, function(_state) {
583
- switch(_state.label){
584
- case 0:
585
- if (released) return [
586
- 2
587
- ];
588
- released = true;
589
- clients.delete(lease);
590
- entry.refs = Math.max(0, entry.refs - 1);
591
- if (!(entry.refs === 0)) return [
592
- 3,
593
- 2
594
- ];
595
- sharedStdioClients.delete(name);
596
- return [
597
- 4,
598
- target.close()
599
- ];
600
- case 1:
601
- _state.sent();
602
- _state.label = 2;
603
- case 2:
604
- return [
605
- 2
606
- ];
607
- }
608
- });
609
- })();
669
+ releasePromise !== null && releasePromise !== void 0 ? releasePromise : releasePromise = function() {
670
+ return _async_to_generator(function() {
671
+ return _ts_generator(this, function(_state) {
672
+ switch(_state.label){
673
+ case 0:
674
+ entry.refs = Math.max(0, entry.refs - 1);
675
+ _state.label = 1;
676
+ case 1:
677
+ _state.trys.push([
678
+ 1,
679
+ ,
680
+ 4,
681
+ 5
682
+ ]);
683
+ if (!(entry.refs === 0)) return [
684
+ 3,
685
+ 3
686
+ ];
687
+ sharedStdioClients.delete(name);
688
+ return [
689
+ 4,
690
+ target.close()
691
+ ];
692
+ case 2:
693
+ _state.sent();
694
+ _state.label = 3;
695
+ case 3:
696
+ return [
697
+ 3,
698
+ 5
699
+ ];
700
+ case 4:
701
+ clients.delete(lease);
702
+ return [
703
+ 7
704
+ ];
705
+ case 5:
706
+ return [
707
+ 2
708
+ ];
709
+ }
710
+ });
711
+ })();
712
+ }();
713
+ return releasePromise;
610
714
  };
611
715
  }
612
716
  var value = Reflect.get(target, prop, target);
@@ -629,7 +733,9 @@ function createServerRegistry(serversConfig, options) {
629
733
  };
630
734
  return [
631
735
  4,
632
- (0, _connectclientts.connectMcpClient)(registryLike, name, options)
736
+ (0, _connectclientts.connectMcpClient)(registryLike, name, _object_spread_props(_object_spread({}, options), {
737
+ signal: closeAbortController.signal
738
+ }))
633
739
  ];
634
740
  case 4:
635
741
  rawClient = _state.sent();
@@ -645,84 +751,199 @@ function createServerRegistry(serversConfig, options) {
645
751
  });
646
752
  })();
647
753
  };
648
- // Create close function that stops all clients and servers
649
- var close = function close() {
650
- var signal = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 'SIGINT', opts = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
754
+ var connect = function connect(name, options) {
755
+ if (lifecycleState !== 'open') {
756
+ return Promise.reject(new Error("Cannot connect to server '".concat(name, "': registry is ").concat(lifecycleState)));
757
+ }
758
+ var pending = connectInternal(name, options).then(function(client) {
759
+ return _async_to_generator(function() {
760
+ var result;
761
+ return _ts_generator(this, function(_state) {
762
+ switch(_state.label){
763
+ case 0:
764
+ if (lifecycleState === 'open') return [
765
+ 2,
766
+ client
767
+ ];
768
+ return [
769
+ 4,
770
+ settleWithin(closeClientOnce(client), DEFAULT_SHUTDOWN_TIMEOUT_MS)
771
+ ];
772
+ case 1:
773
+ result = _state.sent();
774
+ if (!result.settled) closeRaceErrors.push(new Error("Client for server '".concat(name, "' did not close within ").concat(DEFAULT_SHUTDOWN_TIMEOUT_MS, "ms after a connection raced registry shutdown")));
775
+ else if ('error' in result) closeRaceErrors.push(withCause("Failed to close server '".concat(name, "' after a connection raced registry shutdown"), asError(result.error)));
776
+ throw new Error("Connection to server '".concat(name, "' completed while the registry was closing"));
777
+ }
778
+ });
779
+ })();
780
+ });
781
+ pendingConnects.add(pending);
782
+ void pending.then(function() {
783
+ return pendingConnects.delete(pending);
784
+ }, function() {
785
+ return pendingConnects.delete(pending);
786
+ });
787
+ return pending;
788
+ };
789
+ var closeAll = function closeAll(signal, opts) {
651
790
  return _async_to_generator(function() {
652
- var clientClosePromises, closeResults, timedOut, killedCount;
791
+ var _errors, _opts_timeoutMs, timeoutMs, errors, pendingResult, clientEntries, clientResults, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, client, result, serverEntries, serverResults, timedOut, killedCount, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, result1, details;
653
792
  return _ts_generator(this, function(_state) {
654
793
  switch(_state.label){
655
794
  case 0:
656
795
  _loggerts.logger.info("[registry] closing (".concat(signal, ")"));
657
- // First, close all tracked clients
658
- clientClosePromises = Array.from(clients).map(function(client) {
659
- return _async_to_generator(function() {
660
- var unused;
661
- return _ts_generator(this, function(_state) {
662
- switch(_state.label){
663
- case 0:
664
- _state.trys.push([
665
- 0,
666
- 2,
667
- ,
668
- 3
669
- ]);
670
- return [
671
- 4,
672
- client.close()
673
- ];
674
- case 1:
675
- _state.sent();
676
- return [
677
- 3,
678
- 3
679
- ];
680
- case 2:
681
- unused = _state.sent();
682
- return [
683
- 3,
684
- 3
685
- ];
686
- case 3:
687
- return [
688
- 2
689
- ];
690
- }
691
- });
692
- })();
693
- });
796
+ timeoutMs = (_opts_timeoutMs = opts.timeoutMs) !== null && _opts_timeoutMs !== void 0 ? _opts_timeoutMs : DEFAULT_SHUTDOWN_TIMEOUT_MS;
797
+ errors = [];
798
+ closeAbortController.abort(new Error('Server registry is closing'));
694
799
  return [
695
800
  4,
696
- Promise.all(clientClosePromises)
801
+ settleWithin(Promise.allSettled(Array.from(pendingConnects)), timeoutMs)
697
802
  ];
698
803
  case 1:
699
- _state.sent();
700
- clients.clear();
701
- // Then close all server processes
702
- if (servers.size === 0) {
703
- return [
704
- 2,
705
- {
706
- timedOut: false,
707
- killedCount: 0
708
- }
709
- ];
804
+ pendingResult = _state.sent();
805
+ if (!pendingResult.settled) {
806
+ errors.push(new Error("In-flight server connections did not settle within ".concat(timeoutMs, "ms after cancellation")));
710
807
  }
808
+ (_errors = errors).push.apply(_errors, _to_consumable_array(closeRaceErrors));
809
+ // Client close can hang independently; bound it so every owned server still gets cleanup.
810
+ clientEntries = Array.from(clients);
711
811
  return [
712
812
  4,
713
- Promise.all(Array.from(servers.values()).map(function(server) {
714
- return server.close(signal, opts);
813
+ Promise.all(clientEntries.map(function(client) {
814
+ return _async_to_generator(function() {
815
+ var _tmp;
816
+ return _ts_generator(this, function(_state) {
817
+ switch(_state.label){
818
+ case 0:
819
+ _tmp = {
820
+ client: client
821
+ };
822
+ return [
823
+ 4,
824
+ settleWithin(closeClientOnce(client), timeoutMs)
825
+ ];
826
+ case 1:
827
+ return [
828
+ 2,
829
+ (_tmp.result = _state.sent(), _tmp)
830
+ ];
831
+ }
832
+ });
833
+ })();
715
834
  }))
716
835
  ];
717
836
  case 2:
718
- closeResults = _state.sent();
719
- // Check if any timed out and count how many were force-killed
720
- timedOut = closeResults.some(function(result) {
721
- return result.timedOut;
722
- });
723
- killedCount = closeResults.filter(function(result) {
724
- return result.killed;
725
- }).length;
837
+ clientResults = _state.sent();
838
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
839
+ try {
840
+ for(_iterator = clientResults[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
841
+ _step_value = _step.value, client = _step_value.client, result = _step_value.result;
842
+ if (!result.settled) {
843
+ errors.push(new Error("Client".concat(client.serverName ? " for server '".concat(client.serverName, "'") : '', " did not close within ").concat(timeoutMs, "ms")));
844
+ } else if ('error' in result) {
845
+ errors.push(withCause("Failed to close client".concat(client.serverName ? " for server '".concat(client.serverName, "'") : '', ": ").concat(asError(result.error).message), asError(result.error)));
846
+ }
847
+ }
848
+ } catch (err) {
849
+ _didIteratorError = true;
850
+ _iteratorError = err;
851
+ } finally{
852
+ try {
853
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
854
+ _iterator.return();
855
+ }
856
+ } finally{
857
+ if (_didIteratorError) {
858
+ throw _iteratorError;
859
+ }
860
+ }
861
+ }
862
+ clients.clear();
863
+ sharedStdioClients.clear();
864
+ // Server teardown runs even when pending connects or client closes failed.
865
+ serverEntries = Array.from(servers.entries());
866
+ return [
867
+ 4,
868
+ Promise.all(serverEntries.map(function(param) {
869
+ var _param = _sliced_to_array(param, 2), name = _param[0], server = _param[1];
870
+ return _async_to_generator(function() {
871
+ var _tmp, error;
872
+ return _ts_generator(this, function(_state) {
873
+ switch(_state.label){
874
+ case 0:
875
+ _state.trys.push([
876
+ 0,
877
+ 2,
878
+ ,
879
+ 3
880
+ ]);
881
+ _tmp = {
882
+ name: name
883
+ };
884
+ return [
885
+ 4,
886
+ server.close(signal, opts)
887
+ ];
888
+ case 1:
889
+ return [
890
+ 2,
891
+ (_tmp.result = _state.sent(), _tmp)
892
+ ];
893
+ case 2:
894
+ error = _state.sent();
895
+ return [
896
+ 2,
897
+ {
898
+ name: name,
899
+ error: asError(error)
900
+ }
901
+ ];
902
+ case 3:
903
+ return [
904
+ 2
905
+ ];
906
+ }
907
+ });
908
+ })();
909
+ }))
910
+ ];
911
+ case 3:
912
+ serverResults = _state.sent();
913
+ timedOut = false;
914
+ killedCount = 0;
915
+ _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
916
+ try {
917
+ for(_iterator1 = serverResults[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
918
+ result1 = _step1.value;
919
+ if ('error' in result1 && result1.error !== undefined) {
920
+ errors.push(withCause("Failed to close server '".concat(result1.name, "': ").concat(result1.error.message), result1.error));
921
+ } else if ('result' in result1) {
922
+ timedOut || (timedOut = result1.result.timedOut);
923
+ if (result1.result.killed) killedCount += 1;
924
+ }
925
+ }
926
+ } catch (err) {
927
+ _didIteratorError1 = true;
928
+ _iteratorError1 = err;
929
+ } finally{
930
+ try {
931
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
932
+ _iterator1.return();
933
+ }
934
+ } finally{
935
+ if (_didIteratorError1) {
936
+ throw _iteratorError1;
937
+ }
938
+ }
939
+ }
940
+ lifecycleState = 'closed';
941
+ if (errors.length > 0) {
942
+ details = errors.map(function(error) {
943
+ return error.message;
944
+ }).join('; ');
945
+ throw new AggregateError(errors, "Registry shutdown completed with ".concat(errors.length, " cleanup failure(s): ").concat(details));
946
+ }
726
947
  return [
727
948
  2,
728
949
  {
@@ -734,6 +955,14 @@ function createServerRegistry(serversConfig, options) {
734
955
  });
735
956
  })();
736
957
  };
958
+ var close = function close() {
959
+ var signal = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 'SIGINT', opts = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
960
+ if (!closePromise) {
961
+ lifecycleState = 'closing';
962
+ closePromise = closeAll(signal, opts);
963
+ }
964
+ return closePromise;
965
+ };
737
966
  var searchFromRegistry = function searchFromRegistry(query) {
738
967
  var _$options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
739
968
  return _async_to_generator(function() {