@kevisual/router 0.0.19 → 0.0.21-beta

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.
@@ -495,7 +495,8 @@ declare const util: {
495
495
  declare class QueryUtil<T extends RouteObject = RouteObject> {
496
496
  obj: T;
497
497
  app: QueryRouterServer$1;
498
- constructor(object: T, opts?: ChainOptions);
498
+ query: Query;
499
+ constructor(object: T, opts?: ChainOptions & QueryChainOptions);
499
500
  static createFormObj<U extends RouteObject>(object: U, opts?: ChainOptions): QueryUtil<U>;
500
501
  static create<U extends Record<string, RouteOpts$1>>(value: U, opts?: ChainOptions): QueryUtil<U>;
501
502
  get<K extends keyof T>(key: K): RouteOpts$1;
@@ -6427,9 +6427,11 @@ const util = {
6427
6427
  class QueryUtil {
6428
6428
  obj;
6429
6429
  app;
6430
+ query;
6430
6431
  constructor(object, opts) {
6431
6432
  this.obj = object;
6432
6433
  this.app = opts?.app;
6434
+ this.query = opts?.query;
6433
6435
  }
6434
6436
  static createFormObj(object, opts) {
6435
6437
  return new QueryUtil(object, opts);
@@ -6448,7 +6450,8 @@ class QueryUtil {
6448
6450
  }
6449
6451
  queryChain(key, opts) {
6450
6452
  const value = this.obj[key];
6451
- return new QueryUtil.QueryChain(value, opts);
6453
+ let newOpts = { query: this.query, ...opts };
6454
+ return new QueryUtil.QueryChain(value, newOpts);
6452
6455
  }
6453
6456
  static Chain = Chain;
6454
6457
  static QueryChain = QueryChain;
@@ -58,7 +58,8 @@ declare const util: {
58
58
  declare class QueryUtil<T extends RouteObject = RouteObject> {
59
59
  obj: T;
60
60
  app: QueryRouterServer;
61
- constructor(object: T, opts?: ChainOptions);
61
+ query: Query;
62
+ constructor(object: T, opts?: ChainOptions & QueryChainOptions);
62
63
  static createFormObj<U extends RouteObject>(object: U, opts?: ChainOptions): QueryUtil<U>;
63
64
  static create<U extends Record<string, RouteOpts>>(value: U, opts?: ChainOptions): QueryUtil<U>;
64
65
  get<K extends keyof T>(key: K): RouteOpts;
@@ -97,9 +97,11 @@ const util = {
97
97
  class QueryUtil {
98
98
  obj;
99
99
  app;
100
+ query;
100
101
  constructor(object, opts) {
101
102
  this.obj = object;
102
103
  this.app = opts?.app;
104
+ this.query = opts?.query;
103
105
  }
104
106
  static createFormObj(object, opts) {
105
107
  return new QueryUtil(object, opts);
@@ -118,7 +120,8 @@ class QueryUtil {
118
120
  }
119
121
  queryChain(key, opts) {
120
122
  const value = this.obj[key];
121
- return new QueryUtil.QueryChain(value, opts);
123
+ let newOpts = { query: this.query, ...opts };
124
+ return new QueryUtil.QueryChain(value, newOpts);
122
125
  }
123
126
  static Chain = Chain;
124
127
  static QueryChain = QueryChain;
@@ -1,10 +1,14 @@
1
1
  import * as querystring from 'querystring';
2
2
  import { Key } from 'path-to-regexp';
3
- import { IncomingMessage, ServerResponse } from 'node:http';
3
+ import { IncomingMessage, ServerResponse, Server } from 'node:http';
4
+ import { ListenOptions } from 'node:net';
4
5
 
5
6
  type Req = IncomingMessage & {
6
7
  params?: Record<string, string>;
7
8
  };
9
+ type SimpleObject = {
10
+ [key: string]: any;
11
+ };
8
12
  interface Route {
9
13
  method: string;
10
14
  regexp: RegExp;
@@ -28,7 +32,10 @@ declare class SimpleRouter {
28
32
  use(method: string, route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
29
33
  get(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
30
34
  post(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
35
+ sse(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
31
36
  all(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
37
+ getJson(v: string | number | boolean | SimpleObject): any;
38
+ isSse(req: Req): boolean;
32
39
  /**
33
40
  * 解析 req 和 res 请求
34
41
  * @param req
@@ -36,6 +43,51 @@ declare class SimpleRouter {
36
43
  * @returns
37
44
  */
38
45
  parse(req: Req, res: ServerResponse): Promise<void> | "is_exclude" | "not_found";
46
+ /**
47
+ * 创建一个新的 HttpChain 实例
48
+ * @param req
49
+ * @param res
50
+ * @returns
51
+ */
52
+ chain(req?: Req, res?: ServerResponse): HttpChain;
53
+ static Chain(opts?: HttpChainOpts): HttpChain;
54
+ }
55
+ type HttpChainOpts = {
56
+ req?: Req;
57
+ res?: ServerResponse;
58
+ simpleRouter?: SimpleRouter;
59
+ };
60
+ declare class HttpChain {
61
+ req: Req;
62
+ res: ServerResponse;
63
+ simpleRouter: SimpleRouter;
64
+ server: Server;
65
+ hasSetHeader: boolean;
66
+ isSseSet: boolean;
67
+ constructor(opts?: HttpChainOpts);
68
+ setReq(req: Req): this;
69
+ setRes(res: ServerResponse): this;
70
+ setRouter(router: SimpleRouter): this;
71
+ setServer(server: Server): this;
72
+ /**
73
+ * 兼容 express 的一点功能
74
+ * @param status
75
+ * @returns
76
+ */
77
+ status(status: number): this;
78
+ writeHead(status: number): this;
79
+ json(data: SimpleObject): this;
80
+ /**
81
+ * 兼容 express 的一点功能
82
+ * @param data
83
+ * @returns
84
+ */
85
+ end(data: SimpleObject | string): this;
86
+ listen(opts: ListenOptions, callback?: () => void): this;
87
+ parse(): () => void;
88
+ getString(value: string | SimpleObject): string;
89
+ sse(value: string | SimpleObject): this;
90
+ close(): this;
39
91
  }
40
92
 
41
- export { SimpleRouter };
93
+ export { HttpChain, SimpleRouter };
@@ -511,11 +511,36 @@ class SimpleRouter {
511
511
  post(route, ...fns) {
512
512
  return this.use('post', route, ...fns);
513
513
  }
514
+ sse(route, ...fns) {
515
+ return this.use('sse', route, ...fns);
516
+ }
514
517
  all(route, ...fns) {
515
518
  this.use('post', route, ...fns);
516
519
  this.use('get', route, ...fns);
520
+ this.use('sse', route, ...fns);
517
521
  return this;
518
522
  }
523
+ getJson(v) {
524
+ if (typeof v === 'object') {
525
+ return v;
526
+ }
527
+ try {
528
+ return JSON.parse(v);
529
+ }
530
+ catch (e) {
531
+ return {};
532
+ }
533
+ }
534
+ isSse(req) {
535
+ const { headers } = req;
536
+ if (headers['accept'] && headers['accept'].includes('text/event-stream')) {
537
+ return true;
538
+ }
539
+ if (headers['content-type'] && headers['content-type'].includes('text/event-stream')) {
540
+ return true;
541
+ }
542
+ return false;
543
+ }
519
544
  /**
520
545
  * 解析 req 和 res 请求
521
546
  * @param req
@@ -524,10 +549,13 @@ class SimpleRouter {
524
549
  */
525
550
  parse(req, res) {
526
551
  const { pathname } = new URL(req.url, 'http://localhost');
527
- const method = req.method.toLowerCase();
552
+ let method = req.method.toLowerCase();
528
553
  if (this.exclude.includes(pathname)) {
529
554
  return 'is_exclude';
530
555
  }
556
+ const isSse = this.isSse(req);
557
+ if (isSse)
558
+ method = 'sse';
531
559
  const route = this.routes.find((route) => {
532
560
  const matchResult = route.regexp.exec(pathname);
533
561
  if (matchResult && route.method === method) {
@@ -545,6 +573,169 @@ class SimpleRouter {
545
573
  }
546
574
  return 'not_found';
547
575
  }
576
+ /**
577
+ * 创建一个新的 HttpChain 实例
578
+ * @param req
579
+ * @param res
580
+ * @returns
581
+ */
582
+ chain(req, res) {
583
+ const chain = new HttpChain({ req, res, simpleRouter: this });
584
+ return chain;
585
+ }
586
+ static Chain(opts) {
587
+ return new HttpChain(opts);
588
+ }
589
+ }
590
+ class HttpChain {
591
+ req;
592
+ res;
593
+ simpleRouter;
594
+ server;
595
+ hasSetHeader = false;
596
+ isSseSet = false;
597
+ constructor(opts) {
598
+ this.req = opts?.req;
599
+ this.res = opts?.res;
600
+ this.simpleRouter = opts?.simpleRouter;
601
+ }
602
+ setReq(req) {
603
+ this.req = req;
604
+ return this;
605
+ }
606
+ setRes(res) {
607
+ this.res = res;
608
+ return this;
609
+ }
610
+ setRouter(router) {
611
+ this.simpleRouter = router;
612
+ return this;
613
+ }
614
+ setServer(server) {
615
+ this.server = server;
616
+ return this;
617
+ }
618
+ /**
619
+ * 兼容 express 的一点功能
620
+ * @param status
621
+ * @returns
622
+ */
623
+ status(status) {
624
+ if (!this.res)
625
+ return this;
626
+ if (this.hasSetHeader) {
627
+ return this;
628
+ }
629
+ this.hasSetHeader = true;
630
+ this.res.writeHead(status);
631
+ return this;
632
+ }
633
+ writeHead(status) {
634
+ if (!this.res)
635
+ return this;
636
+ if (this.hasSetHeader) {
637
+ return this;
638
+ }
639
+ this.hasSetHeader = true;
640
+ this.res.writeHead(status);
641
+ return this;
642
+ }
643
+ json(data) {
644
+ if (!this.res)
645
+ return this;
646
+ this.res.end(JSON.stringify(data));
647
+ return this;
648
+ }
649
+ /**
650
+ * 兼容 express 的一点功能
651
+ * @param data
652
+ * @returns
653
+ */
654
+ end(data) {
655
+ if (!this.res)
656
+ return this;
657
+ if (typeof data === 'object') {
658
+ this.res.end(JSON.stringify(data));
659
+ }
660
+ else if (typeof data === 'string') {
661
+ this.res.end(data);
662
+ }
663
+ else {
664
+ this.res.end('nothing');
665
+ }
666
+ return this;
667
+ }
668
+ listen(opts, callback) {
669
+ this.server.listen(opts, callback);
670
+ return this;
671
+ }
672
+ parse() {
673
+ if (!this.server || !this.simpleRouter) {
674
+ throw new Error('Server and SimpleRouter must be set before calling parse');
675
+ }
676
+ const that = this;
677
+ const listener = (req, res) => {
678
+ try {
679
+ that.simpleRouter.parse(req, res);
680
+ }
681
+ catch (error) {
682
+ console.error('Error parsing request:', error);
683
+ if (!res.headersSent) {
684
+ res.writeHead(500);
685
+ res.end(JSON.stringify({ code: 500, message: 'Internal Server Error' }));
686
+ }
687
+ }
688
+ };
689
+ this.server.on('request', listener);
690
+ return () => {
691
+ that.server.removeListener('request', listener);
692
+ };
693
+ }
694
+ getString(value) {
695
+ if (typeof value === 'string') {
696
+ return value;
697
+ }
698
+ return JSON.stringify(value);
699
+ }
700
+ sse(value) {
701
+ const res = this.res;
702
+ const req = this.req;
703
+ if (!res || !req)
704
+ return;
705
+ const data = this.getString(value);
706
+ if (this.isSseSet) {
707
+ res.write(`data: ${data}\n\n`);
708
+ return this;
709
+ }
710
+ const headersMap = new Map([
711
+ ['Content-Type', 'text/event-stream'],
712
+ ['Cache-Control', 'no-cache'],
713
+ ['Connection', 'keep-alive'],
714
+ ]);
715
+ this.isSseSet = true;
716
+ let intervalId;
717
+ if (!this.hasSetHeader) {
718
+ this.hasSetHeader = true;
719
+ res.setHeaders(headersMap);
720
+ // 每隔 2 秒发送一个空行,保持连接
721
+ setInterval(() => {
722
+ res.write('\n'); // 发送一个空行,保持连接
723
+ }, 3000);
724
+ // 客户端断开连接时清理
725
+ req.on('close', () => {
726
+ clearInterval(intervalId);
727
+ res.end();
728
+ });
729
+ }
730
+ this.res.write(`data: ${data}\n\n`);
731
+ return this;
732
+ }
733
+ close() {
734
+ if (this.req?.destroy) {
735
+ this.req.destroy();
736
+ }
737
+ return this;
738
+ }
548
739
  }
549
740
 
550
- export { SimpleRouter };
741
+ export { HttpChain, SimpleRouter };
package/dist/router.d.ts CHANGED
@@ -636,7 +636,8 @@ declare const util: {
636
636
  declare class QueryUtil<T extends RouteObject = RouteObject> {
637
637
  obj: T;
638
638
  app: QueryRouterServer$1;
639
- constructor(object: T, opts?: ChainOptions);
639
+ query: Query;
640
+ constructor(object: T, opts?: ChainOptions & QueryChainOptions);
640
641
  static createFormObj<U extends RouteObject>(object: U, opts?: ChainOptions): QueryUtil<U>;
641
642
  static create<U extends Record<string, RouteOpts$1>>(value: U, opts?: ChainOptions): QueryUtil<U>;
642
643
  get<K extends keyof T>(key: K): RouteOpts$1;
package/dist/router.js CHANGED
@@ -12066,9 +12066,11 @@ const util = {
12066
12066
  class QueryUtil {
12067
12067
  obj;
12068
12068
  app;
12069
+ query;
12069
12070
  constructor(object, opts) {
12070
12071
  this.obj = object;
12071
12072
  this.app = opts?.app;
12073
+ this.query = opts?.query;
12072
12074
  }
12073
12075
  static createFormObj(object, opts) {
12074
12076
  return new QueryUtil(object, opts);
@@ -12087,7 +12089,8 @@ class QueryUtil {
12087
12089
  }
12088
12090
  queryChain(key, opts) {
12089
12091
  const value = this.obj[key];
12090
- return new QueryUtil.QueryChain(value, opts);
12092
+ let newOpts = { query: this.query, ...opts };
12093
+ return new QueryUtil.QueryChain(value, newOpts);
12091
12094
  }
12092
12095
  static Chain = Chain;
12093
12096
  static QueryChain = QueryChain;
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.19",
4
+ "version": "0.0.21-beta",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "main": "./dist/router.js",
@@ -120,9 +120,11 @@ export const util = {
120
120
  export class QueryUtil<T extends RouteObject = RouteObject> {
121
121
  obj: T;
122
122
  app: QueryRouterServer;
123
- constructor(object: T, opts?: ChainOptions) {
123
+ query: Query;
124
+ constructor(object: T, opts?: ChainOptions & QueryChainOptions) {
124
125
  this.obj = object;
125
126
  this.app = opts?.app;
127
+ this.query = opts?.query;
126
128
  }
127
129
  static createFormObj<U extends RouteObject>(object: U, opts?: ChainOptions) {
128
130
  return new QueryUtil<U>(object, opts);
@@ -141,7 +143,8 @@ export class QueryUtil<T extends RouteObject = RouteObject> {
141
143
  }
142
144
  queryChain<K extends keyof T>(key: K, opts?: QueryChainOptions) {
143
145
  const value = this.obj[key];
144
- return new QueryUtil.QueryChain(value, opts);
146
+ let newOpts = { query: this.query, ...opts };
147
+ return new QueryUtil.QueryChain(value, newOpts);
145
148
  }
146
149
  static Chain = Chain;
147
150
  static QueryChain = QueryChain;
@@ -1,8 +1,12 @@
1
1
  import { pathToRegexp, Key } from 'path-to-regexp';
2
- import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { IncomingMessage, ServerResponse, Server } from 'node:http';
3
3
  import { parseBody, parseSearch, parseSearchValue } from './server/parse-body.ts';
4
+ import { ListenOptions } from 'node:net';
4
5
 
5
6
  type Req = IncomingMessage & { params?: Record<string, string> };
7
+ type SimpleObject = {
8
+ [key: string]: any;
9
+ };
6
10
  interface Route {
7
11
  method: string;
8
12
  regexp: RegExp;
@@ -28,7 +32,6 @@ export class SimpleRouter {
28
32
  use(method: string, route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
29
33
  const handlers = Array.isArray(fns) ? fns.flat() : [];
30
34
  const pattern = pathToRegexp(route);
31
-
32
35
  this.routes.push({ method: method.toLowerCase(), regexp: pattern.regexp, keys: pattern.keys, handlers });
33
36
  return this;
34
37
  }
@@ -38,11 +41,35 @@ export class SimpleRouter {
38
41
  post(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
39
42
  return this.use('post', route, ...fns);
40
43
  }
44
+ sse(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
45
+ return this.use('sse', route, ...fns);
46
+ }
41
47
  all(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
42
48
  this.use('post', route, ...fns);
43
49
  this.use('get', route, ...fns);
50
+ this.use('sse', route, ...fns);
44
51
  return this;
45
52
  }
53
+ getJson(v: string | number | boolean | SimpleObject) {
54
+ if (typeof v === 'object') {
55
+ return v;
56
+ }
57
+ try {
58
+ return JSON.parse(v as string);
59
+ } catch (e) {
60
+ return {};
61
+ }
62
+ }
63
+ isSse(req: Req) {
64
+ const { headers } = req;
65
+ if (headers['accept'] && headers['accept'].includes('text/event-stream')) {
66
+ return true;
67
+ }
68
+ if (headers['content-type'] && headers['content-type'].includes('text/event-stream')) {
69
+ return true;
70
+ }
71
+ return false;
72
+ }
46
73
  /**
47
74
  * 解析 req 和 res 请求
48
75
  * @param req
@@ -51,10 +78,12 @@ export class SimpleRouter {
51
78
  */
52
79
  parse(req: Req, res: ServerResponse) {
53
80
  const { pathname } = new URL(req.url, 'http://localhost');
54
- const method = req.method.toLowerCase();
81
+ let method = req.method.toLowerCase();
55
82
  if (this.exclude.includes(pathname)) {
56
83
  return 'is_exclude';
57
84
  }
85
+ const isSse = this.isSse(req);
86
+ if (isSse) method = 'sse';
58
87
  const route = this.routes.find((route) => {
59
88
  const matchResult = route.regexp.exec(pathname);
60
89
  if (matchResult && route.method === method) {
@@ -74,4 +103,166 @@ export class SimpleRouter {
74
103
 
75
104
  return 'not_found';
76
105
  }
106
+ /**
107
+ * 创建一个新的 HttpChain 实例
108
+ * @param req
109
+ * @param res
110
+ * @returns
111
+ */
112
+ chain(req?: Req, res?: ServerResponse) {
113
+ const chain = new HttpChain({ req, res, simpleRouter: this });
114
+ return chain;
115
+ }
116
+ static Chain(opts?: HttpChainOpts) {
117
+ return new HttpChain(opts);
118
+ }
119
+ }
120
+
121
+ type HttpChainOpts = {
122
+ req?: Req;
123
+ res?: ServerResponse;
124
+ simpleRouter?: SimpleRouter;
125
+ };
126
+ export class HttpChain {
127
+ req: Req;
128
+ res: ServerResponse;
129
+ simpleRouter: SimpleRouter;
130
+ server: Server;
131
+ hasSetHeader: boolean = false;
132
+ isSseSet: boolean = false;
133
+ constructor(opts?: HttpChainOpts) {
134
+ this.req = opts?.req;
135
+ this.res = opts?.res;
136
+ this.simpleRouter = opts?.simpleRouter;
137
+ }
138
+ setReq(req: Req) {
139
+ this.req = req;
140
+ return this;
141
+ }
142
+ setRes(res: ServerResponse) {
143
+ this.res = res;
144
+ return this;
145
+ }
146
+ setRouter(router: SimpleRouter) {
147
+ this.simpleRouter = router;
148
+ return this;
149
+ }
150
+ setServer(server: Server) {
151
+ this.server = server;
152
+ return this;
153
+ }
154
+ /**
155
+ * 兼容 express 的一点功能
156
+ * @param status
157
+ * @returns
158
+ */
159
+ status(status: number) {
160
+ if (!this.res) return this;
161
+ if (this.hasSetHeader) {
162
+ return this;
163
+ }
164
+ this.hasSetHeader = true;
165
+ this.res.writeHead(status);
166
+ return this;
167
+ }
168
+ writeHead(status: number) {
169
+ if (!this.res) return this;
170
+ if (this.hasSetHeader) {
171
+ return this;
172
+ }
173
+ this.hasSetHeader = true;
174
+ this.res.writeHead(status);
175
+ return this;
176
+ }
177
+ json(data: SimpleObject) {
178
+ if (!this.res) return this;
179
+ this.res.end(JSON.stringify(data));
180
+ return this;
181
+ }
182
+ /**
183
+ * 兼容 express 的一点功能
184
+ * @param data
185
+ * @returns
186
+ */
187
+ end(data: SimpleObject | string) {
188
+ if (!this.res) return this;
189
+ if (typeof data === 'object') {
190
+ this.res.end(JSON.stringify(data));
191
+ } else if (typeof data === 'string') {
192
+ this.res.end(data);
193
+ } else {
194
+ this.res.end('nothing');
195
+ }
196
+ return this;
197
+ }
198
+
199
+ listen(opts: ListenOptions, callback?: () => void) {
200
+ this.server.listen(opts, callback);
201
+ return this;
202
+ }
203
+ parse() {
204
+ if (!this.server || !this.simpleRouter) {
205
+ throw new Error('Server and SimpleRouter must be set before calling parse');
206
+ }
207
+ const that = this;
208
+ const listener = (req: Req, res: ServerResponse) => {
209
+ try {
210
+ that.simpleRouter.parse(req, res);
211
+ } catch (error) {
212
+ console.error('Error parsing request:', error);
213
+ if (!res.headersSent) {
214
+ res.writeHead(500);
215
+ res.end(JSON.stringify({ code: 500, message: 'Internal Server Error' }));
216
+ }
217
+ }
218
+ };
219
+ this.server.on('request', listener);
220
+ return () => {
221
+ that.server.removeListener('request', listener);
222
+ };
223
+ }
224
+ getString(value: string | SimpleObject) {
225
+ if (typeof value === 'string') {
226
+ return value;
227
+ }
228
+ return JSON.stringify(value);
229
+ }
230
+ sse(value: string | SimpleObject) {
231
+ const res = this.res;
232
+ const req = this.req;
233
+ if (!res || !req) return;
234
+ const data = this.getString(value);
235
+ if (this.isSseSet) {
236
+ res.write(`data: ${data}\n\n`);
237
+ return this;
238
+ }
239
+ const headersMap = new Map<string, string>([
240
+ ['Content-Type', 'text/event-stream'],
241
+ ['Cache-Control', 'no-cache'],
242
+ ['Connection', 'keep-alive'],
243
+ ]);
244
+ this.isSseSet = true;
245
+ let intervalId: NodeJS.Timeout;
246
+ if (!this.hasSetHeader) {
247
+ this.hasSetHeader = true;
248
+ res.setHeaders(headersMap);
249
+ // 每隔 2 秒发送一个空行,保持连接
250
+ setInterval(() => {
251
+ res.write('\n'); // 发送一个空行,保持连接
252
+ }, 3000);
253
+ // 客户端断开连接时清理
254
+ req.on('close', () => {
255
+ clearInterval(intervalId);
256
+ res.end();
257
+ });
258
+ }
259
+ this.res.write(`data: ${data}\n\n`);
260
+ return this;
261
+ }
262
+ close() {
263
+ if (this.req?.destroy) {
264
+ this.req.destroy();
265
+ }
266
+ return this;
267
+ }
77
268
  }