@kevisual/router 0.0.42 → 0.0.43

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/router.d.ts CHANGED
@@ -390,7 +390,7 @@ type Listener$1 = {
390
390
  fun: (...args: any[]) => Promise<void> | void;
391
391
  };
392
392
  type ListenerFun = (...args: any[]) => Promise<void> | void;
393
- type OnListener = Listener$1 | Listener$1[] | ListenerFun | ListenerFun[];
393
+ type OnListener = Listener$1 | ListenerFun | (Listener$1 | ListenerFun)[];
394
394
  type Cors$2 = {
395
395
  /**
396
396
  * @default '*''
@@ -444,6 +444,29 @@ type WS = {
444
444
  send: (data: any) => void;
445
445
  close: () => void;
446
446
  };
447
+ type RouterReq<T = {}> = {
448
+ url: string;
449
+ method: string;
450
+ headers: Record<string, string>;
451
+ socket?: {
452
+ remoteAddress?: string;
453
+ remotePort?: number;
454
+ };
455
+ cookies?: Record<string, string>;
456
+ } & T;
457
+ type RouterRes<T = {}> = {
458
+ statusCode: number;
459
+ headersSent: boolean;
460
+ _headers: Record<string, string | string[]>;
461
+ _bodyChunks: any[];
462
+ writableEnded: boolean;
463
+ writeHead: (statusCode: number, headers?: Record<string, string>) => void;
464
+ setHeader: (name: string, value: string | string[]) => void;
465
+ cookie: (name: string, value: string, options?: any) => void;
466
+ write: (chunk: any) => void;
467
+ pipe: (stream: any) => void;
468
+ end: (data?: any) => void;
469
+ } & T;
447
470
 
448
471
  interface StringifyOptions {
449
472
  /**
@@ -889,4 +912,4 @@ declare class App<U = {}> {
889
912
  }
890
913
 
891
914
  export { App, Connect, CustomError, Mini, QueryConnect, QueryRouter, QueryRouterServer, QueryUtil, Route, ServerNode, createSchema, define, handleServer, util };
892
- export type { RouteArray, RouteContext, RouteMiddleware, RouteObject, RouteOpts, Rule, Run, Schema };
915
+ export type { RouteArray, RouteContext, RouteMiddleware, RouteObject, RouteOpts, RouterReq, RouterRes, Rule, Run, Schema };
package/dist/router.js CHANGED
@@ -6498,6 +6498,7 @@ class BunServer extends ServerBase {
6498
6498
  idleTimeout: 0, // 4 minutes idle timeout (max 255 seconds)
6499
6499
  fetch: async (request, server) => {
6500
6500
  const host = request.headers.get('host') || 'localhost';
6501
+ const clientInfo = server.requestIP(request); // 返回 { address: string, port: number } 或 null
6501
6502
  const url = new URL(request.url, `http://${host}`);
6502
6503
  // 处理 WebSocket 升级请求
6503
6504
  if (request.headers.get('upgrade') === 'websocket') {
@@ -6520,12 +6521,18 @@ class BunServer extends ServerBase {
6520
6521
  url: url.pathname + url.search,
6521
6522
  method: request.method,
6522
6523
  headers: Object.fromEntries(request.headers.entries()),
6524
+ socket: {
6525
+ // @ts-ignore
6526
+ remoteAddress: request?.remoteAddress || request?.ip || clientInfo?.address || '',
6527
+ remotePort: clientInfo?.port || 0,
6528
+ }
6523
6529
  };
6524
6530
  const res = {
6525
6531
  statusCode: 200,
6526
6532
  headersSent: false,
6527
6533
  writableEnded: false,
6528
6534
  _headers: {},
6535
+ _bodyChunks: [],
6529
6536
  writeHead(statusCode, headers) {
6530
6537
  this.statusCode = statusCode;
6531
6538
  for (const key in headers) {
@@ -6563,9 +6570,79 @@ class BunServer extends ServerBase {
6563
6570
  }
6564
6571
  this.setHeader('Set-Cookie', cookieString);
6565
6572
  },
6573
+ write(chunk, encoding, callback) {
6574
+ if (typeof encoding === 'function') {
6575
+ callback = encoding;
6576
+ encoding = 'utf8';
6577
+ }
6578
+ if (!this._bodyChunks) {
6579
+ this._bodyChunks = [];
6580
+ }
6581
+ this._bodyChunks.push(chunk);
6582
+ if (callback)
6583
+ callback();
6584
+ return true;
6585
+ },
6586
+ pipe(stream) {
6587
+ this.writableEnded = true;
6588
+ // 如果是 ReadableStream,直接使用
6589
+ if (stream instanceof ReadableStream) {
6590
+ resolve(new Response(stream, {
6591
+ status: this.statusCode,
6592
+ headers: this._headers,
6593
+ }));
6594
+ return;
6595
+ }
6596
+ // 如果是 Node.js 流,转换为 ReadableStream
6597
+ const readableStream = new ReadableStream({
6598
+ start(controller) {
6599
+ stream.on('data', (chunk) => {
6600
+ controller.enqueue(chunk);
6601
+ });
6602
+ stream.on('end', () => {
6603
+ controller.close();
6604
+ });
6605
+ stream.on('error', (err) => {
6606
+ controller.error(err);
6607
+ });
6608
+ },
6609
+ cancel() {
6610
+ if (stream.destroy) {
6611
+ stream.destroy();
6612
+ }
6613
+ }
6614
+ });
6615
+ resolve(new Response(readableStream, {
6616
+ status: this.statusCode,
6617
+ headers: this._headers,
6618
+ }));
6619
+ },
6566
6620
  end(data) {
6567
6621
  this.writableEnded = true;
6568
- resolve(new Response(data, {
6622
+ // 合并所有写入的数据块
6623
+ let responseData = data;
6624
+ if (this._bodyChunks && this._bodyChunks.length > 0) {
6625
+ if (data)
6626
+ this._bodyChunks.push(data);
6627
+ // 处理 Buffer 和字符串混合的情况
6628
+ const hasBuffer = this._bodyChunks.some(chunk => chunk instanceof Buffer || chunk instanceof Uint8Array);
6629
+ if (hasBuffer) {
6630
+ // 如果有 Buffer,转换所有内容为 Buffer 后合并
6631
+ const buffers = this._bodyChunks.map(chunk => {
6632
+ if (chunk instanceof Buffer)
6633
+ return chunk;
6634
+ if (chunk instanceof Uint8Array)
6635
+ return Buffer.from(chunk);
6636
+ return Buffer.from(String(chunk));
6637
+ });
6638
+ responseData = Buffer.concat(buffers);
6639
+ }
6640
+ else {
6641
+ // 纯字符串,直接拼接
6642
+ responseData = this._bodyChunks.map(chunk => String(chunk)).join('');
6643
+ }
6644
+ }
6645
+ resolve(new Response(responseData, {
6569
6646
  status: this.statusCode,
6570
6647
  headers: this._headers,
6571
6648
  }));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@kevisual/router",
4
- "version": "0.0.42",
4
+ "version": "0.0.43",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "main": "./dist/router.js",
package/src/index.ts CHANGED
@@ -18,3 +18,6 @@ export type { Rule, Schema, } from './validator/index.ts';
18
18
  export { App } from './app.ts';
19
19
 
20
20
  export * from './router-define.ts';
21
+
22
+
23
+ export { RouterReq, RouterRes } from './server/server-type.ts';
@@ -4,19 +4,8 @@
4
4
  * @tags bun, server, websocket, http
5
5
  * @createdAt 2025-12-20
6
6
  */
7
- import type { IncomingMessage, ServerResponse } from 'node:http';
8
- import { ServerType, type ServerOpts, type Cors, Listener } from './server-type.ts';
9
- import { handleServer } from './handle-server.ts';
7
+ import { ServerType, type ServerOpts, type Cors, RouterRes, RouterReq } from './server-type.ts';
10
8
  import { ServerBase } from './server-base.ts';
11
- import { parseIfJson } from '../utils/parse.ts';
12
-
13
- const resultError = (error: string, code = 500) => {
14
- const r = {
15
- code: code,
16
- message: error,
17
- };
18
- return JSON.stringify(r);
19
- };
20
9
 
21
10
  export class BunServer extends ServerBase implements ServerType {
22
11
  declare _server: any;
@@ -54,15 +43,16 @@ export class BunServer extends ServerBase implements ServerType {
54
43
  }
55
44
  }
56
45
 
57
- const requestCallback = this.createCallback();
46
+ const requestCallback = this.createCallback() as unknown as (req: RouterReq, res: RouterRes) => void;
58
47
  const wsPath = this.path;
59
48
  // @ts-ignore
60
49
  this._server = Bun.serve({
61
50
  port,
62
51
  hostname,
63
52
  idleTimeout: 0, // 4 minutes idle timeout (max 255 seconds)
64
- fetch: async (request: Request, server: any) => {
53
+ fetch: async (request: Bun.BunRequest, server: any) => {
65
54
  const host = request.headers.get('host') || 'localhost';
55
+ const clientInfo = server.requestIP(request); // 返回 { address: string, port: number } 或 null
66
56
  const url = new URL(request.url, `http://${host}`);
67
57
  // 处理 WebSocket 升级请求
68
58
  if (request.headers.get('upgrade') === 'websocket') {
@@ -82,17 +72,23 @@ export class BunServer extends ServerBase implements ServerType {
82
72
 
83
73
  // 将 Bun 的 Request 转换为 Node.js 风格的 req/res
84
74
  return new Promise((resolve) => {
85
- const req: any = {
75
+ const req: RouterReq = {
86
76
  url: url.pathname + url.search,
87
77
  method: request.method,
88
78
  headers: Object.fromEntries(request.headers.entries()),
79
+ socket: {
80
+ // @ts-ignore
81
+ remoteAddress: request?.remoteAddress || request?.ip || clientInfo?.address || '',
82
+ remotePort: clientInfo?.port || 0,
83
+ }
89
84
  };
90
85
 
91
- const res: any = {
86
+ const res: RouterRes = {
92
87
  statusCode: 200,
93
88
  headersSent: false,
94
89
  writableEnded: false,
95
90
  _headers: {} as Record<string, string | string[]>,
91
+ _bodyChunks: [] as any[],
96
92
  writeHead(statusCode: number, headers: Record<string, string | string[]>) {
97
93
  this.statusCode = statusCode;
98
94
  for (const key in headers) {
@@ -130,10 +126,84 @@ export class BunServer extends ServerBase implements ServerType {
130
126
  }
131
127
  this.setHeader('Set-Cookie', cookieString);
132
128
  },
129
+ write(chunk: any, encoding?: string | Function, callback?: Function) {
130
+ if (typeof encoding === 'function') {
131
+ callback = encoding;
132
+ encoding = 'utf8';
133
+ }
134
+ if (!this._bodyChunks) {
135
+ this._bodyChunks = [];
136
+ }
137
+ this._bodyChunks.push(chunk);
138
+ if (callback) callback();
139
+ return true;
140
+ },
141
+ pipe(stream: any) {
142
+ this.writableEnded = true;
143
+
144
+ // 如果是 ReadableStream,直接使用
145
+ if (stream instanceof ReadableStream) {
146
+ resolve(
147
+ new Response(stream, {
148
+ status: this.statusCode,
149
+ headers: this._headers as any,
150
+ })
151
+ );
152
+ return;
153
+ }
154
+
155
+ // 如果是 Node.js 流,转换为 ReadableStream
156
+ const readableStream = new ReadableStream({
157
+ start(controller) {
158
+ stream.on('data', (chunk: any) => {
159
+ controller.enqueue(chunk);
160
+ });
161
+ stream.on('end', () => {
162
+ controller.close();
163
+ });
164
+ stream.on('error', (err: any) => {
165
+ controller.error(err);
166
+ });
167
+ },
168
+ cancel() {
169
+ if (stream.destroy) {
170
+ stream.destroy();
171
+ }
172
+ }
173
+ });
174
+
175
+ resolve(
176
+ new Response(readableStream, {
177
+ status: this.statusCode,
178
+ headers: this._headers as any,
179
+ })
180
+ );
181
+ },
133
182
  end(data?: string) {
134
183
  this.writableEnded = true;
184
+
185
+ // 合并所有写入的数据块
186
+ let responseData: string | Buffer = data;
187
+ if (this._bodyChunks && this._bodyChunks.length > 0) {
188
+ if (data) this._bodyChunks.push(data);
189
+ // 处理 Buffer 和字符串混合的情况
190
+ const hasBuffer = this._bodyChunks.some(chunk => chunk instanceof Buffer || chunk instanceof Uint8Array);
191
+ if (hasBuffer) {
192
+ // 如果有 Buffer,转换所有内容为 Buffer 后合并
193
+ const buffers = this._bodyChunks.map(chunk => {
194
+ if (chunk instanceof Buffer) return chunk;
195
+ if (chunk instanceof Uint8Array) return Buffer.from(chunk);
196
+ return Buffer.from(String(chunk));
197
+ });
198
+ responseData = Buffer.concat(buffers);
199
+ } else {
200
+ // 纯字符串,直接拼接
201
+ responseData = this._bodyChunks.map(chunk => String(chunk)).join('');
202
+ }
203
+ }
204
+
135
205
  resolve(
136
- new Response(data, {
206
+ new Response(responseData as any, {
137
207
  status: this.statusCode,
138
208
  headers: this._headers as any,
139
209
  })
@@ -7,7 +7,7 @@ export type Listener = {
7
7
  fun: (...args: any[]) => Promise<void> | void;
8
8
  }
9
9
  export type ListenerFun = (...args: any[]) => Promise<void> | void;
10
- export type OnListener = Listener | Listener[] | ListenerFun | ListenerFun[];
10
+ export type OnListener = Listener | ListenerFun | (Listener | ListenerFun)[];
11
11
  export type Cors = {
12
12
  /**
13
13
  * @default '*''
@@ -52,19 +52,27 @@ type WS = {
52
52
  close: () => void;
53
53
  }
54
54
 
55
- export type CommonReq = {
55
+ export type RouterReq<T = {}> = {
56
56
  url: string;
57
57
  method: string;
58
58
  headers: Record<string, string>;
59
- [key: string]: any;
60
- }
59
+ socket?: {
60
+ remoteAddress?: string;
61
+ remotePort?: number;
62
+ };
63
+ cookies?: Record<string, string>;
64
+ } & T;
61
65
 
62
- export type CommonRes = {
66
+ export type RouterRes<T = {}> = {
63
67
  statusCode: number;
68
+ headersSent: boolean;
69
+ _headers: Record<string, string | string[]>;
70
+ _bodyChunks: any[];
64
71
  writableEnded: boolean;
65
72
  writeHead: (statusCode: number, headers?: Record<string, string>) => void;
66
73
  setHeader: (name: string, value: string | string[]) => void;
67
74
  cookie: (name: string, value: string, options?: any) => void;
75
+ write: (chunk: any) => void;
76
+ pipe: (stream: any) => void;
68
77
  end: (data?: any) => void;
69
- [key: string]: any;
70
- }
78
+ } & T;