@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.
- package/dist/ai/providers/anthropic-provider.d.ts.map +1 -1
- package/dist/ai/providers/google-provider.d.ts.map +1 -1
- package/dist/ai/providers/ollama-provider.d.ts.map +1 -1
- package/dist/ai/providers/openai-provider.d.ts.map +1 -1
- package/dist/ai/service.d.ts.map +1 -1
- package/dist/ai/types.d.ts +5 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/core/application.d.ts +17 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/context.d.ts +5 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/server.d.ts +17 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/di/container.d.ts +16 -0
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/lifecycle.d.ts +48 -0
- package/dist/di/lifecycle.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +10 -6
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +316 -108
- package/dist/mcp/server.d.ts +5 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/docs/idle-timeout.md +99 -8
- package/docs/lifecycle.md +74 -4
- package/docs/zh/idle-timeout.md +97 -6
- package/docs/zh/lifecycle.md +47 -8
- package/package.json +1 -1
- package/src/ai/providers/anthropic-provider.ts +5 -2
- package/src/ai/providers/google-provider.ts +3 -0
- package/src/ai/providers/ollama-provider.ts +3 -0
- package/src/ai/providers/openai-provider.ts +5 -2
- package/src/ai/service.ts +17 -5
- package/src/ai/types.ts +5 -0
- package/src/core/application.ts +55 -23
- package/src/core/context.ts +7 -0
- package/src/core/server.ts +121 -18
- package/src/di/container.ts +55 -1
- package/src/di/lifecycle.ts +114 -0
- package/src/di/module-registry.ts +58 -10
- package/src/index.ts +4 -0
- package/src/mcp/server.ts +6 -15
- package/tests/di/lifecycle.test.ts +102 -1
- 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
|
-
|
|
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
|
|
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 =
|
|
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.
|
|
4226
|
-
|
|
4227
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
4965
|
+
const instances = this.resolveAllComponentInstances();
|
|
4812
4966
|
await callOnModuleInit(instances);
|
|
4813
4967
|
}
|
|
4814
4968
|
async callBootstrapHooks() {
|
|
4815
|
-
const instances = this.
|
|
4969
|
+
const instances = this.resolveAllComponentInstances();
|
|
4816
4970
|
await callOnApplicationBootstrap(instances);
|
|
4817
4971
|
}
|
|
4818
4972
|
async callModuleDestroyHooks() {
|
|
4819
|
-
const instances = this.
|
|
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.
|
|
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
|
-
|
|
6659
|
-
|
|
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
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
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
|
-
|
|
6674
|
-
|
|
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
|
}
|