@lytjs/http-server 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @lytjs/http-server
2
+
3
+ > LytJS HTTP 服务器。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@lytjs/http-server.svg)](https://www.npmjs.com/package/@lytjs/http-server)
6
+ [![license](https://img.shields.io/npm/l/@lytjs/http-server.svg)](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
7
+
8
+ ## 简介
9
+
10
+ `@lytjs/http-server` 是 LytJS 框架的 HTTP 服务器,支持路由、中间件、静态文件服务等功能。
11
+
12
+ ### 核心特性
13
+
14
+ - **RESTful 路由**:支持动态路由和路径参数
15
+ - **中间件支持**:完整的中间件系统
16
+ - **静态文件服务**:支持静态资源服务
17
+ - **Fetch API 兼容**:使用标准的 Request/Response API
18
+ - **零依赖**:不引入任何外部依赖
19
+
20
+ ## 安装
21
+
22
+ ```bash
23
+ npm install @lytjs/http-server
24
+ ```
25
+
26
+ 或使用 pnpm:
27
+
28
+ ```bash
29
+ pnpm add @lytjs/http-server
30
+ ```
31
+
32
+ ## 许可证
33
+
34
+ MIT License - [查看许可证](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
35
+
36
+ ## 贡献指南
37
+
38
+ 欢迎提交 Issue 和 Pull Request!
39
+
40
+ - [Gitee 仓库](https://gitee.com/lytjs/lytjs)
41
+ - [问题反馈](https://gitee.com/lytjs/lytjs/issues)
package/dist/index.cjs ADDED
@@ -0,0 +1,411 @@
1
+ 'use strict';
2
+
3
+ var http = require('http');
4
+ var commonQuery = require('@lytjs/common-query');
5
+
6
+ // src/server.ts
7
+
8
+ // src/router.ts
9
+ var PARAM_RE = /^:(\w+)(\??)?(\.\.\.)?$/;
10
+ var WILDCARD_RE = /^\*$/;
11
+ function tokenizePath(path) {
12
+ const segments = path.split("/");
13
+ const tokens = [];
14
+ for (const segment of segments) {
15
+ if (!segment) continue;
16
+ const paramMatch = segment.match(PARAM_RE);
17
+ if (paramMatch) {
18
+ const [, name, optional, repeatable] = paramMatch;
19
+ if (name) {
20
+ tokens.push({
21
+ type: "param",
22
+ name,
23
+ repeatable: repeatable === "...",
24
+ optional: optional === "?"
25
+ });
26
+ }
27
+ continue;
28
+ }
29
+ if (WILDCARD_RE.test(segment)) {
30
+ tokens.push({ type: "wildcard", value: "*" });
31
+ continue;
32
+ }
33
+ tokens.push({ type: "static", value: segment });
34
+ }
35
+ return tokens;
36
+ }
37
+ function scoreRoute(tokens) {
38
+ let score = 0;
39
+ for (const token of tokens) {
40
+ switch (token.type) {
41
+ case "static":
42
+ score += 3;
43
+ break;
44
+ case "param":
45
+ score += token.optional ? 1 : 2;
46
+ break;
47
+ case "wildcard":
48
+ score += 0;
49
+ break;
50
+ }
51
+ }
52
+ return score;
53
+ }
54
+ function matchPath(pathname, tokens, strict = false) {
55
+ const pathSegments = pathname.split("/").filter(Boolean);
56
+ const params = {};
57
+ let matched = true;
58
+ let i = 0;
59
+ for (const token of tokens) {
60
+ if (i >= pathSegments.length) {
61
+ if (token.type === "param" && token.optional) continue;
62
+ if (token.type === "wildcard") continue;
63
+ matched = false;
64
+ break;
65
+ }
66
+ const segment = pathSegments[i];
67
+ switch (token.type) {
68
+ case "static":
69
+ if (segment !== token.value) {
70
+ matched = false;
71
+ }
72
+ i++;
73
+ break;
74
+ case "param":
75
+ if (token.repeatable) {
76
+ params[token.name] = pathSegments.slice(i);
77
+ i = pathSegments.length;
78
+ } else if (segment !== void 0) {
79
+ params[token.name] = segment;
80
+ i++;
81
+ }
82
+ break;
83
+ case "wildcard":
84
+ params[token.value] = pathSegments.slice(i).join("/");
85
+ i = pathSegments.length;
86
+ break;
87
+ }
88
+ if (!matched) break;
89
+ }
90
+ if (matched && i < pathSegments.length) {
91
+ matched = false;
92
+ }
93
+ if (!strict && matched && pathSegments.length === 0 && tokens.length === 0) {
94
+ matched = true;
95
+ }
96
+ return {
97
+ matched,
98
+ params,
99
+ path: "/" + pathSegments.slice(0, i).join("/"),
100
+ score: scoreRoute(tokens)
101
+ };
102
+ }
103
+ var Router = class {
104
+ constructor() {
105
+ /** 路由列表 */
106
+ this.routes = [];
107
+ }
108
+ /**
109
+ * 添加路由
110
+ *
111
+ * @param method - HTTP 方法
112
+ * @param path - 路径
113
+ * @param handler - 处理器
114
+ * @returns 路由实例
115
+ */
116
+ on(method, path, handler) {
117
+ const tokens = tokenizePath(path);
118
+ this.routes.push({ method, path, tokens, handler });
119
+ return this;
120
+ }
121
+ /**
122
+ * 添加 GET 路由
123
+ *
124
+ * @param path - 路径
125
+ * @param handler - 处理器
126
+ * @returns 路由实例
127
+ */
128
+ get(path, handler) {
129
+ return this.on("GET", path, handler);
130
+ }
131
+ /**
132
+ * 添加 POST 路由
133
+ *
134
+ * @param path - 路径
135
+ * @param handler - 处理器
136
+ * @returns 路由实例
137
+ */
138
+ post(path, handler) {
139
+ return this.on("POST", path, handler);
140
+ }
141
+ /**
142
+ * 添加 PUT 路由
143
+ *
144
+ * @param path - 路径
145
+ * @param handler - 处理器
146
+ * @returns 路由实例
147
+ */
148
+ put(path, handler) {
149
+ return this.on("PUT", path, handler);
150
+ }
151
+ /**
152
+ * 添加 PATCH 路由
153
+ *
154
+ * @param path - 路径
155
+ * @param handler - 处理器
156
+ * @returns 路由实例
157
+ */
158
+ patch(path, handler) {
159
+ return this.on("PATCH", path, handler);
160
+ }
161
+ /**
162
+ * 添加 DELETE 路由
163
+ *
164
+ * @param path - 路径
165
+ * @param handler - 处理器
166
+ * @returns 路由实例
167
+ */
168
+ delete(path, handler) {
169
+ return this.on("DELETE", path, handler);
170
+ }
171
+ /**
172
+ * 匹配路由
173
+ *
174
+ * @param method - HTTP 方法
175
+ * @param path - 路径
176
+ * @returns 匹配结果或 null
177
+ */
178
+ match(method, path) {
179
+ const candidates = this.routes.filter((r) => r.method === method);
180
+ let bestMatch = null;
181
+ let bestScore = -1;
182
+ for (const route of candidates) {
183
+ const result = matchPath(path, route.tokens);
184
+ if (result.matched && result.score > bestScore) {
185
+ bestScore = result.score;
186
+ bestMatch = { handler: route.handler, params: result.params };
187
+ }
188
+ }
189
+ return bestMatch;
190
+ }
191
+ };
192
+ function createRouter() {
193
+ return new Router();
194
+ }
195
+ var Server = class {
196
+ /**
197
+ * 构造函数
198
+ */
199
+ constructor() {
200
+ /** 中间件列表 */
201
+ this.middlewares = [];
202
+ this.router = new Router();
203
+ }
204
+ /**
205
+ * 添加中间件
206
+ *
207
+ * @param middleware - 中间件函数
208
+ * @returns 服务器实例
209
+ */
210
+ use(middleware) {
211
+ this.middlewares.push(middleware);
212
+ return this;
213
+ }
214
+ /**
215
+ * 添加路由
216
+ *
217
+ * @param method - HTTP 方法
218
+ * @param path - 路径
219
+ * @param handler - 处理器
220
+ * @returns 服务器实例
221
+ */
222
+ on(method, path, handler) {
223
+ this.router.on(method, path, handler);
224
+ return this;
225
+ }
226
+ /**
227
+ * 添加 GET 路由
228
+ *
229
+ * @param path - 路径
230
+ * @param handler - 处理器
231
+ * @returns 服务器实例
232
+ */
233
+ get(path, handler) {
234
+ return this.on("GET", path, handler);
235
+ }
236
+ /**
237
+ * 添加 POST 路由
238
+ *
239
+ * @param path - 路径
240
+ * @param handler - 处理器
241
+ * @returns 服务器实例
242
+ */
243
+ post(path, handler) {
244
+ return this.on("POST", path, handler);
245
+ }
246
+ /**
247
+ * 添加 PUT 路由
248
+ *
249
+ * @param path - 路径
250
+ * @param handler - 处理器
251
+ * @returns 服务器实例
252
+ */
253
+ put(path, handler) {
254
+ return this.on("PUT", path, handler);
255
+ }
256
+ /**
257
+ * 添加 PATCH 路由
258
+ *
259
+ * @param path - 路径
260
+ * @param handler - 处理器
261
+ * @returns 服务器实例
262
+ */
263
+ patch(path, handler) {
264
+ return this.on("PATCH", path, handler);
265
+ }
266
+ /**
267
+ * 添加 DELETE 路由
268
+ *
269
+ * @param path - 路径
270
+ * @param handler - 处理器
271
+ * @returns 服务器实例
272
+ */
273
+ delete(path, handler) {
274
+ return this.on("DELETE", path, handler);
275
+ }
276
+ /**
277
+ * 启动服务器监听
278
+ *
279
+ * @param port - 端口
280
+ * @param hostname - 主机名
281
+ * @returns Promise
282
+ */
283
+ listen(port, hostname) {
284
+ return new Promise((resolve) => {
285
+ this.server = http.createServer(this.handleRequest.bind(this));
286
+ this.server.listen(port, hostname, () => {
287
+ resolve();
288
+ });
289
+ });
290
+ }
291
+ /**
292
+ * 关闭服务器
293
+ *
294
+ * @returns Promise
295
+ */
296
+ close() {
297
+ return new Promise((resolve, reject) => {
298
+ if (this.server) {
299
+ this.server.close((err) => {
300
+ if (err) {
301
+ reject(err);
302
+ } else {
303
+ resolve();
304
+ }
305
+ });
306
+ } else {
307
+ resolve();
308
+ }
309
+ });
310
+ }
311
+ /**
312
+ * 处理请求
313
+ *
314
+ * @param req - Node.js 请求对象
315
+ * @param res - Node.js 响应对象
316
+ */
317
+ async handleRequest(req, res) {
318
+ const url = req.url || "/";
319
+ const path = url.split("?")[0] || "/";
320
+ const method = req.method || "GET";
321
+ const request = {
322
+ method,
323
+ url,
324
+ path,
325
+ headers: req.headers,
326
+ query: commonQuery.parseQueryStringWithArrays(url),
327
+ params: {},
328
+ ip: req.socket.remoteAddress
329
+ };
330
+ const response = {
331
+ status: 200,
332
+ headers: {}
333
+ };
334
+ const ctx = {
335
+ request,
336
+ response
337
+ };
338
+ const match = this.router.match(method, path);
339
+ if (match) {
340
+ ctx.request.params = {};
341
+ for (const [key, value] of Object.entries(match.params)) {
342
+ if (Array.isArray(value)) {
343
+ ctx.request.params[key] = value[0] || "";
344
+ } else {
345
+ ctx.request.params[key] = value;
346
+ }
347
+ }
348
+ const handler = async () => {
349
+ await match.handler(ctx);
350
+ };
351
+ let index = this.middlewares.length;
352
+ const next = async () => {
353
+ index--;
354
+ if (index >= 0 && this.middlewares[index]) {
355
+ await this.middlewares[index](ctx, next);
356
+ } else {
357
+ await handler();
358
+ }
359
+ };
360
+ await next();
361
+ } else {
362
+ if (ctx.response) {
363
+ ctx.response.status = 404;
364
+ ctx.response.body = { error: "\u672A\u627E\u5230" };
365
+ }
366
+ }
367
+ if (ctx.response) {
368
+ this.sendResponse(res, ctx.response);
369
+ }
370
+ }
371
+ /**
372
+ * 发送响应
373
+ *
374
+ * @param res - Node.js 响应对象
375
+ * @param response - 响应对象
376
+ */
377
+ sendResponse(res, response) {
378
+ res.statusCode = response.status;
379
+ for (const [key, value] of Object.entries(response.headers)) {
380
+ if (Array.isArray(value)) {
381
+ for (const v of value) {
382
+ res.setHeader(key, v);
383
+ }
384
+ } else {
385
+ res.setHeader(key, value);
386
+ }
387
+ }
388
+ if (response.body !== void 0) {
389
+ const body = typeof response.body === "string" ? response.body : JSON.stringify(response.body);
390
+ if (!response.headers["content-type"]) {
391
+ res.setHeader(
392
+ "content-type",
393
+ typeof response.body === "string" ? "text/plain" : "application/json"
394
+ );
395
+ }
396
+ res.end(body);
397
+ } else {
398
+ res.end();
399
+ }
400
+ }
401
+ };
402
+ function createServer() {
403
+ return new Server();
404
+ }
405
+
406
+ exports.Router = Router;
407
+ exports.Server = Server;
408
+ exports.createRouter = createRouter;
409
+ exports.createServer = createServer;
410
+ //# sourceMappingURL=index.cjs.map
411
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/router.ts","../src/server.ts"],"names":["createNodeServer","parseQueryStringWithArrays"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HTTP 路由实现\n */\nimport type { Handler } from './types';\nimport type { HttpMethod } from '@lytjs/shared-types';\n\n// ===== Token Types =====\n\ninterface TokenStatic {\n type: 'static';\n value: string;\n}\n\ninterface TokenParam {\n type: 'param';\n name: string;\n repeatable: boolean;\n optional: boolean;\n}\n\ninterface TokenWildcard {\n type: 'wildcard';\n value: string;\n}\n\ntype PathToken = TokenStatic | TokenParam | TokenWildcard;\n\n// ===== Path Tokenizer =====\n\nconst PARAM_RE = /^:(\\w+)(\\??)?(\\.\\.\\.)?$/;\nconst WILDCARD_RE = /^\\*$/;\n\n/**\n * Tokenize a path segment string into tokens\n */\nfunction tokenizePath(path: string): PathToken[] {\n const segments = path.split('/');\n const tokens: PathToken[] = [];\n\n for (const segment of segments) {\n if (!segment) continue;\n\n const paramMatch = segment.match(PARAM_RE);\n if (paramMatch) {\n const [, name, optional, repeatable] = paramMatch;\n if (name) {\n tokens.push({\n type: 'param',\n name,\n repeatable: repeatable === '...',\n optional: optional === '?',\n });\n }\n continue;\n }\n\n if (WILDCARD_RE.test(segment)) {\n tokens.push({ type: 'wildcard', value: '*' });\n continue;\n }\n\n tokens.push({ type: 'static', value: segment });\n }\n\n return tokens;\n}\n\n// ===== Path Scoring =====\n\n/**\n * Score a route record for ranking (higher = more specific)\n */\nfunction scoreRoute(tokens: PathToken[]): number {\n let score = 0;\n for (const token of tokens) {\n switch (token.type) {\n case 'static':\n score += 3;\n break;\n case 'param':\n score += token.optional ? 1 : 2;\n break;\n case 'wildcard':\n score += 0;\n break;\n }\n }\n return score;\n}\n\n// ===== Path Matching =====\n\ninterface PathMatchResult {\n matched: boolean;\n params: Record<string, string | string[]>;\n path: string;\n score: number;\n}\n\n/**\n * Match a pathname against a tokenized route\n */\nfunction matchPath(\n pathname: string,\n tokens: PathToken[],\n strict: boolean = false,\n): PathMatchResult {\n const pathSegments = pathname.split('/').filter(Boolean);\n const params: Record<string, string | string[]> = {};\n let matched = true;\n let i = 0;\n\n for (const token of tokens) {\n if (i >= pathSegments.length) {\n if (token.type === 'param' && token.optional) continue;\n if (token.type === 'wildcard') continue;\n matched = false;\n break;\n }\n\n const segment = pathSegments[i];\n\n switch (token.type) {\n case 'static':\n if (segment !== token.value) {\n matched = false;\n }\n i++;\n break;\n\n case 'param':\n if (token.repeatable) {\n params[token.name] = pathSegments.slice(i);\n i = pathSegments.length;\n } else if (segment !== undefined) {\n params[token.name] = segment;\n i++;\n }\n break;\n\n case 'wildcard':\n params[token.value] = pathSegments.slice(i).join('/');\n i = pathSegments.length;\n break;\n }\n\n if (!matched) break;\n }\n\n // Check if all path segments were consumed\n if (matched && i < pathSegments.length) {\n matched = false;\n }\n\n // In non-strict mode, trailing slash is ok\n if (!strict && matched && pathSegments.length === 0 && tokens.length === 0) {\n matched = true;\n }\n\n return {\n matched,\n params,\n path: '/' + pathSegments.slice(0, i).join('/'),\n score: scoreRoute(tokens),\n };\n}\n\n/**\n * 路由类\n */\nexport class Router {\n /** 路由列表 */\n private routes: Array<{\n method: HttpMethod;\n path: string;\n tokens: ReturnType<typeof tokenizePath>;\n handler: Handler;\n }> = [];\n\n /**\n * 添加路由\n *\n * @param method - HTTP 方法\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n on(method: HttpMethod, path: string, handler: Handler): this {\n const tokens = tokenizePath(path);\n this.routes.push({ method, path, tokens, handler });\n return this;\n }\n\n /**\n * 添加 GET 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n get(path: string, handler: Handler): this {\n return this.on('GET', path, handler);\n }\n\n /**\n * 添加 POST 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n post(path: string, handler: Handler): this {\n return this.on('POST', path, handler);\n }\n\n /**\n * 添加 PUT 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n put(path: string, handler: Handler): this {\n return this.on('PUT', path, handler);\n }\n\n /**\n * 添加 PATCH 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n patch(path: string, handler: Handler): this {\n return this.on('PATCH', path, handler);\n }\n\n /**\n * 添加 DELETE 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n delete(path: string, handler: Handler): this {\n return this.on('DELETE', path, handler);\n }\n\n /**\n * 匹配路由\n *\n * @param method - HTTP 方法\n * @param path - 路径\n * @returns 匹配结果或 null\n */\n match(\n method: HttpMethod,\n path: string,\n ): { handler: Handler; params: Record<string, string | string[]> } | null {\n const candidates = this.routes.filter((r) => r.method === method);\n\n let bestMatch: { handler: Handler; params: Record<string, string | string[]> } | null = null;\n let bestScore = -1;\n\n for (const route of candidates) {\n const result = matchPath(path, route.tokens);\n if (result.matched && result.score > bestScore) {\n bestScore = result.score;\n bestMatch = { handler: route.handler, params: result.params };\n }\n }\n\n return bestMatch;\n }\n}\n\n/**\n * 创建路由\n *\n * @returns 路由实例\n */\nexport function createRouter(): Router {\n return new Router();\n}\n","/**\n * HTTP 服务器实现\n */\nimport type { Server as NodeServer, IncomingMessage, ServerResponse } from 'node:http';\nimport { createServer as createNodeServer } from 'node:http';\nimport type { Handler } from './types';\nimport type {\n HttpMethod,\n HttpContext as Context,\n HttpRequest as Request,\n HttpResponse as Response,\n} from '@lytjs/shared-types';\nimport { Router } from './router';\nimport { parseQueryStringWithArrays } from '@lytjs/common-query';\n\n/**\n * HTTP 服务器类\n */\nexport class Server {\n /** 路由实例 */\n private router: Router;\n /** 中间件列表 */\n private middlewares: ((ctx: Context, next: () => Promise<void>) => Promise<void>)[] = [];\n /** Node.js 服务器实例 */\n private server?: NodeServer;\n\n /**\n * 构造函数\n */\n constructor() {\n this.router = new Router();\n }\n\n /**\n * 添加中间件\n *\n * @param middleware - 中间件函数\n * @returns 服务器实例\n */\n use(middleware: (ctx: Context, next: () => Promise<void>) => Promise<void>): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n /**\n * 添加路由\n *\n * @param method - HTTP 方法\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n on(method: HttpMethod, path: string, handler: Handler): this {\n this.router.on(method, path, handler);\n return this;\n }\n\n /**\n * 添加 GET 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n get(path: string, handler: Handler): this {\n return this.on('GET', path, handler);\n }\n\n /**\n * 添加 POST 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n post(path: string, handler: Handler): this {\n return this.on('POST', path, handler);\n }\n\n /**\n * 添加 PUT 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n put(path: string, handler: Handler): this {\n return this.on('PUT', path, handler);\n }\n\n /**\n * 添加 PATCH 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n patch(path: string, handler: Handler): this {\n return this.on('PATCH', path, handler);\n }\n\n /**\n * 添加 DELETE 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n delete(path: string, handler: Handler): this {\n return this.on('DELETE', path, handler);\n }\n\n /**\n * 启动服务器监听\n *\n * @param port - 端口\n * @param hostname - 主机名\n * @returns Promise\n */\n listen(port: number, hostname?: string): Promise<void> {\n return new Promise((resolve) => {\n this.server = createNodeServer(this.handleRequest.bind(this));\n this.server.listen(port, hostname, () => {\n resolve();\n });\n });\n }\n\n /**\n * 关闭服务器\n *\n * @returns Promise\n */\n close(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n this.server.close((err) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * 处理请求\n *\n * @param req - Node.js 请求对象\n * @param res - Node.js 响应对象\n */\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = req.url || '/';\n const path = url.split('?')[0] || '/';\n const method = (req.method || 'GET') as HttpMethod;\n\n const request: Request = {\n method,\n url,\n path,\n headers: req.headers,\n query: parseQueryStringWithArrays(url),\n params: {},\n ip: req.socket.remoteAddress,\n };\n\n const response: Response = {\n status: 200,\n headers: {},\n };\n\n const ctx: Context = {\n request,\n response,\n };\n\n const match = this.router.match(method, path);\n\n if (match) {\n // 转换类型,确保兼容性\n ctx.request.params = {};\n for (const [key, value] of Object.entries(match.params)) {\n if (Array.isArray(value)) {\n // 如果是数组,只取第一个元素\n ctx.request.params[key] = value[0] || '';\n } else {\n ctx.request.params[key] = value;\n }\n }\n\n const handler = async () => {\n await match.handler(ctx);\n };\n\n let index = this.middlewares.length;\n const next = async () => {\n index--;\n if (index >= 0 && this.middlewares[index]) {\n await this.middlewares[index](ctx, next);\n } else {\n await handler();\n }\n };\n\n await next();\n } else {\n if (ctx.response) {\n ctx.response.status = 404;\n ctx.response.body = { error: '未找到' };\n }\n }\n\n if (ctx.response) {\n this.sendResponse(res, ctx.response);\n }\n }\n\n /**\n * 发送响应\n *\n * @param res - Node.js 响应对象\n * @param response - 响应对象\n */\n private sendResponse(res: ServerResponse, response: Response): void {\n res.statusCode = response.status;\n\n for (const [key, value] of Object.entries(response.headers)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n res.setHeader(key, v);\n }\n } else {\n res.setHeader(key, value);\n }\n }\n\n if (response.body !== undefined) {\n const body =\n typeof response.body === 'string' ? response.body : JSON.stringify(response.body);\n\n if (!response.headers['content-type']) {\n res.setHeader(\n 'content-type',\n typeof response.body === 'string' ? 'text/plain' : 'application/json',\n );\n }\n\n res.end(body);\n } else {\n res.end();\n }\n }\n}\n\n/**\n * 创建 HTTP 服务器\n *\n * @returns 服务器实例\n */\nexport function createServer(): Server {\n return new Server();\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,406 @@
1
+ import { createServer as createServer$1 } from 'http';
2
+ import { parseQueryStringWithArrays } from '@lytjs/common-query';
3
+
4
+ // src/server.ts
5
+
6
+ // src/router.ts
7
+ var PARAM_RE = /^:(\w+)(\??)?(\.\.\.)?$/;
8
+ var WILDCARD_RE = /^\*$/;
9
+ function tokenizePath(path) {
10
+ const segments = path.split("/");
11
+ const tokens = [];
12
+ for (const segment of segments) {
13
+ if (!segment) continue;
14
+ const paramMatch = segment.match(PARAM_RE);
15
+ if (paramMatch) {
16
+ const [, name, optional, repeatable] = paramMatch;
17
+ if (name) {
18
+ tokens.push({
19
+ type: "param",
20
+ name,
21
+ repeatable: repeatable === "...",
22
+ optional: optional === "?"
23
+ });
24
+ }
25
+ continue;
26
+ }
27
+ if (WILDCARD_RE.test(segment)) {
28
+ tokens.push({ type: "wildcard", value: "*" });
29
+ continue;
30
+ }
31
+ tokens.push({ type: "static", value: segment });
32
+ }
33
+ return tokens;
34
+ }
35
+ function scoreRoute(tokens) {
36
+ let score = 0;
37
+ for (const token of tokens) {
38
+ switch (token.type) {
39
+ case "static":
40
+ score += 3;
41
+ break;
42
+ case "param":
43
+ score += token.optional ? 1 : 2;
44
+ break;
45
+ case "wildcard":
46
+ score += 0;
47
+ break;
48
+ }
49
+ }
50
+ return score;
51
+ }
52
+ function matchPath(pathname, tokens, strict = false) {
53
+ const pathSegments = pathname.split("/").filter(Boolean);
54
+ const params = {};
55
+ let matched = true;
56
+ let i = 0;
57
+ for (const token of tokens) {
58
+ if (i >= pathSegments.length) {
59
+ if (token.type === "param" && token.optional) continue;
60
+ if (token.type === "wildcard") continue;
61
+ matched = false;
62
+ break;
63
+ }
64
+ const segment = pathSegments[i];
65
+ switch (token.type) {
66
+ case "static":
67
+ if (segment !== token.value) {
68
+ matched = false;
69
+ }
70
+ i++;
71
+ break;
72
+ case "param":
73
+ if (token.repeatable) {
74
+ params[token.name] = pathSegments.slice(i);
75
+ i = pathSegments.length;
76
+ } else if (segment !== void 0) {
77
+ params[token.name] = segment;
78
+ i++;
79
+ }
80
+ break;
81
+ case "wildcard":
82
+ params[token.value] = pathSegments.slice(i).join("/");
83
+ i = pathSegments.length;
84
+ break;
85
+ }
86
+ if (!matched) break;
87
+ }
88
+ if (matched && i < pathSegments.length) {
89
+ matched = false;
90
+ }
91
+ if (!strict && matched && pathSegments.length === 0 && tokens.length === 0) {
92
+ matched = true;
93
+ }
94
+ return {
95
+ matched,
96
+ params,
97
+ path: "/" + pathSegments.slice(0, i).join("/"),
98
+ score: scoreRoute(tokens)
99
+ };
100
+ }
101
+ var Router = class {
102
+ constructor() {
103
+ /** 路由列表 */
104
+ this.routes = [];
105
+ }
106
+ /**
107
+ * 添加路由
108
+ *
109
+ * @param method - HTTP 方法
110
+ * @param path - 路径
111
+ * @param handler - 处理器
112
+ * @returns 路由实例
113
+ */
114
+ on(method, path, handler) {
115
+ const tokens = tokenizePath(path);
116
+ this.routes.push({ method, path, tokens, handler });
117
+ return this;
118
+ }
119
+ /**
120
+ * 添加 GET 路由
121
+ *
122
+ * @param path - 路径
123
+ * @param handler - 处理器
124
+ * @returns 路由实例
125
+ */
126
+ get(path, handler) {
127
+ return this.on("GET", path, handler);
128
+ }
129
+ /**
130
+ * 添加 POST 路由
131
+ *
132
+ * @param path - 路径
133
+ * @param handler - 处理器
134
+ * @returns 路由实例
135
+ */
136
+ post(path, handler) {
137
+ return this.on("POST", path, handler);
138
+ }
139
+ /**
140
+ * 添加 PUT 路由
141
+ *
142
+ * @param path - 路径
143
+ * @param handler - 处理器
144
+ * @returns 路由实例
145
+ */
146
+ put(path, handler) {
147
+ return this.on("PUT", path, handler);
148
+ }
149
+ /**
150
+ * 添加 PATCH 路由
151
+ *
152
+ * @param path - 路径
153
+ * @param handler - 处理器
154
+ * @returns 路由实例
155
+ */
156
+ patch(path, handler) {
157
+ return this.on("PATCH", path, handler);
158
+ }
159
+ /**
160
+ * 添加 DELETE 路由
161
+ *
162
+ * @param path - 路径
163
+ * @param handler - 处理器
164
+ * @returns 路由实例
165
+ */
166
+ delete(path, handler) {
167
+ return this.on("DELETE", path, handler);
168
+ }
169
+ /**
170
+ * 匹配路由
171
+ *
172
+ * @param method - HTTP 方法
173
+ * @param path - 路径
174
+ * @returns 匹配结果或 null
175
+ */
176
+ match(method, path) {
177
+ const candidates = this.routes.filter((r) => r.method === method);
178
+ let bestMatch = null;
179
+ let bestScore = -1;
180
+ for (const route of candidates) {
181
+ const result = matchPath(path, route.tokens);
182
+ if (result.matched && result.score > bestScore) {
183
+ bestScore = result.score;
184
+ bestMatch = { handler: route.handler, params: result.params };
185
+ }
186
+ }
187
+ return bestMatch;
188
+ }
189
+ };
190
+ function createRouter() {
191
+ return new Router();
192
+ }
193
+ var Server = class {
194
+ /**
195
+ * 构造函数
196
+ */
197
+ constructor() {
198
+ /** 中间件列表 */
199
+ this.middlewares = [];
200
+ this.router = new Router();
201
+ }
202
+ /**
203
+ * 添加中间件
204
+ *
205
+ * @param middleware - 中间件函数
206
+ * @returns 服务器实例
207
+ */
208
+ use(middleware) {
209
+ this.middlewares.push(middleware);
210
+ return this;
211
+ }
212
+ /**
213
+ * 添加路由
214
+ *
215
+ * @param method - HTTP 方法
216
+ * @param path - 路径
217
+ * @param handler - 处理器
218
+ * @returns 服务器实例
219
+ */
220
+ on(method, path, handler) {
221
+ this.router.on(method, path, handler);
222
+ return this;
223
+ }
224
+ /**
225
+ * 添加 GET 路由
226
+ *
227
+ * @param path - 路径
228
+ * @param handler - 处理器
229
+ * @returns 服务器实例
230
+ */
231
+ get(path, handler) {
232
+ return this.on("GET", path, handler);
233
+ }
234
+ /**
235
+ * 添加 POST 路由
236
+ *
237
+ * @param path - 路径
238
+ * @param handler - 处理器
239
+ * @returns 服务器实例
240
+ */
241
+ post(path, handler) {
242
+ return this.on("POST", path, handler);
243
+ }
244
+ /**
245
+ * 添加 PUT 路由
246
+ *
247
+ * @param path - 路径
248
+ * @param handler - 处理器
249
+ * @returns 服务器实例
250
+ */
251
+ put(path, handler) {
252
+ return this.on("PUT", path, handler);
253
+ }
254
+ /**
255
+ * 添加 PATCH 路由
256
+ *
257
+ * @param path - 路径
258
+ * @param handler - 处理器
259
+ * @returns 服务器实例
260
+ */
261
+ patch(path, handler) {
262
+ return this.on("PATCH", path, handler);
263
+ }
264
+ /**
265
+ * 添加 DELETE 路由
266
+ *
267
+ * @param path - 路径
268
+ * @param handler - 处理器
269
+ * @returns 服务器实例
270
+ */
271
+ delete(path, handler) {
272
+ return this.on("DELETE", path, handler);
273
+ }
274
+ /**
275
+ * 启动服务器监听
276
+ *
277
+ * @param port - 端口
278
+ * @param hostname - 主机名
279
+ * @returns Promise
280
+ */
281
+ listen(port, hostname) {
282
+ return new Promise((resolve) => {
283
+ this.server = createServer$1(this.handleRequest.bind(this));
284
+ this.server.listen(port, hostname, () => {
285
+ resolve();
286
+ });
287
+ });
288
+ }
289
+ /**
290
+ * 关闭服务器
291
+ *
292
+ * @returns Promise
293
+ */
294
+ close() {
295
+ return new Promise((resolve, reject) => {
296
+ if (this.server) {
297
+ this.server.close((err) => {
298
+ if (err) {
299
+ reject(err);
300
+ } else {
301
+ resolve();
302
+ }
303
+ });
304
+ } else {
305
+ resolve();
306
+ }
307
+ });
308
+ }
309
+ /**
310
+ * 处理请求
311
+ *
312
+ * @param req - Node.js 请求对象
313
+ * @param res - Node.js 响应对象
314
+ */
315
+ async handleRequest(req, res) {
316
+ const url = req.url || "/";
317
+ const path = url.split("?")[0] || "/";
318
+ const method = req.method || "GET";
319
+ const request = {
320
+ method,
321
+ url,
322
+ path,
323
+ headers: req.headers,
324
+ query: parseQueryStringWithArrays(url),
325
+ params: {},
326
+ ip: req.socket.remoteAddress
327
+ };
328
+ const response = {
329
+ status: 200,
330
+ headers: {}
331
+ };
332
+ const ctx = {
333
+ request,
334
+ response
335
+ };
336
+ const match = this.router.match(method, path);
337
+ if (match) {
338
+ ctx.request.params = {};
339
+ for (const [key, value] of Object.entries(match.params)) {
340
+ if (Array.isArray(value)) {
341
+ ctx.request.params[key] = value[0] || "";
342
+ } else {
343
+ ctx.request.params[key] = value;
344
+ }
345
+ }
346
+ const handler = async () => {
347
+ await match.handler(ctx);
348
+ };
349
+ let index = this.middlewares.length;
350
+ const next = async () => {
351
+ index--;
352
+ if (index >= 0 && this.middlewares[index]) {
353
+ await this.middlewares[index](ctx, next);
354
+ } else {
355
+ await handler();
356
+ }
357
+ };
358
+ await next();
359
+ } else {
360
+ if (ctx.response) {
361
+ ctx.response.status = 404;
362
+ ctx.response.body = { error: "\u672A\u627E\u5230" };
363
+ }
364
+ }
365
+ if (ctx.response) {
366
+ this.sendResponse(res, ctx.response);
367
+ }
368
+ }
369
+ /**
370
+ * 发送响应
371
+ *
372
+ * @param res - Node.js 响应对象
373
+ * @param response - 响应对象
374
+ */
375
+ sendResponse(res, response) {
376
+ res.statusCode = response.status;
377
+ for (const [key, value] of Object.entries(response.headers)) {
378
+ if (Array.isArray(value)) {
379
+ for (const v of value) {
380
+ res.setHeader(key, v);
381
+ }
382
+ } else {
383
+ res.setHeader(key, value);
384
+ }
385
+ }
386
+ if (response.body !== void 0) {
387
+ const body = typeof response.body === "string" ? response.body : JSON.stringify(response.body);
388
+ if (!response.headers["content-type"]) {
389
+ res.setHeader(
390
+ "content-type",
391
+ typeof response.body === "string" ? "text/plain" : "application/json"
392
+ );
393
+ }
394
+ res.end(body);
395
+ } else {
396
+ res.end();
397
+ }
398
+ }
399
+ };
400
+ function createServer() {
401
+ return new Server();
402
+ }
403
+
404
+ export { Router, Server, createRouter, createServer };
405
+ //# sourceMappingURL=index.mjs.map
406
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/router.ts","../src/server.ts"],"names":["createNodeServer"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * HTTP 路由实现\n */\nimport type { Handler } from './types';\nimport type { HttpMethod } from '@lytjs/shared-types';\n\n// ===== Token Types =====\n\ninterface TokenStatic {\n type: 'static';\n value: string;\n}\n\ninterface TokenParam {\n type: 'param';\n name: string;\n repeatable: boolean;\n optional: boolean;\n}\n\ninterface TokenWildcard {\n type: 'wildcard';\n value: string;\n}\n\ntype PathToken = TokenStatic | TokenParam | TokenWildcard;\n\n// ===== Path Tokenizer =====\n\nconst PARAM_RE = /^:(\\w+)(\\??)?(\\.\\.\\.)?$/;\nconst WILDCARD_RE = /^\\*$/;\n\n/**\n * Tokenize a path segment string into tokens\n */\nfunction tokenizePath(path: string): PathToken[] {\n const segments = path.split('/');\n const tokens: PathToken[] = [];\n\n for (const segment of segments) {\n if (!segment) continue;\n\n const paramMatch = segment.match(PARAM_RE);\n if (paramMatch) {\n const [, name, optional, repeatable] = paramMatch;\n if (name) {\n tokens.push({\n type: 'param',\n name,\n repeatable: repeatable === '...',\n optional: optional === '?',\n });\n }\n continue;\n }\n\n if (WILDCARD_RE.test(segment)) {\n tokens.push({ type: 'wildcard', value: '*' });\n continue;\n }\n\n tokens.push({ type: 'static', value: segment });\n }\n\n return tokens;\n}\n\n// ===== Path Scoring =====\n\n/**\n * Score a route record for ranking (higher = more specific)\n */\nfunction scoreRoute(tokens: PathToken[]): number {\n let score = 0;\n for (const token of tokens) {\n switch (token.type) {\n case 'static':\n score += 3;\n break;\n case 'param':\n score += token.optional ? 1 : 2;\n break;\n case 'wildcard':\n score += 0;\n break;\n }\n }\n return score;\n}\n\n// ===== Path Matching =====\n\ninterface PathMatchResult {\n matched: boolean;\n params: Record<string, string | string[]>;\n path: string;\n score: number;\n}\n\n/**\n * Match a pathname against a tokenized route\n */\nfunction matchPath(\n pathname: string,\n tokens: PathToken[],\n strict: boolean = false,\n): PathMatchResult {\n const pathSegments = pathname.split('/').filter(Boolean);\n const params: Record<string, string | string[]> = {};\n let matched = true;\n let i = 0;\n\n for (const token of tokens) {\n if (i >= pathSegments.length) {\n if (token.type === 'param' && token.optional) continue;\n if (token.type === 'wildcard') continue;\n matched = false;\n break;\n }\n\n const segment = pathSegments[i];\n\n switch (token.type) {\n case 'static':\n if (segment !== token.value) {\n matched = false;\n }\n i++;\n break;\n\n case 'param':\n if (token.repeatable) {\n params[token.name] = pathSegments.slice(i);\n i = pathSegments.length;\n } else if (segment !== undefined) {\n params[token.name] = segment;\n i++;\n }\n break;\n\n case 'wildcard':\n params[token.value] = pathSegments.slice(i).join('/');\n i = pathSegments.length;\n break;\n }\n\n if (!matched) break;\n }\n\n // Check if all path segments were consumed\n if (matched && i < pathSegments.length) {\n matched = false;\n }\n\n // In non-strict mode, trailing slash is ok\n if (!strict && matched && pathSegments.length === 0 && tokens.length === 0) {\n matched = true;\n }\n\n return {\n matched,\n params,\n path: '/' + pathSegments.slice(0, i).join('/'),\n score: scoreRoute(tokens),\n };\n}\n\n/**\n * 路由类\n */\nexport class Router {\n /** 路由列表 */\n private routes: Array<{\n method: HttpMethod;\n path: string;\n tokens: ReturnType<typeof tokenizePath>;\n handler: Handler;\n }> = [];\n\n /**\n * 添加路由\n *\n * @param method - HTTP 方法\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n on(method: HttpMethod, path: string, handler: Handler): this {\n const tokens = tokenizePath(path);\n this.routes.push({ method, path, tokens, handler });\n return this;\n }\n\n /**\n * 添加 GET 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n get(path: string, handler: Handler): this {\n return this.on('GET', path, handler);\n }\n\n /**\n * 添加 POST 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n post(path: string, handler: Handler): this {\n return this.on('POST', path, handler);\n }\n\n /**\n * 添加 PUT 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n put(path: string, handler: Handler): this {\n return this.on('PUT', path, handler);\n }\n\n /**\n * 添加 PATCH 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n patch(path: string, handler: Handler): this {\n return this.on('PATCH', path, handler);\n }\n\n /**\n * 添加 DELETE 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 路由实例\n */\n delete(path: string, handler: Handler): this {\n return this.on('DELETE', path, handler);\n }\n\n /**\n * 匹配路由\n *\n * @param method - HTTP 方法\n * @param path - 路径\n * @returns 匹配结果或 null\n */\n match(\n method: HttpMethod,\n path: string,\n ): { handler: Handler; params: Record<string, string | string[]> } | null {\n const candidates = this.routes.filter((r) => r.method === method);\n\n let bestMatch: { handler: Handler; params: Record<string, string | string[]> } | null = null;\n let bestScore = -1;\n\n for (const route of candidates) {\n const result = matchPath(path, route.tokens);\n if (result.matched && result.score > bestScore) {\n bestScore = result.score;\n bestMatch = { handler: route.handler, params: result.params };\n }\n }\n\n return bestMatch;\n }\n}\n\n/**\n * 创建路由\n *\n * @returns 路由实例\n */\nexport function createRouter(): Router {\n return new Router();\n}\n","/**\n * HTTP 服务器实现\n */\nimport type { Server as NodeServer, IncomingMessage, ServerResponse } from 'node:http';\nimport { createServer as createNodeServer } from 'node:http';\nimport type { Handler } from './types';\nimport type {\n HttpMethod,\n HttpContext as Context,\n HttpRequest as Request,\n HttpResponse as Response,\n} from '@lytjs/shared-types';\nimport { Router } from './router';\nimport { parseQueryStringWithArrays } from '@lytjs/common-query';\n\n/**\n * HTTP 服务器类\n */\nexport class Server {\n /** 路由实例 */\n private router: Router;\n /** 中间件列表 */\n private middlewares: ((ctx: Context, next: () => Promise<void>) => Promise<void>)[] = [];\n /** Node.js 服务器实例 */\n private server?: NodeServer;\n\n /**\n * 构造函数\n */\n constructor() {\n this.router = new Router();\n }\n\n /**\n * 添加中间件\n *\n * @param middleware - 中间件函数\n * @returns 服务器实例\n */\n use(middleware: (ctx: Context, next: () => Promise<void>) => Promise<void>): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n /**\n * 添加路由\n *\n * @param method - HTTP 方法\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n on(method: HttpMethod, path: string, handler: Handler): this {\n this.router.on(method, path, handler);\n return this;\n }\n\n /**\n * 添加 GET 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n get(path: string, handler: Handler): this {\n return this.on('GET', path, handler);\n }\n\n /**\n * 添加 POST 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n post(path: string, handler: Handler): this {\n return this.on('POST', path, handler);\n }\n\n /**\n * 添加 PUT 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n put(path: string, handler: Handler): this {\n return this.on('PUT', path, handler);\n }\n\n /**\n * 添加 PATCH 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n patch(path: string, handler: Handler): this {\n return this.on('PATCH', path, handler);\n }\n\n /**\n * 添加 DELETE 路由\n *\n * @param path - 路径\n * @param handler - 处理器\n * @returns 服务器实例\n */\n delete(path: string, handler: Handler): this {\n return this.on('DELETE', path, handler);\n }\n\n /**\n * 启动服务器监听\n *\n * @param port - 端口\n * @param hostname - 主机名\n * @returns Promise\n */\n listen(port: number, hostname?: string): Promise<void> {\n return new Promise((resolve) => {\n this.server = createNodeServer(this.handleRequest.bind(this));\n this.server.listen(port, hostname, () => {\n resolve();\n });\n });\n }\n\n /**\n * 关闭服务器\n *\n * @returns Promise\n */\n close(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.server) {\n this.server.close((err) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * 处理请求\n *\n * @param req - Node.js 请求对象\n * @param res - Node.js 响应对象\n */\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = req.url || '/';\n const path = url.split('?')[0] || '/';\n const method = (req.method || 'GET') as HttpMethod;\n\n const request: Request = {\n method,\n url,\n path,\n headers: req.headers,\n query: parseQueryStringWithArrays(url),\n params: {},\n ip: req.socket.remoteAddress,\n };\n\n const response: Response = {\n status: 200,\n headers: {},\n };\n\n const ctx: Context = {\n request,\n response,\n };\n\n const match = this.router.match(method, path);\n\n if (match) {\n // 转换类型,确保兼容性\n ctx.request.params = {};\n for (const [key, value] of Object.entries(match.params)) {\n if (Array.isArray(value)) {\n // 如果是数组,只取第一个元素\n ctx.request.params[key] = value[0] || '';\n } else {\n ctx.request.params[key] = value;\n }\n }\n\n const handler = async () => {\n await match.handler(ctx);\n };\n\n let index = this.middlewares.length;\n const next = async () => {\n index--;\n if (index >= 0 && this.middlewares[index]) {\n await this.middlewares[index](ctx, next);\n } else {\n await handler();\n }\n };\n\n await next();\n } else {\n if (ctx.response) {\n ctx.response.status = 404;\n ctx.response.body = { error: '未找到' };\n }\n }\n\n if (ctx.response) {\n this.sendResponse(res, ctx.response);\n }\n }\n\n /**\n * 发送响应\n *\n * @param res - Node.js 响应对象\n * @param response - 响应对象\n */\n private sendResponse(res: ServerResponse, response: Response): void {\n res.statusCode = response.status;\n\n for (const [key, value] of Object.entries(response.headers)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n res.setHeader(key, v);\n }\n } else {\n res.setHeader(key, value);\n }\n }\n\n if (response.body !== undefined) {\n const body =\n typeof response.body === 'string' ? response.body : JSON.stringify(response.body);\n\n if (!response.headers['content-type']) {\n res.setHeader(\n 'content-type',\n typeof response.body === 'string' ? 'text/plain' : 'application/json',\n );\n }\n\n res.end(body);\n } else {\n res.end();\n }\n }\n}\n\n/**\n * 创建 HTTP 服务器\n *\n * @returns 服务器实例\n */\nexport function createServer(): Server {\n return new Server();\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@lytjs/http-server",
3
+ "version": "6.6.0",
4
+ "description": "LytJS HTTP 服务器",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "sideEffects": false,
23
+ "scripts": {
24
+ "dev": "tsup --watch",
25
+ "build": "tsup",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "test:coverage": "vitest run --coverage",
29
+ "type-check": "tsc --noEmit",
30
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
31
+ "clean": "rm -rf dist node_modules .turbo"
32
+ },
33
+ "dependencies": {
34
+ "@lytjs/common-is": "workspace:*",
35
+ "@lytjs/common-query": "workspace:*",
36
+ "@lytjs/middleware": "workspace:*",
37
+ "@lytjs/shared-types": "workspace:*"
38
+ },
39
+ "devDependencies": {
40
+ "tsup": "^8.3.6",
41
+ "typescript": "^5.7.3",
42
+ "vitest": "^3.0.0"
43
+ },
44
+ "peerDependencies": {},
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "registry": "https://registry.npmjs.org/"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://gitee.com/lytjs/lytjs.git",
52
+ "directory": "packages/ecosystem/packages/web-framework/packages/http-server"
53
+ },
54
+ "keywords": [
55
+ "lytjs",
56
+ "http",
57
+ "server",
58
+ "router"
59
+ ],
60
+ "author": "LytJS Team",
61
+ "license": "MIT"
62
+ }