@dangao/bun-server 2.1.0 → 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 (45) hide show
  1. package/dist/ai/providers/anthropic-provider.d.ts.map +1 -1
  2. package/dist/ai/providers/google-provider.d.ts.map +1 -1
  3. package/dist/ai/providers/ollama-provider.d.ts.map +1 -1
  4. package/dist/ai/providers/openai-provider.d.ts.map +1 -1
  5. package/dist/ai/service.d.ts.map +1 -1
  6. package/dist/ai/types.d.ts +5 -0
  7. package/dist/ai/types.d.ts.map +1 -1
  8. package/dist/core/application.d.ts +17 -0
  9. package/dist/core/application.d.ts.map +1 -1
  10. package/dist/core/context.d.ts +5 -0
  11. package/dist/core/context.d.ts.map +1 -1
  12. package/dist/core/server.d.ts +17 -0
  13. package/dist/core/server.d.ts.map +1 -1
  14. package/dist/di/container.d.ts +16 -0
  15. package/dist/di/container.d.ts.map +1 -1
  16. package/dist/di/lifecycle.d.ts +48 -0
  17. package/dist/di/lifecycle.d.ts.map +1 -1
  18. package/dist/di/module-registry.d.ts +10 -6
  19. package/dist/di/module-registry.d.ts.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +316 -108
  23. package/dist/mcp/server.d.ts +5 -2
  24. package/dist/mcp/server.d.ts.map +1 -1
  25. package/docs/idle-timeout.md +99 -8
  26. package/docs/lifecycle.md +74 -4
  27. package/docs/zh/idle-timeout.md +97 -6
  28. package/docs/zh/lifecycle.md +47 -8
  29. package/package.json +1 -1
  30. package/src/ai/providers/anthropic-provider.ts +5 -2
  31. package/src/ai/providers/google-provider.ts +3 -0
  32. package/src/ai/providers/ollama-provider.ts +3 -0
  33. package/src/ai/providers/openai-provider.ts +5 -2
  34. package/src/ai/service.ts +17 -5
  35. package/src/ai/types.ts +5 -0
  36. package/src/core/application.ts +55 -23
  37. package/src/core/context.ts +7 -0
  38. package/src/core/server.ts +121 -18
  39. package/src/di/container.ts +55 -1
  40. package/src/di/lifecycle.ts +114 -0
  41. package/src/di/module-registry.ts +58 -10
  42. package/src/index.ts +4 -0
  43. package/src/mcp/server.ts +6 -15
  44. package/tests/di/lifecycle.test.ts +102 -1
  45. package/tests/di/scoped-lifecycle.test.ts +61 -0
package/dist/index.js CHANGED
@@ -1769,6 +1769,88 @@ var init_types = __esm(() => {
1769
1769
  })(Lifecycle ||= {});
1770
1770
  });
1771
1771
 
1772
+ // src/di/lifecycle.ts
1773
+ function hasOnModuleInit(instance) {
1774
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onModuleInit" in instance && typeof instance.onModuleInit === "function";
1775
+ }
1776
+ function hasOnModuleDestroy(instance) {
1777
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onModuleDestroy" in instance && typeof instance.onModuleDestroy === "function";
1778
+ }
1779
+ function hasOnApplicationBootstrap(instance) {
1780
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onApplicationBootstrap" in instance && typeof instance.onApplicationBootstrap === "function";
1781
+ }
1782
+ function hasOnApplicationShutdown(instance) {
1783
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onApplicationShutdown" in instance && typeof instance.onApplicationShutdown === "function";
1784
+ }
1785
+ function hasComponentBeforeCreate(target) {
1786
+ return target !== null && target !== undefined && typeof target === "function" && "onBeforeCreate" in target && typeof target.onBeforeCreate === "function";
1787
+ }
1788
+ function hasOnAfterCreate(instance) {
1789
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onAfterCreate" in instance && typeof instance.onAfterCreate === "function";
1790
+ }
1791
+ function hasOnBeforeDestroy(instance) {
1792
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onBeforeDestroy" in instance && typeof instance.onBeforeDestroy === "function";
1793
+ }
1794
+ function hasOnAfterDestroy(instance) {
1795
+ return instance !== null && instance !== undefined && typeof instance === "object" && "onAfterDestroy" in instance && typeof instance.onAfterDestroy === "function";
1796
+ }
1797
+ function callComponentBeforeCreate(target) {
1798
+ if (hasComponentBeforeCreate(target)) {
1799
+ target.onBeforeCreate();
1800
+ }
1801
+ }
1802
+ function callOnAfterCreate(instance) {
1803
+ if (hasOnAfterCreate(instance)) {
1804
+ instance.onAfterCreate();
1805
+ }
1806
+ }
1807
+ async function callOnModuleInit(instances) {
1808
+ for (const instance of instances) {
1809
+ if (hasOnModuleInit(instance)) {
1810
+ await instance.onModuleInit();
1811
+ }
1812
+ }
1813
+ }
1814
+ async function callOnApplicationBootstrap(instances) {
1815
+ for (const instance of instances) {
1816
+ if (hasOnApplicationBootstrap(instance)) {
1817
+ await instance.onApplicationBootstrap();
1818
+ }
1819
+ }
1820
+ }
1821
+ async function callOnModuleDestroy(instances) {
1822
+ for (let i = instances.length - 1;i >= 0; i--) {
1823
+ const instance = instances[i];
1824
+ if (hasOnModuleDestroy(instance)) {
1825
+ await instance.onModuleDestroy();
1826
+ }
1827
+ }
1828
+ }
1829
+ async function callOnApplicationShutdown(instances, signal) {
1830
+ for (let i = instances.length - 1;i >= 0; i--) {
1831
+ const instance = instances[i];
1832
+ if (hasOnApplicationShutdown(instance)) {
1833
+ await instance.onApplicationShutdown(signal);
1834
+ }
1835
+ }
1836
+ }
1837
+ async function callOnBeforeDestroy(instances) {
1838
+ for (let i = instances.length - 1;i >= 0; i--) {
1839
+ const instance = instances[i];
1840
+ if (hasOnBeforeDestroy(instance)) {
1841
+ await instance.onBeforeDestroy();
1842
+ }
1843
+ }
1844
+ }
1845
+ async function callOnAfterDestroy(instances) {
1846
+ for (let i = instances.length - 1;i >= 0; i--) {
1847
+ const instance = instances[i];
1848
+ if (hasOnAfterDestroy(instance)) {
1849
+ await instance.onAfterDestroy();
1850
+ }
1851
+ }
1852
+ }
1853
+
1772
1854
  // src/di/container.ts
1773
1855
  import"reflect-metadata";
1774
1856
  import { LoggerManager as LoggerManager5 } from "@dangao/logsmith";
@@ -1951,6 +2033,7 @@ class Container {
1951
2033
  throw new Error(`Invalid token type: ${tokenType}. Token: ${String(token)}`);
1952
2034
  }
1953
2035
  instantiate(constructor) {
2036
+ callComponentBeforeCreate(constructor);
1954
2037
  const plan = this.getDependencyPlan(constructor);
1955
2038
  let instance;
1956
2039
  if (plan.paramLength === 0) {
@@ -1962,7 +2045,9 @@ class Container {
1962
2045
  }
1963
2046
  instance = new constructor(...dependencies);
1964
2047
  }
1965
- return this.applyPostProcessors(instance, constructor);
2048
+ const processed = this.applyPostProcessors(instance, constructor);
2049
+ callOnAfterCreate(processed);
2050
+ return processed;
1966
2051
  }
1967
2052
  getTokenKey(token) {
1968
2053
  if (typeof token === "function") {
@@ -1977,6 +2062,34 @@ class Container {
1977
2062
  this.dependencyPlans.clear();
1978
2063
  this.postProcessors.length = 0;
1979
2064
  }
2065
+ getScopedInstances(context) {
2066
+ const scopedMap = this.scopedInstances.get(context);
2067
+ if (!scopedMap || scopedMap.size === 0) {
2068
+ return [];
2069
+ }
2070
+ const seen = new Set;
2071
+ const instances = [];
2072
+ for (const instance of scopedMap.values()) {
2073
+ if (!seen.has(instance)) {
2074
+ seen.add(instance);
2075
+ instances.push(instance);
2076
+ }
2077
+ }
2078
+ return instances;
2079
+ }
2080
+ clearScopedInstances(context) {
2081
+ this.scopedInstances.delete(context);
2082
+ }
2083
+ async disposeScopedInstances(context) {
2084
+ const instances = this.getScopedInstances(context);
2085
+ if (instances.length === 0) {
2086
+ return;
2087
+ }
2088
+ await callOnBeforeDestroy(instances);
2089
+ await callOnModuleDestroy(instances);
2090
+ await callOnAfterDestroy(instances);
2091
+ this.clearScopedInstances(context);
2092
+ }
1980
2093
  isRegistered(token) {
1981
2094
  const tokenKey = this.getTokenKey(token);
1982
2095
  return this.providers.has(tokenKey);
@@ -4041,6 +4154,7 @@ class Context {
4041
4154
  files = [];
4042
4155
  _body;
4043
4156
  _bodyParsed = false;
4157
+ signal;
4044
4158
  constructor(request) {
4045
4159
  this.request = request;
4046
4160
  this.url = new URL2(request.url);
@@ -4049,6 +4163,7 @@ class Context {
4049
4163
  this.query = this.url.searchParams;
4050
4164
  this.headers = request.headers;
4051
4165
  this.responseHeaders = new Headers;
4166
+ this.signal = request.signal;
4052
4167
  }
4053
4168
  async getBody() {
4054
4169
  if (!this._bodyParsed) {
@@ -4194,7 +4309,27 @@ class BunServer {
4194
4309
  this.isShuttingDown = false;
4195
4310
  this.shutdownPromise = undefined;
4196
4311
  this.shutdownResolve = undefined;
4197
- const fetchHandler = (request, server) => {
4312
+ const sseKeepAlive = this.options.sseKeepAlive;
4313
+ const sseHeartbeatEnabled = sseKeepAlive?.enabled !== false;
4314
+ const sseHeartbeatIntervalMs = sseKeepAlive?.intervalMs ?? 15000;
4315
+ const postProcessSse = (response, request, bunServer) => {
4316
+ const ct = response.headers.get("content-type");
4317
+ if (!ct?.includes("text/event-stream")) {
4318
+ return response;
4319
+ }
4320
+ bunServer.timeout(request, 0);
4321
+ if (sseHeartbeatEnabled && response.body) {
4322
+ return BunServer.wrapSseWithHeartbeat(response, sseHeartbeatIntervalMs, request.signal);
4323
+ }
4324
+ return response;
4325
+ };
4326
+ const decrementAndMaybeShutdown = () => {
4327
+ this.activeRequests--;
4328
+ if (this.isShuttingDown && this.activeRequests === 0 && this.shutdownResolve) {
4329
+ this.shutdownResolve();
4330
+ }
4331
+ };
4332
+ const fetchHandler = (request, bunServer) => {
4198
4333
  if (this.isShuttingDown) {
4199
4334
  return new Response("Server is shutting down", { status: 503 });
4200
4335
  }
@@ -4206,7 +4341,7 @@ class BunServer {
4206
4341
  }
4207
4342
  const context2 = new Context(request);
4208
4343
  const queryParams = new URLSearchParams(url.searchParams);
4209
- const upgraded = server.upgrade(request, {
4344
+ const upgraded = bunServer.upgrade(request, {
4210
4345
  data: {
4211
4346
  path: url.pathname,
4212
4347
  query: queryParams,
@@ -4222,19 +4357,12 @@ class BunServer {
4222
4357
  const context = new Context(request);
4223
4358
  const responsePromise = this.options.fetch(context);
4224
4359
  if (responsePromise instanceof Promise) {
4225
- responsePromise.finally(() => {
4226
- this.activeRequests--;
4227
- if (this.isShuttingDown && this.activeRequests === 0 && this.shutdownResolve) {
4228
- this.shutdownResolve();
4229
- }
4230
- }).catch(() => {});
4231
- } else {
4232
- this.activeRequests--;
4233
- if (this.isShuttingDown && this.activeRequests === 0 && this.shutdownResolve) {
4234
- this.shutdownResolve();
4235
- }
4360
+ const processed = responsePromise.then((response) => postProcessSse(response, request, bunServer));
4361
+ processed.finally(decrementAndMaybeShutdown).catch(() => {});
4362
+ return processed;
4236
4363
  }
4237
- return responsePromise;
4364
+ decrementAndMaybeShutdown();
4365
+ return postProcessSse(responsePromise, request, bunServer);
4238
4366
  };
4239
4367
  const websocketHandlers = {
4240
4368
  open: async (ws) => {
@@ -4332,6 +4460,68 @@ class BunServer {
4332
4460
  getHostname() {
4333
4461
  return this.options.hostname;
4334
4462
  }
4463
+ static wrapSseWithHeartbeat(original, intervalMs, signal) {
4464
+ const encoder = new TextEncoder;
4465
+ const keepaliveChunk = encoder.encode(`: keepalive
4466
+
4467
+ `);
4468
+ const originalBody = original.body;
4469
+ let heartbeat;
4470
+ let reader;
4471
+ const wrapped = new ReadableStream({
4472
+ start(controller) {
4473
+ reader = originalBody.getReader();
4474
+ heartbeat = setInterval(() => {
4475
+ try {
4476
+ controller.enqueue(keepaliveChunk);
4477
+ } catch {
4478
+ clearInterval(heartbeat);
4479
+ heartbeat = undefined;
4480
+ }
4481
+ }, intervalMs);
4482
+ const onAbort = () => {
4483
+ if (heartbeat) {
4484
+ clearInterval(heartbeat);
4485
+ heartbeat = undefined;
4486
+ }
4487
+ };
4488
+ signal.addEventListener("abort", onAbort, { once: true });
4489
+ const pump = async () => {
4490
+ try {
4491
+ while (true) {
4492
+ const { done, value } = await reader.read();
4493
+ if (done)
4494
+ break;
4495
+ controller.enqueue(value);
4496
+ }
4497
+ controller.close();
4498
+ } catch (err) {
4499
+ try {
4500
+ controller.error(err);
4501
+ } catch {}
4502
+ } finally {
4503
+ if (heartbeat) {
4504
+ clearInterval(heartbeat);
4505
+ heartbeat = undefined;
4506
+ }
4507
+ signal.removeEventListener("abort", onAbort);
4508
+ }
4509
+ };
4510
+ pump();
4511
+ },
4512
+ cancel() {
4513
+ if (heartbeat) {
4514
+ clearInterval(heartbeat);
4515
+ heartbeat = undefined;
4516
+ }
4517
+ reader?.cancel();
4518
+ }
4519
+ });
4520
+ return new Response(wrapped, {
4521
+ status: original.status,
4522
+ headers: original.headers
4523
+ });
4524
+ }
4335
4525
  }
4336
4526
 
4337
4527
  // src/core/application.ts
@@ -4570,51 +4760,6 @@ init_types();
4570
4760
  init_module();
4571
4761
  init_decorators();
4572
4762
 
4573
- // src/di/lifecycle.ts
4574
- function hasOnModuleInit(instance) {
4575
- return instance !== null && instance !== undefined && typeof instance === "object" && "onModuleInit" in instance && typeof instance.onModuleInit === "function";
4576
- }
4577
- function hasOnModuleDestroy(instance) {
4578
- return instance !== null && instance !== undefined && typeof instance === "object" && "onModuleDestroy" in instance && typeof instance.onModuleDestroy === "function";
4579
- }
4580
- function hasOnApplicationBootstrap(instance) {
4581
- return instance !== null && instance !== undefined && typeof instance === "object" && "onApplicationBootstrap" in instance && typeof instance.onApplicationBootstrap === "function";
4582
- }
4583
- function hasOnApplicationShutdown(instance) {
4584
- return instance !== null && instance !== undefined && typeof instance === "object" && "onApplicationShutdown" in instance && typeof instance.onApplicationShutdown === "function";
4585
- }
4586
- async function callOnModuleInit(instances) {
4587
- for (const instance of instances) {
4588
- if (hasOnModuleInit(instance)) {
4589
- await instance.onModuleInit();
4590
- }
4591
- }
4592
- }
4593
- async function callOnApplicationBootstrap(instances) {
4594
- for (const instance of instances) {
4595
- if (hasOnApplicationBootstrap(instance)) {
4596
- await instance.onApplicationBootstrap();
4597
- }
4598
- }
4599
- }
4600
- async function callOnModuleDestroy(instances) {
4601
- for (let i = instances.length - 1;i >= 0; i--) {
4602
- const instance = instances[i];
4603
- if (hasOnModuleDestroy(instance)) {
4604
- await instance.onModuleDestroy();
4605
- }
4606
- }
4607
- }
4608
- async function callOnApplicationShutdown(instances, signal) {
4609
- for (let i = instances.length - 1;i >= 0; i--) {
4610
- const instance = instances[i];
4611
- if (hasOnApplicationShutdown(instance)) {
4612
- await instance.onApplicationShutdown(signal);
4613
- }
4614
- }
4615
- }
4616
-
4617
- // src/di/module-registry.ts
4618
4763
  class ModuleRegistry {
4619
4764
  static instance;
4620
4765
  moduleRefs = new Map;
@@ -4771,7 +4916,7 @@ class ModuleRegistry {
4771
4916
  }
4772
4917
  return moduleRef.middlewares;
4773
4918
  }
4774
- resolveAllProviderInstances() {
4919
+ resolveAllComponentInstances() {
4775
4920
  const instances = [];
4776
4921
  const seen = new Set;
4777
4922
  for (const [, ref] of this.moduleRefs) {
@@ -4804,25 +4949,62 @@ class ModuleRegistry {
4804
4949
  }
4805
4950
  } catch (_error) {}
4806
4951
  }
4952
+ for (const controller of ref.metadata.controllers) {
4953
+ try {
4954
+ const instance = ref.container.resolve(controller);
4955
+ if (!seen.has(instance)) {
4956
+ seen.add(instance);
4957
+ instances.push(instance);
4958
+ }
4959
+ } catch (_error) {}
4960
+ }
4807
4961
  }
4808
4962
  return instances;
4809
4963
  }
4810
4964
  async callModuleInitHooks() {
4811
- const instances = this.resolveAllProviderInstances();
4965
+ const instances = this.resolveAllComponentInstances();
4812
4966
  await callOnModuleInit(instances);
4813
4967
  }
4814
4968
  async callBootstrapHooks() {
4815
- const instances = this.resolveAllProviderInstances();
4969
+ const instances = this.resolveAllComponentInstances();
4816
4970
  await callOnApplicationBootstrap(instances);
4817
4971
  }
4818
4972
  async callModuleDestroyHooks() {
4819
- const instances = this.resolveAllProviderInstances();
4973
+ const instances = this.resolveAllComponentInstances();
4974
+ await callOnBeforeDestroy(instances);
4820
4975
  await callOnModuleDestroy(instances);
4976
+ await callOnAfterDestroy(instances);
4821
4977
  }
4822
4978
  async callShutdownHooks(signal) {
4823
- const instances = this.resolveAllProviderInstances();
4979
+ const instances = this.resolveAllComponentInstances();
4824
4980
  await callOnApplicationShutdown(instances, signal);
4825
4981
  }
4982
+ async disposeScopedInstances(context) {
4983
+ const containers = [];
4984
+ if (this.rootContainer) {
4985
+ containers.push(this.rootContainer);
4986
+ }
4987
+ containers.push(...this.getAllModuleContainers());
4988
+ const instances = [];
4989
+ const seen = new Set;
4990
+ for (const container of containers) {
4991
+ const scopedInstances = container.getScopedInstances(context);
4992
+ for (const instance of scopedInstances) {
4993
+ if (!seen.has(instance)) {
4994
+ seen.add(instance);
4995
+ instances.push(instance);
4996
+ }
4997
+ }
4998
+ }
4999
+ if (instances.length > 0) {
5000
+ await callOnBeforeDestroy(instances);
5001
+ await callOnModuleDestroy(instances);
5002
+ await callOnAfterDestroy(instances);
5003
+ }
5004
+ for (const container of containers) {
5005
+ container.clearScopedInstances(context);
5006
+ }
5007
+ }
4826
5008
  registerExport(parentContainer, moduleRef, token) {
4827
5009
  if (!moduleRef.container.isRegistered(token)) {
4828
5010
  throw new Error(`Module ${moduleRef.moduleClass.name} cannot export ${typeof token === "function" ? token.name : String(token)} because it is not registered`);
@@ -6570,6 +6752,7 @@ class Application {
6570
6752
  hostname: finalHostname,
6571
6753
  reusePort: this.options.reusePort,
6572
6754
  idleTimeout: this.options.idleTimeout,
6755
+ sseKeepAlive: this.options.sseKeepAlive,
6573
6756
  fetch: this.handleRequest.bind(this),
6574
6757
  websocketRegistry: this.websocketRegistry,
6575
6758
  gracefulShutdownTimeout: this.options.gracefulShutdownTimeout
@@ -6649,30 +6832,43 @@ class Application {
6649
6832
  }
6650
6833
  async handleRequest(context) {
6651
6834
  const logger = LoggerManager11.getLogger();
6835
+ const moduleRegistry = ModuleRegistry.getInstance();
6652
6836
  logger.debug("[Request] Incoming", {
6653
6837
  method: context.method,
6654
6838
  path: context.path,
6655
6839
  url: context.url?.href
6656
6840
  });
6657
6841
  return await contextStore.run(context, async () => {
6658
- if (["POST", "PUT", "PATCH"].includes(context.method)) {
6659
- await context.getBody();
6660
- }
6661
- const registry = RouteRegistry.getInstance();
6662
- const router = registry.getRouter();
6663
- await router.preHandle(context);
6664
- return await this.middlewarePipeline.run(context, async () => {
6665
- const response = await router.handle(context);
6666
- if (response) {
6667
- return response;
6842
+ try {
6843
+ if (["POST", "PUT", "PATCH"].includes(context.method)) {
6844
+ await context.getBody();
6668
6845
  }
6669
- logger.debug("[Router] No route matched", {
6670
- method: context.method,
6671
- path: context.path
6846
+ const registry = RouteRegistry.getInstance();
6847
+ const router = registry.getRouter();
6848
+ await router.preHandle(context);
6849
+ return await this.middlewarePipeline.run(context, async () => {
6850
+ const response = await router.handle(context);
6851
+ if (response) {
6852
+ return response;
6853
+ }
6854
+ logger.debug("[Router] No route matched", {
6855
+ method: context.method,
6856
+ path: context.path
6857
+ });
6858
+ context.setStatus(404);
6859
+ return context.createErrorResponse({ error: "Not Found" });
6672
6860
  });
6673
- context.setStatus(404);
6674
- return context.createErrorResponse({ error: "Not Found" });
6675
- });
6861
+ } finally {
6862
+ try {
6863
+ await moduleRegistry.disposeScopedInstances(context);
6864
+ } catch (error) {
6865
+ logger.warn("[Application] Failed to dispose scoped instances", {
6866
+ path: context.path,
6867
+ method: context.method,
6868
+ errorMessage: error instanceof Error ? error.message : String(error)
6869
+ });
6870
+ }
6871
+ }
6676
6872
  });
6677
6873
  }
6678
6874
  registerController(controllerClass) {
@@ -13864,7 +14060,7 @@ class AiService {
13864
14060
  const fallback = this.options.fallback ?? false;
13865
14061
  const timeout = this.options.timeout ?? 30000;
13866
14062
  if (!fallback) {
13867
- return this.withTimeout(this.getProvider(targetName).complete(request), timeout, targetName);
14063
+ return this.withTimeout(this.getProvider(targetName).complete(request), timeout, targetName, request.signal);
13868
14064
  }
13869
14065
  const names = [
13870
14066
  targetName,
@@ -13876,21 +14072,32 @@ class AiService {
13876
14072
  const provider = this.providers.get(name);
13877
14073
  if (!provider)
13878
14074
  continue;
13879
- return await this.withTimeout(provider.complete({ ...request, provider: name }), timeout, name);
14075
+ return await this.withTimeout(provider.complete({ ...request, provider: name }), timeout, name, request.signal);
13880
14076
  } catch (err) {
13881
14077
  errors.push(`${name}: ${err instanceof Error ? err.message : String(err)}`);
13882
14078
  }
13883
14079
  }
13884
14080
  throw new AiAllProvidersFailed(errors);
13885
14081
  }
13886
- withTimeout(promise, ms, providerName) {
14082
+ withTimeout(promise, ms, providerName, signal) {
13887
14083
  return new Promise((resolve2, reject) => {
14084
+ if (signal?.aborted) {
14085
+ reject(signal.reason ?? new Error("Aborted"));
14086
+ return;
14087
+ }
13888
14088
  const timer = setTimeout(() => reject(new AiTimeoutError(providerName, ms)), ms);
14089
+ const onAbort = () => {
14090
+ clearTimeout(timer);
14091
+ reject(signal.reason ?? new Error("Aborted"));
14092
+ };
14093
+ signal?.addEventListener("abort", onAbort, { once: true });
13889
14094
  promise.then((val) => {
13890
14095
  clearTimeout(timer);
14096
+ signal?.removeEventListener("abort", onAbort);
13891
14097
  resolve2(val);
13892
14098
  }, (err) => {
13893
14099
  clearTimeout(timer);
14100
+ signal?.removeEventListener("abort", onAbort);
13894
14101
  reject(err);
13895
14102
  });
13896
14103
  });
@@ -14016,7 +14223,7 @@ class OpenAIProvider {
14016
14223
  function: { name: t.name, description: t.description, parameters: t.parameters }
14017
14224
  }));
14018
14225
  }
14019
- const response = await this.post("/chat/completions", body);
14226
+ const response = await this.post("/chat/completions", body, request.signal);
14020
14227
  const choice = response.choices?.[0];
14021
14228
  const usage = response.usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
14022
14229
  const message = choice?.message;
@@ -14056,6 +14263,7 @@ class OpenAIProvider {
14056
14263
  const encoder = new TextEncoder;
14057
14264
  const apiKey = this.apiKey;
14058
14265
  const baseUrl = this.baseUrl;
14266
+ const signal = request.signal;
14059
14267
  return new ReadableStream({
14060
14268
  async start(controller2) {
14061
14269
  try {
@@ -14065,7 +14273,8 @@ class OpenAIProvider {
14065
14273
  "Content-Type": "application/json",
14066
14274
  Authorization: `Bearer ${apiKey}`
14067
14275
  },
14068
- body: JSON.stringify(body)
14276
+ body: JSON.stringify(body),
14277
+ signal
14069
14278
  });
14070
14279
  if (!res.ok || !res.body) {
14071
14280
  const err = await res.text();
@@ -14119,14 +14328,15 @@ class OpenAIProvider {
14119
14328
  countTokens(messages) {
14120
14329
  return Math.ceil(messages.reduce((sum, m) => sum + m.content.length, 0) / 4);
14121
14330
  }
14122
- async post(path, body) {
14331
+ async post(path, body, signal) {
14123
14332
  const res = await fetch(`${this.baseUrl}${path}`, {
14124
14333
  method: "POST",
14125
14334
  headers: {
14126
14335
  "Content-Type": "application/json",
14127
14336
  Authorization: `Bearer ${this.apiKey}`
14128
14337
  },
14129
- body: JSON.stringify(body)
14338
+ body: JSON.stringify(body),
14339
+ signal
14130
14340
  });
14131
14341
  if (res.status === 429) {
14132
14342
  const retryAfter = res.headers.get("retry-after");
@@ -14202,7 +14412,7 @@ class AnthropicProvider {
14202
14412
  input_schema: t.parameters
14203
14413
  }));
14204
14414
  }
14205
- const response = await this.post("/v1/messages", body);
14415
+ const response = await this.post("/v1/messages", body, request.signal);
14206
14416
  const usage = response["usage"] ?? { input_tokens: 0, output_tokens: 0 };
14207
14417
  let content = "";
14208
14418
  const toolCalls = [];
@@ -14250,6 +14460,7 @@ class AnthropicProvider {
14250
14460
  const baseUrl = this.baseUrl;
14251
14461
  const anthropicVersion = this.anthropicVersion;
14252
14462
  const encoder = new TextEncoder;
14463
+ const signal = request.signal;
14253
14464
  return new ReadableStream({
14254
14465
  async start(controller2) {
14255
14466
  try {
@@ -14260,7 +14471,8 @@ class AnthropicProvider {
14260
14471
  "x-api-key": apiKey,
14261
14472
  "anthropic-version": anthropicVersion
14262
14473
  },
14263
- body: JSON.stringify(body)
14474
+ body: JSON.stringify(body),
14475
+ signal
14264
14476
  });
14265
14477
  if (!res.ok || !res.body) {
14266
14478
  controller2.enqueue(encoder.encode(`data: ${JSON.stringify({ error: await res.text(), done: true })}
@@ -14310,7 +14522,7 @@ class AnthropicProvider {
14310
14522
  countTokens(messages) {
14311
14523
  return Math.ceil(messages.reduce((sum, m) => sum + m.content.length, 0) / 4);
14312
14524
  }
14313
- async post(path, body) {
14525
+ async post(path, body, signal) {
14314
14526
  const res = await fetch(`${this.baseUrl}${path}`, {
14315
14527
  method: "POST",
14316
14528
  headers: {
@@ -14318,7 +14530,8 @@ class AnthropicProvider {
14318
14530
  "x-api-key": this.apiKey,
14319
14531
  "anthropic-version": this.anthropicVersion
14320
14532
  },
14321
- body: JSON.stringify(body)
14533
+ body: JSON.stringify(body),
14534
+ signal
14322
14535
  });
14323
14536
  if (res.status === 429)
14324
14537
  throw new AiRateLimitError(this.name);
@@ -14351,7 +14564,8 @@ class OllamaProvider {
14351
14564
  temperature: request.temperature,
14352
14565
  num_predict: request.maxTokens
14353
14566
  }
14354
- })
14567
+ }),
14568
+ signal: request.signal
14355
14569
  });
14356
14570
  if (!res.ok) {
14357
14571
  throw new AiProviderError(await res.text(), this.name, res.status);
@@ -14376,6 +14590,7 @@ class OllamaProvider {
14376
14590
  const model = request.model ?? this.defaultModel;
14377
14591
  const baseUrl = this.baseUrl;
14378
14592
  const encoder = new TextEncoder;
14593
+ const signal = request.signal;
14379
14594
  return new ReadableStream({
14380
14595
  async start(controller2) {
14381
14596
  try {
@@ -14390,7 +14605,8 @@ class OllamaProvider {
14390
14605
  temperature: request.temperature,
14391
14606
  num_predict: request.maxTokens
14392
14607
  }
14393
- })
14608
+ }),
14609
+ signal
14394
14610
  });
14395
14611
  if (!res.ok || !res.body) {
14396
14612
  controller2.enqueue(encoder.encode(`data: ${JSON.stringify({ error: await res.text(), done: true })}
@@ -14472,7 +14688,8 @@ class GoogleProvider {
14472
14688
  const res = await fetch(`${this.baseUrl}/models/${model}:generateContent?key=${this.apiKey}`, {
14473
14689
  method: "POST",
14474
14690
  headers: { "Content-Type": "application/json" },
14475
- body: JSON.stringify(body)
14691
+ body: JSON.stringify(body),
14692
+ signal: request.signal
14476
14693
  });
14477
14694
  if (res.status === 429)
14478
14695
  throw new AiRateLimitError(this.name);
@@ -14515,6 +14732,7 @@ class GoogleProvider {
14515
14732
  const apiKey = this.apiKey;
14516
14733
  const baseUrl = this.baseUrl;
14517
14734
  const encoder = new TextEncoder;
14735
+ const signal = request.signal;
14518
14736
  const body = {
14519
14737
  contents,
14520
14738
  generationConfig: { temperature: request.temperature, maxOutputTokens: request.maxTokens }
@@ -14527,7 +14745,8 @@ class GoogleProvider {
14527
14745
  const res = await fetch(`${baseUrl}/models/${model}:streamGenerateContent?key=${apiKey}&alt=sse`, {
14528
14746
  method: "POST",
14529
14747
  headers: { "Content-Type": "application/json" },
14530
- body: JSON.stringify(body)
14748
+ body: JSON.stringify(body),
14749
+ signal
14531
14750
  });
14532
14751
  if (!res.ok || !res.body) {
14533
14752
  controller2.enqueue(encoder.encode(`data: ${JSON.stringify({ error: await res.text(), done: true })}
@@ -15891,8 +16110,6 @@ class McpServer {
15891
16110
  });
15892
16111
  }
15893
16112
  createSseResponse() {
15894
- const registry = this.registry;
15895
- const serverInfo = this.serverInfo;
15896
16113
  const encoder = new TextEncoder;
15897
16114
  const stream = new ReadableStream({
15898
16115
  start(controller2) {
@@ -15904,15 +16121,6 @@ data: ${JSON.stringify({
15904
16121
 
15905
16122
  `;
15906
16123
  controller2.enqueue(encoder.encode(initEvent));
15907
- const pingInterval = setInterval(() => {
15908
- try {
15909
- controller2.enqueue(encoder.encode(`: ping
15910
-
15911
- `));
15912
- } catch (_error) {
15913
- clearInterval(pingInterval);
15914
- }
15915
- }, 15000);
15916
16124
  }
15917
16125
  });
15918
16126
  return new Response(stream, {
@@ -15920,7 +16128,7 @@ data: ${JSON.stringify({
15920
16128
  "Content-Type": "text/event-stream",
15921
16129
  "Cache-Control": "no-cache",
15922
16130
  Connection: "keep-alive",
15923
- "X-MCP-Server": `${serverInfo.name}/${serverInfo.version}`
16131
+ "X-MCP-Server": `${this.serverInfo.name}/${this.serverInfo.version}`
15924
16132
  }
15925
16133
  });
15926
16134
  }