@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 +25 -2
- package/dist/router.js +78 -1
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/server/server-bun.ts +87 -17
- package/src/server/server-type.ts +15 -7
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
|
|
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
|
-
|
|
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
package/src/index.ts
CHANGED
package/src/server/server-bun.ts
CHANGED
|
@@ -4,19 +4,8 @@
|
|
|
4
4
|
* @tags bun, server, websocket, http
|
|
5
5
|
* @createdAt 2025-12-20
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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 |
|
|
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
|
|
55
|
+
export type RouterReq<T = {}> = {
|
|
56
56
|
url: string;
|
|
57
57
|
method: string;
|
|
58
58
|
headers: Record<string, string>;
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
socket?: {
|
|
60
|
+
remoteAddress?: string;
|
|
61
|
+
remotePort?: number;
|
|
62
|
+
};
|
|
63
|
+
cookies?: Record<string, string>;
|
|
64
|
+
} & T;
|
|
61
65
|
|
|
62
|
-
export type
|
|
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
|
-
|
|
70
|
-
}
|
|
78
|
+
} & T;
|