vafast 0.3.10 → 0.4.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/README.md +61 -0
- package/dist/defineRoute.d.ts +101 -3
- package/dist/defineRoute.js +30 -1
- package/dist/defineRoute.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +363 -50
- package/dist/index.js.map +1 -1
- package/dist/monitoring/index.js +18 -1
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/native-monitor.js +18 -1
- package/dist/monitoring/native-monitor.js.map +1 -1
- package/dist/node-server/index.js +46 -2
- package/dist/node-server/index.js.map +1 -1
- package/dist/node-server/serve.d.ts +16 -1
- package/dist/node-server/serve.js +46 -2
- package/dist/node-server/serve.js.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router.js +2 -1
- package/dist/router.js.map +1 -1
- package/dist/serve.js +46 -2
- package/dist/serve.js.map +1 -1
- package/dist/server/index.js +18 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/server-factory.js +18 -1
- package/dist/server/server-factory.js.map +1 -1
- package/dist/server/server.d.ts +15 -1
- package/dist/server/server.js +18 -1
- package/dist/server/server.js.map +1 -1
- package/dist/types/types.d.ts +12 -0
- package/dist/utils/create-handler.d.ts +12 -3
- package/dist/utils/create-handler.js +2 -1
- package/dist/utils/create-handler.js.map +1 -1
- package/dist/utils/index.d.ts +4 -1
- package/dist/utils/index.js +224 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/route-registry.d.ts +134 -0
- package/dist/utils/route-registry.js +128 -0
- package/dist/utils/route-registry.js.map +1 -0
- package/dist/utils/sse.d.ts +87 -0
- package/dist/utils/sse.js +181 -0
- package/dist/utils/sse.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
// src/router.ts
|
|
2
2
|
function flattenNestedRoutes(routes) {
|
|
3
3
|
const flattened = [];
|
|
4
|
-
function processRoute(
|
|
5
|
-
const currentPath = normalizePath(parentPath +
|
|
4
|
+
function processRoute(route2, parentPath = "", parentMiddleware = []) {
|
|
5
|
+
const currentPath = normalizePath(parentPath + route2.path);
|
|
6
6
|
const currentMiddleware = [
|
|
7
7
|
...parentMiddleware,
|
|
8
|
-
...
|
|
8
|
+
...route2.middleware || []
|
|
9
9
|
];
|
|
10
|
-
if ("method" in
|
|
10
|
+
if ("method" in route2 && "handler" in route2) {
|
|
11
|
+
const leafRoute = route2;
|
|
11
12
|
flattened.push({
|
|
12
|
-
...
|
|
13
|
+
...leafRoute,
|
|
13
14
|
fullPath: currentPath,
|
|
14
15
|
middlewareChain: currentMiddleware
|
|
15
16
|
});
|
|
16
|
-
} else if ("children" in
|
|
17
|
-
for (const child of
|
|
17
|
+
} else if ("children" in route2 && route2.children) {
|
|
18
|
+
for (const child of route2.children) {
|
|
18
19
|
processRoute(child, currentPath, currentMiddleware);
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
for (const
|
|
23
|
-
processRoute(
|
|
23
|
+
for (const route2 of routes) {
|
|
24
|
+
processRoute(route2);
|
|
24
25
|
}
|
|
25
26
|
return flattened;
|
|
26
27
|
}
|
|
@@ -187,12 +188,12 @@ var BaseServer = class {
|
|
|
187
188
|
*/
|
|
188
189
|
logFlattenedRoutes(routes, type = "\u8DEF\u7531") {
|
|
189
190
|
console.log(`\u{1F680} \u6241\u5E73\u5316\u540E\u7684${type}:`);
|
|
190
|
-
for (const
|
|
191
|
-
const method =
|
|
192
|
-
const path =
|
|
191
|
+
for (const route2 of routes) {
|
|
192
|
+
const method = route2.method || "GET";
|
|
193
|
+
const path = route2.fullPath || route2.path;
|
|
193
194
|
console.log(` ${method} ${path}`);
|
|
194
|
-
if (
|
|
195
|
-
console.log(` \u4E2D\u95F4\u4EF6\u94FE: ${
|
|
195
|
+
if (route2.middlewareChain && route2.middlewareChain.length > 0) {
|
|
196
|
+
console.log(` \u4E2D\u95F4\u4EF6\u94FE: ${route2.middlewareChain.length} \u4E2A`);
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
console.log("");
|
|
@@ -203,13 +204,13 @@ var BaseServer = class {
|
|
|
203
204
|
*/
|
|
204
205
|
detectRouteConflicts(routes) {
|
|
205
206
|
const pathGroups = /* @__PURE__ */ new Map();
|
|
206
|
-
for (const
|
|
207
|
-
const path =
|
|
208
|
-
const method =
|
|
207
|
+
for (const route2 of routes) {
|
|
208
|
+
const path = route2.fullPath || route2.path;
|
|
209
|
+
const method = route2.method || "GET";
|
|
209
210
|
if (!pathGroups.has(path)) {
|
|
210
211
|
pathGroups.set(path, []);
|
|
211
212
|
}
|
|
212
|
-
pathGroups.get(path).push({ ...
|
|
213
|
+
pathGroups.get(path).push({ ...route2, method });
|
|
213
214
|
}
|
|
214
215
|
for (const [path, routeList] of pathGroups) {
|
|
215
216
|
if (routeList.length > 1) {
|
|
@@ -219,8 +220,8 @@ var BaseServer = class {
|
|
|
219
220
|
console.warn(
|
|
220
221
|
`\u26A0\uFE0F \u8DEF\u7531\u51B2\u7A81: ${uniqueMethods[0]} ${path} \u5B9A\u4E49\u4E86 ${routeList.length} \u6B21`
|
|
221
222
|
);
|
|
222
|
-
routeList.forEach((
|
|
223
|
-
console.warn(` ${index + 1}. ${
|
|
223
|
+
routeList.forEach((route2, index) => {
|
|
224
|
+
console.warn(` ${index + 1}. ${route2.method} ${path}`);
|
|
224
225
|
});
|
|
225
226
|
} else {
|
|
226
227
|
console.log(`\u2139\uFE0F \u8DEF\u5F84 ${path} \u652F\u6301\u65B9\u6CD5: ${uniqueMethods.join(", ")}`);
|
|
@@ -526,12 +527,12 @@ var Server = class extends BaseServer {
|
|
|
526
527
|
registerRoutes(routes) {
|
|
527
528
|
const flattened = flattenNestedRoutes(routes);
|
|
528
529
|
this.routes.push(...flattened);
|
|
529
|
-
for (const
|
|
530
|
+
for (const route2 of flattened) {
|
|
530
531
|
this.router.register(
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
532
|
+
route2.method,
|
|
533
|
+
route2.fullPath,
|
|
534
|
+
route2.handler,
|
|
535
|
+
route2.middlewareChain || []
|
|
535
536
|
);
|
|
536
537
|
}
|
|
537
538
|
this.detectRouteConflicts(flattened);
|
|
@@ -608,18 +609,18 @@ var Server = class extends BaseServer {
|
|
|
608
609
|
}
|
|
609
610
|
return this.createErrorResponse(method, pathname);
|
|
610
611
|
};
|
|
611
|
-
addRoute(
|
|
612
|
+
addRoute(route2) {
|
|
612
613
|
const flattenedRoute = {
|
|
613
|
-
...
|
|
614
|
-
fullPath:
|
|
615
|
-
middlewareChain:
|
|
614
|
+
...route2,
|
|
615
|
+
fullPath: route2.path,
|
|
616
|
+
middlewareChain: route2.middleware || []
|
|
616
617
|
};
|
|
617
618
|
this.routes.push(flattenedRoute);
|
|
618
619
|
this.router.register(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
620
|
+
route2.method,
|
|
621
|
+
route2.path,
|
|
622
|
+
route2.handler,
|
|
623
|
+
route2.middleware || []
|
|
623
624
|
);
|
|
624
625
|
}
|
|
625
626
|
addRoutes(routes) {
|
|
@@ -628,31 +629,47 @@ var Server = class extends BaseServer {
|
|
|
628
629
|
getRoutes() {
|
|
629
630
|
return this.router.getRoutes();
|
|
630
631
|
}
|
|
632
|
+
/**
|
|
633
|
+
* 获取完整的路由元信息(不含 handler 和 middleware)
|
|
634
|
+
*
|
|
635
|
+
* 用于 API 文档生成、Webhook 事件注册、权限检查等场景
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```typescript
|
|
639
|
+
* const routes = server.getRoutesWithMeta()
|
|
640
|
+
* for (const route of routes) {
|
|
641
|
+
* console.log(route.fullPath, route.name, route.description)
|
|
642
|
+
* }
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
645
|
+
getRoutesWithMeta() {
|
|
646
|
+
return this.routes;
|
|
647
|
+
}
|
|
631
648
|
};
|
|
632
649
|
|
|
633
650
|
// src/middleware/component-router.ts
|
|
634
651
|
function flattenComponentRoutes(routes) {
|
|
635
652
|
const flattened = [];
|
|
636
|
-
function processRoute(
|
|
637
|
-
const currentPath = parentPath +
|
|
653
|
+
function processRoute(route2, parentPath = "", parentMiddleware = []) {
|
|
654
|
+
const currentPath = parentPath + route2.path;
|
|
638
655
|
const currentMiddleware = [
|
|
639
656
|
...parentMiddleware,
|
|
640
|
-
...
|
|
657
|
+
...route2.middleware || []
|
|
641
658
|
];
|
|
642
|
-
if ("component" in
|
|
659
|
+
if ("component" in route2) {
|
|
643
660
|
flattened.push({
|
|
644
|
-
...
|
|
661
|
+
...route2,
|
|
645
662
|
fullPath: currentPath,
|
|
646
663
|
middlewareChain: currentMiddleware
|
|
647
664
|
});
|
|
648
|
-
} else if ("children" in
|
|
649
|
-
for (const child of
|
|
665
|
+
} else if ("children" in route2 && route2.children) {
|
|
666
|
+
for (const child of route2.children) {
|
|
650
667
|
processRoute(child, currentPath, currentMiddleware);
|
|
651
668
|
}
|
|
652
669
|
}
|
|
653
670
|
}
|
|
654
|
-
for (const
|
|
655
|
-
processRoute(
|
|
671
|
+
for (const route2 of routes) {
|
|
672
|
+
processRoute(route2);
|
|
656
673
|
}
|
|
657
674
|
return flattened;
|
|
658
675
|
}
|
|
@@ -880,9 +897,9 @@ var ComponentServer = class extends BaseServer {
|
|
|
880
897
|
return new Response("Method Not Allowed", { status: 405 });
|
|
881
898
|
}
|
|
882
899
|
let matchedRoute = null;
|
|
883
|
-
for (const
|
|
884
|
-
if (PathMatcher.matchPath(
|
|
885
|
-
matchedRoute =
|
|
900
|
+
for (const route2 of this.routes) {
|
|
901
|
+
if (PathMatcher.matchPath(route2.fullPath, pathname)) {
|
|
902
|
+
matchedRoute = route2;
|
|
886
903
|
break;
|
|
887
904
|
}
|
|
888
905
|
}
|
|
@@ -1314,7 +1331,7 @@ function createHandler(schemaOrHandler, maybeHandler) {
|
|
|
1314
1331
|
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1315
1332
|
precompileSchemas(schema);
|
|
1316
1333
|
}
|
|
1317
|
-
|
|
1334
|
+
const handlerFn = async (req) => {
|
|
1318
1335
|
try {
|
|
1319
1336
|
const query = parseQuery(req);
|
|
1320
1337
|
const headers = parseHeaders(req);
|
|
@@ -1345,6 +1362,7 @@ function createHandler(schemaOrHandler, maybeHandler) {
|
|
|
1345
1362
|
return handleInternalError(error);
|
|
1346
1363
|
}
|
|
1347
1364
|
};
|
|
1365
|
+
return handlerFn;
|
|
1348
1366
|
}
|
|
1349
1367
|
function createHandlerWithExtra(schemaOrHandler, maybeHandler) {
|
|
1350
1368
|
const hasSchema = !isHandler(schemaOrHandler);
|
|
@@ -1587,6 +1605,225 @@ var Patterns = {
|
|
|
1587
1605
|
JWT: JWT_REGEX
|
|
1588
1606
|
};
|
|
1589
1607
|
|
|
1608
|
+
// src/utils/sse.ts
|
|
1609
|
+
function formatSSEEvent(event) {
|
|
1610
|
+
const lines = [];
|
|
1611
|
+
if (event.id !== void 0) {
|
|
1612
|
+
lines.push(`id: ${event.id}`);
|
|
1613
|
+
}
|
|
1614
|
+
if (event.event !== void 0) {
|
|
1615
|
+
lines.push(`event: ${event.event}`);
|
|
1616
|
+
}
|
|
1617
|
+
if (event.retry !== void 0) {
|
|
1618
|
+
lines.push(`retry: ${event.retry}`);
|
|
1619
|
+
}
|
|
1620
|
+
const dataStr = typeof event.data === "string" ? event.data : JSON.stringify(event.data);
|
|
1621
|
+
const dataLines = dataStr.split("\n");
|
|
1622
|
+
for (const line of dataLines) {
|
|
1623
|
+
lines.push(`data: ${line}`);
|
|
1624
|
+
}
|
|
1625
|
+
return lines.join("\n") + "\n\n";
|
|
1626
|
+
}
|
|
1627
|
+
function createSSEHandler(schemaOrGenerator, maybeGenerator) {
|
|
1628
|
+
const hasSchema = typeof schemaOrGenerator !== "function";
|
|
1629
|
+
const schema = hasSchema ? schemaOrGenerator : {};
|
|
1630
|
+
const generator = hasSchema ? maybeGenerator : schemaOrGenerator;
|
|
1631
|
+
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1632
|
+
precompileSchemas(schema);
|
|
1633
|
+
}
|
|
1634
|
+
const handlerFn = async (req) => {
|
|
1635
|
+
try {
|
|
1636
|
+
const query = parseQuery(req);
|
|
1637
|
+
const headers = parseHeaders(req);
|
|
1638
|
+
const cookies = parseCookies(req);
|
|
1639
|
+
const params = req.params || {};
|
|
1640
|
+
const data = { body: void 0, query, params, headers, cookies };
|
|
1641
|
+
if (schema.body || schema.query || schema.params || schema.headers || schema.cookies) {
|
|
1642
|
+
validateAllSchemas(schema, data);
|
|
1643
|
+
}
|
|
1644
|
+
const stream2 = new ReadableStream({
|
|
1645
|
+
async start(controller) {
|
|
1646
|
+
const encoder2 = new TextEncoder();
|
|
1647
|
+
try {
|
|
1648
|
+
const gen = generator({
|
|
1649
|
+
req,
|
|
1650
|
+
body: void 0,
|
|
1651
|
+
query,
|
|
1652
|
+
params,
|
|
1653
|
+
headers,
|
|
1654
|
+
cookies
|
|
1655
|
+
});
|
|
1656
|
+
for await (const event of gen) {
|
|
1657
|
+
const formatted = formatSSEEvent(event);
|
|
1658
|
+
controller.enqueue(encoder2.encode(formatted));
|
|
1659
|
+
}
|
|
1660
|
+
} catch (error) {
|
|
1661
|
+
const errorEvent = formatSSEEvent({
|
|
1662
|
+
event: "error",
|
|
1663
|
+
data: {
|
|
1664
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
1665
|
+
}
|
|
1666
|
+
});
|
|
1667
|
+
controller.enqueue(encoder2.encode(errorEvent));
|
|
1668
|
+
} finally {
|
|
1669
|
+
controller.close();
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1673
|
+
return new Response(stream2, {
|
|
1674
|
+
headers: {
|
|
1675
|
+
"Content-Type": "text/event-stream",
|
|
1676
|
+
"Cache-Control": "no-cache",
|
|
1677
|
+
"Connection": "keep-alive",
|
|
1678
|
+
"X-Accel-Buffering": "no"
|
|
1679
|
+
// Nginx 禁用缓冲
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
} catch (error) {
|
|
1683
|
+
return new Response(
|
|
1684
|
+
JSON.stringify({
|
|
1685
|
+
success: false,
|
|
1686
|
+
error: "Validation Error",
|
|
1687
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
1688
|
+
}),
|
|
1689
|
+
{
|
|
1690
|
+
status: 400,
|
|
1691
|
+
headers: { "Content-Type": "application/json" }
|
|
1692
|
+
}
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
const handler = handlerFn;
|
|
1697
|
+
handler.__sse = { __brand: "SSE" };
|
|
1698
|
+
handler.__schema = schema;
|
|
1699
|
+
handler.__returnType = void 0;
|
|
1700
|
+
return handler;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// src/utils/route-registry.ts
|
|
1704
|
+
var RouteRegistry = class {
|
|
1705
|
+
/** 所有路由元信息 */
|
|
1706
|
+
routes = [];
|
|
1707
|
+
/** 路由映射表:METHOD:fullPath -> RouteMeta */
|
|
1708
|
+
routeMap = /* @__PURE__ */ new Map();
|
|
1709
|
+
/** 分类映射表:category -> RouteMeta[] */
|
|
1710
|
+
categoryMap = /* @__PURE__ */ new Map();
|
|
1711
|
+
constructor(routes) {
|
|
1712
|
+
this.buildRegistry(routes);
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* 构建注册表
|
|
1716
|
+
*/
|
|
1717
|
+
buildRegistry(routes) {
|
|
1718
|
+
for (const route2 of routes) {
|
|
1719
|
+
const meta = {
|
|
1720
|
+
method: route2.method,
|
|
1721
|
+
path: route2.path,
|
|
1722
|
+
fullPath: route2.fullPath,
|
|
1723
|
+
name: route2.name,
|
|
1724
|
+
description: route2.description
|
|
1725
|
+
};
|
|
1726
|
+
for (const key of Object.keys(route2)) {
|
|
1727
|
+
if (!["method", "path", "fullPath", "name", "description", "handler", "middleware", "middlewareChain"].includes(key)) {
|
|
1728
|
+
meta[key] = route2[key];
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
this.routes.push(meta);
|
|
1732
|
+
this.routeMap.set(`${route2.method}:${route2.fullPath}`, meta);
|
|
1733
|
+
const category = this.extractCategory(route2.fullPath);
|
|
1734
|
+
if (!this.categoryMap.has(category)) {
|
|
1735
|
+
this.categoryMap.set(category, []);
|
|
1736
|
+
}
|
|
1737
|
+
this.categoryMap.get(category).push(meta);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* 提取分类(第一段路径)
|
|
1742
|
+
*/
|
|
1743
|
+
extractCategory(path) {
|
|
1744
|
+
const segments = path.split("/").filter(Boolean);
|
|
1745
|
+
return segments[0] || "root";
|
|
1746
|
+
}
|
|
1747
|
+
// ============================================
|
|
1748
|
+
// 查询接口
|
|
1749
|
+
// ============================================
|
|
1750
|
+
/**
|
|
1751
|
+
* 获取所有路由元信息
|
|
1752
|
+
*/
|
|
1753
|
+
getAll() {
|
|
1754
|
+
return [...this.routes];
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* 按 method + path 查询路由
|
|
1758
|
+
*/
|
|
1759
|
+
get(method, path) {
|
|
1760
|
+
return this.routeMap.get(`${method}:${path}`);
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* 检查路由是否存在
|
|
1764
|
+
*/
|
|
1765
|
+
has(method, path) {
|
|
1766
|
+
return this.routeMap.has(`${method}:${path}`);
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* 按分类获取路由
|
|
1770
|
+
*/
|
|
1771
|
+
getByCategory(category) {
|
|
1772
|
+
return this.categoryMap.get(category) || [];
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* 获取所有分类
|
|
1776
|
+
*/
|
|
1777
|
+
getCategories() {
|
|
1778
|
+
return Array.from(this.categoryMap.keys()).sort();
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* 筛选有特定字段的路由
|
|
1782
|
+
*
|
|
1783
|
+
* @example
|
|
1784
|
+
* ```typescript
|
|
1785
|
+
* // 获取所有配置了 webhook 的路由
|
|
1786
|
+
* const webhookRoutes = registry.filter('webhook')
|
|
1787
|
+
* ```
|
|
1788
|
+
*/
|
|
1789
|
+
filter(field) {
|
|
1790
|
+
return this.routes.filter((r) => field in r && r[field] !== void 0);
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* 按条件筛选路由
|
|
1794
|
+
*
|
|
1795
|
+
* @example
|
|
1796
|
+
* ```typescript
|
|
1797
|
+
* // 获取所有 POST 请求
|
|
1798
|
+
* const postRoutes = registry.filterBy(r => r.method === 'POST')
|
|
1799
|
+
* ```
|
|
1800
|
+
*/
|
|
1801
|
+
filterBy(predicate) {
|
|
1802
|
+
return this.routes.filter(predicate);
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* 获取路由数量
|
|
1806
|
+
*/
|
|
1807
|
+
get size() {
|
|
1808
|
+
return this.routes.length;
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* 遍历所有路由
|
|
1812
|
+
*/
|
|
1813
|
+
forEach(callback) {
|
|
1814
|
+
this.routes.forEach(callback);
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* 映射所有路由
|
|
1818
|
+
*/
|
|
1819
|
+
map(callback) {
|
|
1820
|
+
return this.routes.map(callback);
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
function createRouteRegistry(routes) {
|
|
1824
|
+
return new RouteRegistry(routes);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1590
1827
|
// src/middleware/authMiddleware.ts
|
|
1591
1828
|
var requireAuth = async (req, next) => {
|
|
1592
1829
|
const token = getCookie2(req, "auth");
|
|
@@ -1885,6 +2122,29 @@ function createPermissionAuth(permissions, options) {
|
|
|
1885
2122
|
}
|
|
1886
2123
|
|
|
1887
2124
|
// src/defineRoute.ts
|
|
2125
|
+
function route(method, path, handler, middleware) {
|
|
2126
|
+
return {
|
|
2127
|
+
method,
|
|
2128
|
+
path,
|
|
2129
|
+
handler,
|
|
2130
|
+
middleware
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
function get(path, handler, middleware) {
|
|
2134
|
+
return route("GET", path, handler, middleware);
|
|
2135
|
+
}
|
|
2136
|
+
function post(path, handler, middleware) {
|
|
2137
|
+
return route("POST", path, handler, middleware);
|
|
2138
|
+
}
|
|
2139
|
+
function put(path, handler, middleware) {
|
|
2140
|
+
return route("PUT", path, handler, middleware);
|
|
2141
|
+
}
|
|
2142
|
+
function del(path, handler, middleware) {
|
|
2143
|
+
return route("DELETE", path, handler, middleware);
|
|
2144
|
+
}
|
|
2145
|
+
function patch(path, handler, middleware) {
|
|
2146
|
+
return route("PATCH", path, handler, middleware);
|
|
2147
|
+
}
|
|
1888
2148
|
function defineRoutes(routes) {
|
|
1889
2149
|
return routes;
|
|
1890
2150
|
}
|
|
@@ -1893,8 +2153,8 @@ function defineRoutes(routes) {
|
|
|
1893
2153
|
function createTypedRoute(config) {
|
|
1894
2154
|
return config;
|
|
1895
2155
|
}
|
|
1896
|
-
function isTypedRoute(
|
|
1897
|
-
return
|
|
2156
|
+
function isTypedRoute(route2) {
|
|
2157
|
+
return route2 && typeof route2 === "object" && "method" in route2 && "path" in route2 && "handler" in route2;
|
|
1898
2158
|
}
|
|
1899
2159
|
|
|
1900
2160
|
// src/node-server/serve.ts
|
|
@@ -2108,10 +2368,53 @@ function createRequestHandler(fetch, defaultHost, onError) {
|
|
|
2108
2368
|
};
|
|
2109
2369
|
}
|
|
2110
2370
|
function serve(options, callback) {
|
|
2111
|
-
const { fetch, port = 3e3, hostname = "0.0.0.0", onError } = options;
|
|
2371
|
+
const { fetch, port = 3e3, hostname = "0.0.0.0", onError, gracefulShutdown } = options;
|
|
2112
2372
|
const defaultHost = `${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}`;
|
|
2113
2373
|
const handler = createRequestHandler(fetch, defaultHost, onError);
|
|
2114
2374
|
const server = createServer(handler);
|
|
2375
|
+
const connections = /* @__PURE__ */ new Set();
|
|
2376
|
+
server.on("connection", (socket) => {
|
|
2377
|
+
connections.add(socket);
|
|
2378
|
+
socket.on("close", () => connections.delete(socket));
|
|
2379
|
+
});
|
|
2380
|
+
let isShuttingDown = false;
|
|
2381
|
+
const shutdown = async () => {
|
|
2382
|
+
if (isShuttingDown) return;
|
|
2383
|
+
isShuttingDown = true;
|
|
2384
|
+
const shutdownOptions = typeof gracefulShutdown === "object" ? gracefulShutdown : {};
|
|
2385
|
+
const timeout = shutdownOptions.timeout ?? 3e4;
|
|
2386
|
+
if (shutdownOptions.onShutdown) {
|
|
2387
|
+
await shutdownOptions.onShutdown();
|
|
2388
|
+
}
|
|
2389
|
+
return new Promise((resolve) => {
|
|
2390
|
+
const forceCloseTimer = setTimeout(() => {
|
|
2391
|
+
for (const socket of connections) {
|
|
2392
|
+
socket.destroy();
|
|
2393
|
+
}
|
|
2394
|
+
connections.clear();
|
|
2395
|
+
resolve();
|
|
2396
|
+
}, timeout);
|
|
2397
|
+
server.close(() => {
|
|
2398
|
+
clearTimeout(forceCloseTimer);
|
|
2399
|
+
shutdownOptions.onShutdownComplete?.();
|
|
2400
|
+
resolve();
|
|
2401
|
+
});
|
|
2402
|
+
for (const socket of connections) {
|
|
2403
|
+
if (!socket.writableLength) {
|
|
2404
|
+
socket.end();
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
});
|
|
2408
|
+
};
|
|
2409
|
+
if (gracefulShutdown) {
|
|
2410
|
+
const shutdownOptions = typeof gracefulShutdown === "object" ? gracefulShutdown : {};
|
|
2411
|
+
const signals = shutdownOptions.signals ?? ["SIGINT", "SIGTERM"];
|
|
2412
|
+
for (const signal of signals) {
|
|
2413
|
+
process.on(signal, () => {
|
|
2414
|
+
shutdown().then(() => process.exit(0));
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2115
2418
|
server.listen(port, hostname, callback);
|
|
2116
2419
|
return {
|
|
2117
2420
|
server,
|
|
@@ -2122,7 +2425,8 @@ function serve(options, callback) {
|
|
|
2122
2425
|
if (err) reject(err);
|
|
2123
2426
|
else resolve();
|
|
2124
2427
|
});
|
|
2125
|
-
})
|
|
2428
|
+
}),
|
|
2429
|
+
shutdown
|
|
2126
2430
|
};
|
|
2127
2431
|
}
|
|
2128
2432
|
|
|
@@ -2136,6 +2440,7 @@ export {
|
|
|
2136
2440
|
FormatRegistry2 as FormatRegistry,
|
|
2137
2441
|
HtmlRenderer,
|
|
2138
2442
|
Patterns,
|
|
2443
|
+
RouteRegistry,
|
|
2139
2444
|
Server,
|
|
2140
2445
|
ServerFactory,
|
|
2141
2446
|
TokenError,
|
|
@@ -2152,13 +2457,17 @@ export {
|
|
|
2152
2457
|
createPermissionAuth,
|
|
2153
2458
|
createRequestValidator,
|
|
2154
2459
|
createRoleAuth,
|
|
2460
|
+
createRouteRegistry,
|
|
2461
|
+
createSSEHandler,
|
|
2155
2462
|
createTokenPair,
|
|
2156
2463
|
createTypedRoute,
|
|
2157
2464
|
createValidator,
|
|
2158
2465
|
defineRoutes,
|
|
2466
|
+
del,
|
|
2159
2467
|
empty,
|
|
2160
2468
|
flattenNestedRoutes,
|
|
2161
2469
|
generateToken,
|
|
2470
|
+
get,
|
|
2162
2471
|
getCookie,
|
|
2163
2472
|
getHeader,
|
|
2164
2473
|
getLocals,
|
|
@@ -2180,13 +2489,17 @@ export {
|
|
|
2180
2489
|
parseQueryFast,
|
|
2181
2490
|
parseRequest,
|
|
2182
2491
|
parseToken,
|
|
2492
|
+
patch,
|
|
2493
|
+
post,
|
|
2183
2494
|
precompileSchemas,
|
|
2495
|
+
put,
|
|
2184
2496
|
rateLimit,
|
|
2185
2497
|
redirect,
|
|
2186
2498
|
refreshToken,
|
|
2187
2499
|
registerFormat,
|
|
2188
2500
|
registerFormats,
|
|
2189
2501
|
requireAuth,
|
|
2502
|
+
route,
|
|
2190
2503
|
serve,
|
|
2191
2504
|
setLocals,
|
|
2192
2505
|
simpleHandler,
|