k99 0.7.1 → 0.9.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/index.mjs CHANGED
@@ -1,14 +1,15 @@
1
1
  /*!
2
- * k99 v0.7.1
3
- * (c) 2019-2025 猛火Fierflame
2
+ * k99 v0.9.0
3
+ * (c) 2019-2026 猛火Fierflame
4
4
  * @license MIT
5
5
  */
6
6
  /**
7
7
  *
8
8
  * @param {string} str
9
- * @returns {Uint8Array}
9
+ * @returns {Uint8Array<ArrayBuffer>}
10
10
  */
11
11
  function str2utf8bin(str) {
12
+ // @ts-ignore
12
13
  return new TextEncoder().encode(str);
13
14
  }
14
15
  /**
@@ -47,26 +48,15 @@ async function write(writer, chunk) {
47
48
  }
48
49
  }
49
50
 
50
- /**
51
- *
52
- * @param {any} k
53
- * @param {any} v
54
- * @returns {any}
55
- */
56
- function replacer(k, v) {
57
- if (typeof v === 'bigint') {
58
- return String(v);
59
- }
60
- return v;
61
- }
62
51
 
63
52
  /**
64
53
  *
65
54
  * @param {any} result
55
+ * @param {(this: any, key: string, value: any) => any} replacer
66
56
  * @param {Promise<never>} [aborted]
67
57
  * @returns {[BodyInit, number, string] | null}
68
58
  */
69
- function toBodyData(result, aborted) {
59
+ function toBodyData(result, replacer, aborted) {
70
60
  if (result instanceof ReadableStream) {
71
61
  return [result, 0, ''];
72
62
  }
@@ -80,11 +70,16 @@ function toBodyData(result, aborted) {
80
70
  return [result, 0, ''];
81
71
  }
82
72
  if (ArrayBuffer.isView(result) || result instanceof ArrayBuffer) {
73
+ // @ts-ignore
83
74
  return [result, result.byteLength, ''];
84
75
  }
85
76
  if (typeof result === 'string') {
86
77
  const body = str2utf8bin(result);
87
- return [body, body.byteLength, ''];
78
+ return [body, body.byteLength, 'text/plain'];
79
+ }
80
+ if (['bigint', 'boolean', 'number'].includes(typeof result)) {
81
+ const body = str2utf8bin(JSON.stringify(result, replacer));
82
+ return [body, body.byteLength, 'application/json'];
88
83
  }
89
84
  if (typeof result !== 'object') {
90
85
  return null;
@@ -111,11 +106,12 @@ function toBodyData(result, aborted) {
111
106
  *
112
107
  * @param {any} result
113
108
  * @param {Headers} headers
109
+ * @param {(this: any, key: string, value: any) => any} replacer
114
110
  * @param {Promise<never>} [aborted]
115
111
  * @returns
116
112
  */
117
- function toBody(result, headers, aborted) {
118
- const bodyData = toBodyData(result, aborted);
113
+ function toBody(result, headers, replacer, aborted) {
114
+ const bodyData = toBodyData(result, replacer, aborted);
119
115
  if (!bodyData) { return null; }
120
116
  const [body, size, type] = bodyData;
121
117
  if (type && !headers.get('Content-Type')) {
@@ -209,7 +205,7 @@ function clearCookie(
209
205
  }
210
206
  }
211
207
 
212
- /** @import { Context, Cookie, CookieOption, FindHandler, Method, Options, Params, Service } from './types' */
208
+ /** @import { Context, Cookie, CookieOption, FindHandler, HandlerResult, Method, Options, Params, Service } from './types' */
213
209
 
214
210
 
215
211
  const noBodyMethods = new Set(['GET', 'OPTIONS']);
@@ -262,6 +258,18 @@ function getMethod(request, toMethod) {
262
258
  }
263
259
  return /** @type {Method} */(methodStr.toUpperCase());
264
260
  }
261
+ /**
262
+ *
263
+ * @param {any} k
264
+ * @param {any} v
265
+ * @returns {any}
266
+ */
267
+ function defaultReplacer(k, v) {
268
+ if (typeof v === 'bigint') {
269
+ return String(v);
270
+ }
271
+ return v;
272
+ }
265
273
  /**
266
274
  *
267
275
  * @param {Request} request
@@ -269,10 +277,15 @@ function getMethod(request, toMethod) {
269
277
  * @param {Options} [options]
270
278
  * @returns {Promise<Response | null>}
271
279
  */
272
- function main(
273
- request, getHandler,
274
- { runner, error: echoError, catch: catchError, method: toMethod, environment } = {},
275
- ) {
280
+ function main(request, getHandler, {
281
+ runner,
282
+ error: echoError,
283
+ catch: catchError,
284
+ method: toMethod,
285
+ environment,
286
+ replacer: JSONReplacer,
287
+ } = {}) {
288
+ const replacer = typeof JSONReplacer === 'function' ? JSONReplacer : defaultReplacer;
276
289
  /**
277
290
  *
278
291
  * @param {Request} request
@@ -296,12 +309,12 @@ function main(
296
309
  /** @type {any} */
297
310
  let error = null;
298
311
 
299
- let resolve = () => {};
312
+ let resolve = () => { };
300
313
  /** @type {(error: unknown) => void} */
301
- let reject = () => {};
314
+ let reject = () => { };
302
315
  /** @type {Promise<void>} */
303
316
  const donePromise = new Promise((a, b) => { resolve = a; reject = b; });
304
- donePromise.catch(() => {});
317
+ donePromise.catch(() => { });
305
318
 
306
319
  /** @type {Params} */
307
320
  let params = {};
@@ -319,7 +332,7 @@ function main(
319
332
  if (!data || noBodyMethods.has(method.toUpperCase())) {
320
333
  return exec(new Request(fetchUrl, { method, headers, signal }), context);
321
334
  }
322
- const body = toBody(data, headers);
335
+ const body = toBody(data, headers, replacer);
323
336
  return exec(new Request(fetchUrl, { method, headers, signal, body }), context);
324
337
  },
325
338
  done(onfulfilled, onrejected) {
@@ -364,7 +377,7 @@ function main(
364
377
  set responseType(type) { setHeader(responseHeaders, 'content-type', type); },
365
378
  getCookie(name) { return getCookie(sentCookies, name); },
366
379
  setCookie(name, value, { expire, domain, path, secure, httpOnly } = {}) {
367
- sentCookies.push({name, value, expire, domain, path, secure, httpOnly});
380
+ sentCookies.push({ name, value, expire, domain, path, secure, httpOnly });
368
381
  setCookiesHeader(responseHeaders, sentCookies);
369
382
  },
370
383
  /**
@@ -382,16 +395,22 @@ function main(
382
395
  return Promise.race([
383
396
  aborted,
384
397
  Promise.resolve().then(() => getHandler(context, v => { params = v; })),
385
- ]).then(handler => handler ? Promise.race([aborted, handler(context)]).then(result => {
386
- if (result instanceof Response) {
387
- return result;
398
+ ]).then(async handlers => {
399
+ const allHandlers = [handlers].flat().filter(h => typeof h === 'function');
400
+ if (!allHandlers.length) { return null; }
401
+ /** @type {HandlerResult?} */
402
+ let result;
403
+ for (const handle of allHandlers) {
404
+ result = await Promise.race([aborted, handle(context)]);
405
+ if (result !== undefined) { break; }
388
406
  }
407
+ if (result instanceof Response) { return result; }
389
408
  const headers = new Headers(context.responseHeaders);
390
409
  const { status } = context;
391
- if (!result) { return new Response(null, { status, headers }); }
392
- const body = toBody(result, headers, aborted);
410
+ if (!result == null) { return new Response(null, { status, headers }); }
411
+ const body = toBody(result, headers, replacer, aborted);
393
412
  return new Response(body, { status, headers });
394
- }) : null).then(response => {
413
+ }).then(response => {
395
414
  destroyed = true;
396
415
  resolve();
397
416
  return response;
@@ -407,40 +426,151 @@ function main(
407
426
  return exec(request);
408
427
  }
409
428
 
410
- /** @import { FindHandler, Options } from './main/types' */
429
+ /** @import { Method, Params } from './main/types' */
411
430
 
412
431
  /**
413
- *
414
- * @param {FindHandler} getHandler
415
- * @param {Options} options
416
- * @returns {(request: Request) => Promise<Response | null>}
432
+ * @template {Function} T
433
+ * @typedef {[T | T[] | Router<T>, Record<string | symbol, any>, string[]]} FindItem
417
434
  */
418
- function make(getHandler, options) {
419
- return r => main(r, getHandler, options);
435
+ /**
436
+ * @template {Function} T
437
+ * @callback Finder
438
+ * @this {Router<T>}
439
+ * @param {Method} method
440
+ * @param {string[]} path
441
+ * @returns {AsyncIterable<FindItem<T>> | Iterable<FindItem<T>>}
442
+ */
443
+
444
+ /**
445
+ * @abstract
446
+ * @template {Function} T
447
+ */
448
+ class Router {
449
+ disabled = false;
450
+ /**
451
+ *
452
+ * @template {Function} T
453
+ * @param {Router<T> | T[] | T} route
454
+ * @param {Method} method
455
+ * @param {string[]} path
456
+ * @param {Params} params
457
+ * @param {AbortSignal?} [signal]
458
+ * @param {((v: Params) => void)?} [setParams]
459
+ * @returns {Promise<T[] | null>}
460
+ */
461
+ static async #find(route, method, path, params, signal, setParams) {
462
+ if (!(route instanceof Router)) {
463
+ if (typeof setParams === 'function') { setParams(params); }
464
+ // @ts-ignore
465
+ return [route].flat();
466
+ }
467
+ if (route.disabled) { return null; }
468
+ if (signal?.aborted) { return null; }
469
+ for await (const [r, result, p] of route.find(method, path)) {
470
+ if (signal?.aborted) { return null; }
471
+ const res = await Router.#find(r, method, p, { ...params, ...result }, signal, setParams);
472
+ if (res) { return [...route.#guards, ...res]; }
473
+ }
474
+ return null;
475
+ }
476
+ /**
477
+ *
478
+ * @template {Function} T
479
+ * @param {Router<T>[]} routers
480
+ * @param {Method} method
481
+ * @param {string[]} path
482
+ * @param {AbortSignal?} [signal]
483
+ * @param {((v: Params) => void)?} [setParams]
484
+ * @returns {Promise<T[] | null>}
485
+ */
486
+ static async find(routers, method, path, signal, setParams) {
487
+ const m = `${method}`.toLowerCase();
488
+ for (const route of routers.flat()) {
489
+ const res = await Router.#find(route, m, path, {}, signal, setParams);
490
+ if (res) { return res; }
491
+ }
492
+ return null;
493
+ }
494
+ /**
495
+ * @abstract
496
+ * @param {Method} method
497
+ * @param {string[]} path
498
+ * @returns {AsyncIterable<FindItem<T>> | Iterable<FindItem<T>>}
499
+ */
500
+ find(method, path) { return []; }
501
+
502
+
503
+ /** @type {T[]} */
504
+ #guards = [];
505
+ /**
506
+ *
507
+ * @param {...T | T[]} guards
508
+ */
509
+ guard(...guards) {
510
+ const list = this.#guards;
511
+ for (const guard of guards.flat()) {
512
+ if (typeof guard !== 'function') { continue; }
513
+ // @ts-ignore
514
+ list.push(guard);
515
+ }
516
+ }
517
+
518
+ /**
519
+ *
520
+ * @template {Function} T
521
+ * @param {Finder<T>} find
522
+ * @returns {Router<T>}
523
+ */
524
+ static create(find) {
525
+ return Object.defineProperties(new Router(), {
526
+ 'find': { configurable: true, value: find, writable: true },
527
+ });
528
+ }
420
529
  }
421
530
 
422
- /** @import { Context, Handler } from './main/types' */
531
+ /** @import { FindHandler, Handler, Options } from './main/types' */
532
+
533
+ /**
534
+ *
535
+ * @param {string} t
536
+ * @returns
537
+ */
538
+ function uriDecode(t) {
539
+ try {
540
+ return decodeURIComponent(t);
541
+ } catch {
542
+ return t;
543
+ }
544
+ }
423
545
  /**
424
546
  *
425
- * @param {Context} context
426
- * @param {Handler[]} handlers
427
- * @returns {Promise<string | boolean | object | undefined>}
547
+ * @param {Router<Handler> | Router<Handler>[] | FindHandler} routers
548
+ * @param {Options} options
549
+ * @returns {(request: Request) => Promise<Response | null>}
428
550
  */
429
- async function runHandles(context, handlers) {
430
- for (const handle of handlers) {
431
- const result = await handle(context);
432
- if (result === undefined) { continue; }
433
- return result;
551
+ function make(routers, options) {
552
+ if (typeof routers === 'function') {
553
+ return r => main(r, routers, options);
434
554
  }
555
+ const list = [routers].flat();
556
+ /** @type {FindHandler} */
557
+ const getHandler = (ctx, setParams) => {
558
+ const path = ctx.url.pathname.split('/').filter(Boolean).map(uriDecode);
559
+ return Router.find(list, ctx.method, path, ctx.signal, setParams);
560
+ };
561
+ return r => main(r, getHandler, options);
435
562
  }
436
563
 
564
+ /** @import { FindHandler, Options } from './main/types' */
565
+
437
566
  /**
438
567
  *
439
- * @param {...(Handler | Handler[])} handlers
440
- * @returns {Handler}
568
+ * @param {FindHandler} getHandler
569
+ * @param {Options} options
570
+ * @returns {(request: Request) => Promise<Response | null>}
441
571
  */
442
- function merge(...handlers) {
443
- return ctx => runHandles(ctx, handlers.flat());
572
+ function bind$1(getHandler, options) {
573
+ return r => main(r, getHandler, options);
444
574
  }
445
575
 
446
576
  /** @import { Context, Service } from './main/types' */
@@ -622,162 +752,6 @@ function storeService(destroy, exec, options) {
622
752
  return service;
623
753
  }
624
754
 
625
- /** @import { Handler } from './main/types' */
626
- /** @import { Onionskin } from './onionskin.mjs' */
627
- /**
628
- * @callback Packer
629
- * @param {Handler} handler
630
- * @returns {Handler}
631
- */
632
- /** @type {Packer} */
633
- const noop$1 = h => h;
634
- /**
635
- *
636
- * @param {Onionskin} onionskin
637
- * @param {Packer} [packer]
638
- * @returns {Packer}
639
- */
640
- function packer(onionskin, packer = noop$1) {
641
- return h => {
642
- const handler = packer(h);
643
- return async (ctx) => onionskin(ctx, async () => handler(ctx));
644
- };
645
- }
646
-
647
- /** @import { Context, FindHandler, Handler, Method, Params } from './main/types' */
648
- /** @import { Onionskin } from './onionskin.mjs' */
649
-
650
- /**
651
- * @callback Guard
652
- * @param {Context} ctx
653
- * @returns {PromiseLike<boolean | Handler | void> | boolean | Handler | void}
654
- */
655
- /**
656
- * @typedef {[Handler | Router, Record<string | symbol, any>, string[]]} FindItem
657
- */
658
- /**
659
- * @callback Finder
660
- * @this {Router}
661
- * @param {Method} method
662
- * @param {string[]} path
663
- * @param {Context} ctx
664
- * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
665
- */
666
-
667
- /**
668
- *
669
- * @param {Set<Guard>} guards
670
- * @param {Context} ctx
671
- * @param {(v: any) => void} setParams
672
- * @param {object} params
673
- * @returns {Promise<boolean | Handler>}
674
- */
675
- async function execGuard(guards, ctx, setParams, params) {
676
- if (!guards.size) { return true; }
677
- setParams(params);
678
- for (const guard of guards) {
679
- if (ctx.destroyed) { return false; }
680
- const ret = await guard(Object.create(ctx, {
681
- params: { value: { ...params } },
682
- }));
683
- if (ret === false) { return false; }
684
- // @ts-ignore
685
- if (typeof ret === 'function') { return ret; }
686
- }
687
- return true;
688
- }
689
-
690
- /**
691
- *
692
- * @param {Router | Handler} route
693
- * @param {string[]} path
694
- * @param {Context} ctx
695
- * @param {(v: Params) => void} setParams
696
- * @param {Params} params
697
- * @returns {Promise<Handler | null>}
698
- */
699
- async function find(route, path, ctx, setParams, params) {
700
- if (!(route instanceof Router)) {
701
- setParams(params);
702
- return route;
703
- }
704
- if (route.disabled) { return null; }
705
- const guardResult = await execGuard(route.guards, ctx, setParams, params);
706
- if (!guardResult) { return null; }
707
- if (typeof guardResult === 'function') { return guardResult; }
708
- if (ctx.destroyed) { return null; }
709
- for await (const [r, result, p] of route.find(ctx.method, path, ctx)) {
710
- if (ctx.destroyed) { return null; }
711
- const res = await find(r, p, ctx, setParams, { ...params, ...result });
712
- if (res) { return route.__onionskin(res); }
713
- }
714
- return null;
715
- }
716
-
717
- /**
718
- *
719
- * @param {string} t
720
- * @returns
721
- */
722
- function uriDecode(t) {
723
- try {
724
- return decodeURIComponent(t);
725
- } catch {
726
- return t;
727
- }
728
- }
729
- /**
730
- * @abstract
731
- */
732
- class Router {
733
- disabled = false;
734
- /**
735
- * @abstract
736
- * @param {Method} method
737
- * @param {string[]} path
738
- * @param {Context} ctx
739
- * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
740
- */
741
- find(method, path, ctx) { return []; }
742
- /**
743
- *
744
- * @param {Router[]} routers
745
- * @returns {FindHandler}
746
- */
747
- static make(routers) {
748
- return async (ctx, setParams) => {
749
- const list = routers.flat();
750
- const path = ctx.url.pathname.split('/').filter(Boolean).map(uriDecode);
751
- for (const route of list) {
752
- const res = await find(route, path, ctx, setParams, {});
753
- if (res) { return res; }
754
- }
755
- return null;
756
- };
757
- }
758
-
759
- /**
760
- *
761
- * @param {Finder} find
762
- * @returns {Router}
763
- */
764
- static create(find) {
765
- return Object.defineProperties(new Router(), {
766
- 'find': { configurable: true, value: find, writable: true },
767
- });
768
- }
769
- /** @readonly @type {Set<Guard>} */
770
- guards = new Set();
771
- /**
772
- *
773
- * @param {Handler} h
774
- * @returns {Handler}
775
- */
776
- __onionskin = (h) => h;
777
- /** @param {Onionskin} os */
778
- onionskin(os) { this.__onionskin = packer(os, this.__onionskin); }
779
- }
780
-
781
755
  /** @import { Match } from './index.mjs' */
782
756
  /** @import { Params } from '../main/types.js' */
783
757
  /**
@@ -960,20 +934,21 @@ function toMatch(path, end) {
960
934
  return path => exec(list, path, end);
961
935
  }
962
936
 
963
- /** @import { Handler, Method } from '../main/types' */
937
+ /** @import { Method } from '../main/types' */
964
938
  /** @import { Binder, Match, Route, RouterRoute } from './index.mjs' */
965
939
 
966
940
  /**
967
941
  *
968
- * @param {(Route | RouterRoute)[]} routes
942
+ * @template {Function} T
943
+ * @param {(Route<T> | RouterRoute<T>)[]} routes
969
944
  * @param {Set<Method>} methods
970
945
  * @param {Match | undefined} match
971
- * @param {Handler[]} handlers
946
+ * @param {T[]} handlers
972
947
  * @returns {() => void}
973
948
  */
974
949
  function bind(routes, methods, match, handlers) {
975
- /** @type {Route} */
976
- const route = { match, methods, handler: ctx => runHandles(ctx, handlers) };
950
+ /** @type {Route<T>} */
951
+ const route = { match, methods, handlers: handlers };
977
952
  routes.push(route);
978
953
  let removed = false;
979
954
  return () => {
@@ -984,45 +959,45 @@ function bind(routes, methods, match, handlers) {
984
959
  routes.splice(index, 1);
985
960
  };
986
961
  }
987
- /** @type {(v: any) => v is Handler} */
962
+ /** @type {(v: any) => v is Function} */
988
963
  const findHandler = v => typeof v === 'function';
989
964
  /**
990
965
  *
991
- * @param {(Route | RouterRoute)[]} routes
966
+ * @template {Function} T
967
+ * @param {(Route<T> | RouterRoute<T>)[]} routes
992
968
  * @param {Iterable<Method>} methods
993
969
  * @param {any[]} p
994
- * @returns {Binder | (() => void)}
970
+ * @returns {Binder<T> | (() => void)}
995
971
  */
996
972
  function verb(routes, methods, p) {
997
973
  const methodSet = new Set(methods);
998
974
  if (!p.length) {
999
975
  const match = undefined;
1000
- /** @type {Binder} */
976
+ /** @type {Binder<T>} */
1001
977
  return (...handlers) => bind(routes, methodSet, match, handlers);
1002
978
  }
1003
979
  const [path] = p;
1004
980
  if (path && typeof path === 'object') {
1005
981
  const match = toMatch([path, p.slice(1)], true);
1006
- /** @type {Binder} */
982
+ /** @type {Binder<T>} */
1007
983
  return (...handlers) => bind(routes, methodSet, match, handlers);
1008
984
  }
1009
985
  const match = toMatch(typeof path === 'string' ? path : '', true);
1010
986
  const handlers = p.filter(findHandler);
1011
987
  if (!handlers.length) {
1012
- /** @type {Binder} */
988
+ /** @type {Binder<T>} */
1013
989
  return (...handlers) => bind(routes, methodSet, match, handlers);
1014
990
  }
1015
991
  return bind(routes, methodSet, match, handlers);
1016
992
  }
1017
993
 
1018
994
  /** @import { Method } from '../main/types' */
1019
- const methods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']);
1020
995
  /**
1021
996
  *
1022
997
  * @param {any} v
1023
998
  * @returns {v is Method}
1024
999
  */
1025
- function isMethod(v) { return methods.has(v); }
1000
+ function isMethod(v) { return Boolean(v); }
1026
1001
  /**
1027
1002
  *
1028
1003
  * @param {Method | Iterable<Method> | ArrayLike<Method>} [methods]
@@ -1040,7 +1015,7 @@ function getMethods(methods) {
1040
1015
  .filter(isMethod);
1041
1016
  }
1042
1017
 
1043
- /** @import { Context, Handler, Method, Params } from '../main/types' */
1018
+ /** @import { Handler, Method, Params } from '../main/types' */
1044
1019
  /** @import { Finder, FindItem } from '../Router.mjs' */
1045
1020
 
1046
1021
  /**
@@ -1049,79 +1024,125 @@ function getMethods(methods) {
1049
1024
  * @returns {[Params, string[]] | undefined}
1050
1025
  */
1051
1026
 
1052
- /** @typedef {(handler: Handler, ...handlers: Handler[]) => () => void} Binder */
1027
+ /**
1028
+ * @template {Function} T
1029
+ * @typedef {(handler: T, ...handlers: T[]) => () => void} Binder
1030
+ */
1031
+
1032
+
1033
+ /**
1034
+ * @template {Function} T
1035
+ * @callback Verb1
1036
+ * @param {T} handler 要注册的处理函数
1037
+ * @param {...T} handlers 要注册的处理函数
1038
+ * @returns {() => void}
1039
+ */
1040
+ /**
1041
+ * @template {Function} T
1042
+ * @callback Verb2
1043
+ * @param {string} path 要注册的路径
1044
+ * @param {T} handler 要注册的处理函数
1045
+ * @param {...T} handlers 要注册的处理函数
1046
+ * @returns {() => void}
1047
+ */
1048
+ /**
1049
+ * @template {Function} T
1050
+ * @callback Verb3
1051
+ * @param {string} path 要注册的路径
1052
+ * @returns {Binder<T>}
1053
+ */
1054
+ /**
1055
+ * @template {Function} T
1056
+ * @callback Verb4
1057
+ * @param {TemplateStringsArray} template 要注册的路径模板
1058
+ * @param {...any} substitutions 要注册的路径模板代替内容
1059
+ * @returns {Binder<T>}
1060
+ */
1061
+ /**
1062
+ * @template {Function} T
1063
+ * @typedef {Verb1<T> & Verb2<T> & Verb3<T> & Verb4<T>} Verb
1064
+ */
1053
1065
 
1054
1066
  /**
1067
+ * @template {Function} T
1055
1068
  * @typedef {object} Route
1056
1069
  * @property {Match} [match] 路径匹配
1057
1070
  * @property {null} [router]
1058
- * @property {string} [plugin] 所属插件
1059
- * @property {Handler} handler 处理函数
1071
+ * @property {T[]} handlers 处理函数
1060
1072
  * @property {Set<Method>} methods 方法列表
1061
1073
  */
1062
1074
 
1063
1075
  /**
1076
+ * @template {Function} T
1064
1077
  * @typedef {object} RouterRoute
1065
1078
  * @property {Match} [match] 路径匹配
1066
- * @property {Router} router
1079
+ * @property {Router<T>} router
1067
1080
  */
1068
1081
 
1069
1082
 
1070
1083
  /**
1071
- * @template {Router | Finder} [T=ApiRouter]
1084
+ * @template {Function} T
1085
+ * @template {Router<T> | Finder<T>} [P=MapRouter<T>]
1072
1086
  * @callback RouteBinder
1073
- * @param {T} [router] 要注册的子路由或子路由的 Finder
1074
- * @returns {T extends Finder ? Router : T}
1087
+ * @param {P} [router] 要注册的子路由或子路由的 Finder<T>
1088
+ * @returns {P extends Finder<T> ? Router<T> : P}
1075
1089
  */
1076
1090
  /**
1077
1091
  *
1078
- * @param {(Route | RouterRoute)[]} routes
1092
+ * @template {Function} T
1093
+ * @param {(Route<T> | RouterRoute<T>)[]} routes
1079
1094
  * @param {string | [string[], any[]]} path
1080
- * @param {Router | Finder} [r]
1081
- * @returns {Router}
1095
+ * @param {Router<T> | Finder<T>} [r]
1096
+ * @returns {Router<T>}
1082
1097
  */
1083
1098
  function bindRouter(routes, path, r) {
1099
+ /** @type {Router<T>} */
1084
1100
  const router = r instanceof Router ? r
1085
1101
  : typeof r === 'function' ? Router.create(r)
1086
- : new ApiRouter();
1102
+ : new MapRouter();
1087
1103
  routes.push({ match: toMatch(path, false), router });
1088
1104
  return router;
1089
1105
  }
1090
- class ApiRouter extends Router {
1091
- /** @readonly @type {(Route | RouterRoute)[]} 路由列表 */
1106
+ /**
1107
+ *
1108
+ * @template {Function} T
1109
+ * @extends {Router<T>}
1110
+ */
1111
+ class MapRouter extends Router {
1112
+ /** @readonly @type {(Route<T> | RouterRoute<T>)[]} 路由列表 */
1092
1113
  #routes = [];
1093
1114
  /**
1094
1115
  * 添加子路由
1095
- * @template {Router | Finder} [T=ApiRouter]
1116
+ * @template {Router<T> | Finder<T>} [P=MapRouter<T>]
1096
1117
  * @overload
1097
- * @param {T} [router] 要注册的子路由或子路由的 Finder
1098
- * @returns {T extends Finder ? Router : T}
1118
+ * @param {P} [router] 要注册的子路由或子路由的 Finder<T>
1119
+ * @returns {P extends Finder<T> ? Router<T> : P}
1099
1120
  */
1100
1121
  /**
1101
1122
  * 添加子路由
1102
- * @template {Router | Finder} [T=ApiRouter]
1123
+ * @template {Router<T> | Finder<T>} [P=MapRouter<T>]
1103
1124
  * @overload
1104
1125
  * @param {string} path 要注册的路径
1105
- * @param {T} [router] 要注册的子路由或子路由的 Finder
1106
- * @returns {T extends Finder ? Router : T}
1126
+ * @param {P} [router] 要注册的子路由或子路由的 Finder<T>
1127
+ * @returns {P extends Finder<T> ? Router<T> : P}
1107
1128
  */
1108
1129
  /**
1109
1130
  * 添加子路由
1110
1131
  * @overload
1111
1132
  * @param {TemplateStringsArray} template 要注册的路径模板
1112
1133
  * @param {...any} substitutions 要注册的路径模板代替内容
1113
- * @returns {RouteBinder}
1134
+ * @returns {RouteBinder<T>}
1114
1135
  */
1115
1136
  /**
1116
1137
  * 添加子路由
1117
1138
  * @param {...any} p 要注册的路径
1118
- * @returns {Router | RouteBinder}
1139
+ * @returns {Router<T> | RouteBinder<T>}
1119
1140
  */
1120
1141
  route(...p) {
1121
1142
  const [a] = p;
1122
1143
  if (a && typeof a === 'object' && !(a instanceof Router)) {
1123
1144
  /**
1124
- * @param { Finder | Router} [r];
1145
+ * @param { Finder<T> | Router<T>} [r];
1125
1146
  * @returns {any}
1126
1147
  */
1127
1148
  return r => bindRouter(this.#routes, [a, p.slice(1)], r);
@@ -1134,30 +1155,29 @@ class ApiRouter extends Router {
1134
1155
  *
1135
1156
  * @param {Method} method
1136
1157
  * @param {string[]} path
1137
- * @param {Context} ctx
1138
- * @returns {Iterable<FindItem>}
1158
+ * @returns {Iterable<FindItem<T>>}
1139
1159
  */
1140
- *find(method, path, ctx) {
1160
+ *find(method, path) {
1141
1161
  for (const route of Array.from(this.#routes)) {
1142
1162
  if (!route.router && !route.methods.has(method)) { continue; }
1143
1163
  const {match} = route;
1144
1164
  if (!match) {
1145
1165
  if (route.router || !path.length) {
1146
- yield [route.router || route.handler, {}, path];
1166
+ yield [route.router || route.handlers, {}, path];
1147
1167
  }
1148
1168
  continue;
1149
1169
  }
1150
1170
  if (!path.length) { continue; }
1151
1171
  const result = match(path);
1152
1172
  if (!result) { continue; }
1153
- yield [route.router || route.handler, ...result];
1173
+ yield [route.router || route.handlers, ...result];
1154
1174
  }
1155
1175
  }
1156
1176
  /**
1157
1177
  * 注册处理函数
1158
1178
  * @overload
1159
1179
  * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1160
- * @param {Handler} handler 要注册的处理函数
1180
+ * @param {T} handler 要注册的处理函数
1161
1181
  * @returns {() => void}
1162
1182
  */
1163
1183
  /**
@@ -1165,7 +1185,7 @@ class ApiRouter extends Router {
1165
1185
  * @overload
1166
1186
  * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1167
1187
  * @param {string} path 要注册的路径
1168
- * @param {Handler} handler 要注册的处理函数
1188
+ * @param {T} handler 要注册的处理函数
1169
1189
  * @returns {() => void}
1170
1190
  */
1171
1191
  /**
@@ -1173,48 +1193,58 @@ class ApiRouter extends Router {
1173
1193
  * @overload
1174
1194
  * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1175
1195
  * @param {string} path 要注册的路径
1176
- * @returns {Binder}
1196
+ * @returns {Binder<T>}
1177
1197
  */
1178
1198
  /**
1179
1199
  * @param {Method | Iterable<Method> | ArrayLike<Method>} methods
1180
- * @param {string| Handler} [path]
1181
- * @param {Handler} [handler]
1182
- * @returns {Binder | (() => void)}
1200
+ * @param {string| T} [path]
1201
+ * @param {...T} handler
1202
+ * @returns {Binder<T> | (() => void)}
1183
1203
  */
1184
- verb(methods, path, handler) {
1204
+ verb(methods, path, ...handler) {
1185
1205
  const allMethods = getMethods(methods);
1186
1206
  if (!allMethods.length) { return () => {}; }
1187
- return verb(this.#routes, allMethods, [path, handler]);
1207
+ return verb(this.#routes, allMethods, [path, ...handler]);
1208
+ }
1209
+
1210
+ /**
1211
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1212
+ * @returns {Verb<T>}
1213
+ */
1214
+ method(method) {
1215
+ const methods = getMethods(method);
1216
+ // @ts-ignore
1217
+ return (...p) => verb(this.#routes, methods, p);
1188
1218
  }
1189
1219
  /**
1190
1220
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1191
1221
  * @overload
1192
- * @param {Handler} handler 要注册的处理函数
1222
+ * @param {...T} handlers 要注册的处理函数
1193
1223
  * @returns {() => void}
1194
1224
  */
1195
1225
  /**
1196
1226
  * 注册处理函数
1197
1227
  * @overload
1198
1228
  * @param {string} path 要注册的路径
1199
- * @param {Handler} handler 要注册的处理函数
1229
+ * @param {...T} handlers 要注册的处理函数
1200
1230
  * @returns {() => void}
1201
1231
  */
1202
1232
  /**
1203
1233
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1204
1234
  * @overload
1205
1235
  * @param {string} path 要注册的路径
1206
- * @returns {Binder}
1236
+ * @returns {Binder<T>}
1207
1237
  */
1208
1238
  /**
1209
1239
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1210
1240
  * @overload
1211
1241
  * @param {TemplateStringsArray} template 要注册的路径模板
1212
1242
  * @param {...any} substitutions 要注册的路径模板代替内容
1213
- * @returns {Binder}
1243
+ * @returns {Binder<T>}
1214
1244
  */
1215
1245
  /**
1216
1246
  * @param {...any} p 要注册的路径
1217
- * @returns {Binder | (() => void)}
1247
+ * @returns {Binder<T> | (() => void)}
1218
1248
  */
1219
1249
  match(...p) {
1220
1250
  return verb(this.#routes, ['GET', 'POST', 'PUT', 'DELETE'], p);
@@ -1222,187 +1252,187 @@ class ApiRouter extends Router {
1222
1252
  /**
1223
1253
  * 注册 HTTP GET 处理函数
1224
1254
  * @overload
1225
- * @param {Handler} handler 要注册的处理函数
1255
+ * @param {...Handler} handlers 要注册的处理函数
1226
1256
  * @returns {() => void}
1227
1257
  */
1228
1258
  /**
1229
1259
  * 注册 HTTP GET 处理函数
1230
1260
  * @overload
1231
1261
  * @param {string} path 要注册的路径
1232
- * @param {Handler} handler 要注册的处理函数
1262
+ * @param {...Handler} handlers 要注册的处理函数
1233
1263
  * @returns {() => void}
1234
1264
  */
1235
1265
  /**
1236
1266
  * 注册 HTTP GET 处理函数
1237
1267
  * @overload
1238
1268
  * @param {string} path 要注册的路径
1239
- * @returns {Binder}
1269
+ * @returns {Binder<T>}
1240
1270
  */
1241
1271
  /**
1242
1272
  * 注册 HTTP GET 处理函数
1243
1273
  * @overload
1244
1274
  * @param {TemplateStringsArray} template 要注册的路径模板
1245
1275
  * @param {...any} substitutions 要注册的路径模板代替内容
1246
- * @returns {Binder}
1276
+ * @returns {Binder<T>}
1247
1277
  */
1248
1278
  /**
1249
1279
  * @param {...any} p 要注册的路径
1250
- * @returns {Binder | (() => void)}
1280
+ * @returns {Binder<T> | (() => void)}
1251
1281
  */
1252
1282
  get(...p) { return verb(this.#routes, ['GET'], p); }
1253
1283
  /**
1254
1284
  * 注册 HTTP POST 处理函数
1255
1285
  * @overload
1256
- * @param {Handler} handler 要注册的处理函数
1286
+ * @param {...Handler} handlers 要注册的处理函数
1257
1287
  * @returns {() => void}
1258
1288
  */
1259
1289
  /**
1260
1290
  * 注册 HTTP POST 处理函数
1261
1291
  * @overload
1262
1292
  * @param {string} path 要注册的路径
1263
- * @param {Handler} handler 要注册的处理函数
1293
+ * @param {...Handler} handlers 要注册的处理函数
1264
1294
  * @returns {() => void}
1265
1295
  */
1266
1296
  /**
1267
1297
  * 注册 HTTP POST 处理函数
1268
1298
  * @overload
1269
1299
  * @param {string} path 要注册的路径
1270
- * @returns {Binder}
1300
+ * @returns {Binder<T>}
1271
1301
  */
1272
1302
  /**
1273
1303
  * 注册 HTTP POST 处理函数
1274
1304
  * @overload
1275
1305
  * @param {TemplateStringsArray} template 要注册的路径模板
1276
1306
  * @param {...any} substitutions 要注册的路径模板代替内容
1277
- * @returns {Binder}
1307
+ * @returns {Binder<T>}
1278
1308
  */
1279
1309
  /**
1280
1310
  * @param {...any} p 要注册的路径
1281
- * @returns {Binder | (() => void)}
1311
+ * @returns {Binder<T> | (() => void)}
1282
1312
  */
1283
1313
  post(...p) { return verb(this.#routes, ['POST'], p); }
1284
1314
  /**
1285
1315
  * 注册 HTTP PUT 处理函数
1286
1316
  * @overload
1287
- * @param {Handler} handler 要注册的处理函数
1317
+ * @param {...Handler} handlers 要注册的处理函数
1288
1318
  * @returns {() => void}
1289
1319
  */
1290
1320
  /**
1291
1321
  * 注册 HTTP PUT 处理函数
1292
1322
  * @overload
1293
1323
  * @param {string} path 要注册的路径
1294
- * @param {Handler} handler 要注册的处理函数
1324
+ * @param {...Handler} handlers 要注册的处理函数
1295
1325
  * @returns {() => void}
1296
1326
  */
1297
1327
  /**
1298
1328
  * 注册 HTTP PUT 处理函数
1299
1329
  * @overload
1300
1330
  * @param {string} path 要注册的路径
1301
- * @returns {Binder}
1331
+ * @returns {Binder<T>}
1302
1332
  */
1303
1333
  /**
1304
1334
  * 注册 HTTP PUT 处理函数
1305
1335
  * @overload
1306
1336
  * @param {TemplateStringsArray} template 要注册的路径模板
1307
1337
  * @param {...any} substitutions 要注册的路径模板代替内容
1308
- * @returns {Binder}
1338
+ * @returns {Binder<T>}
1309
1339
  */
1310
1340
  /**
1311
1341
  * @param {...any} p 要注册的路径
1312
- * @returns {Binder | (() => void)}
1342
+ * @returns {Binder<T> | (() => void)}
1313
1343
  */
1314
1344
  put(...p) { return verb(this.#routes, ['PUT'], p); }
1315
1345
  /**
1316
1346
  * 注册 HTTP DELETE 处理函数
1317
1347
  * @overload
1318
- * @param {Handler} handler 要注册的处理函数
1348
+ * @param {...Handler} handlers 要注册的处理函数
1319
1349
  * @returns {() => void}
1320
1350
  */
1321
1351
  /**
1322
1352
  * 注册 HTTP DELETE 处理函数
1323
1353
  * @overload
1324
1354
  * @param {string} path 要注册的路径
1325
- * @param {Handler} handler 要注册的处理函数
1355
+ * @param {...Handler} handlers 要注册的处理函数
1326
1356
  * @returns {() => void}
1327
1357
  */
1328
1358
  /**
1329
1359
  * 注册 HTTP DELETE 处理函数
1330
1360
  * @overload
1331
1361
  * @param {string} path 要注册的路径
1332
- * @returns {Binder}
1362
+ * @returns {Binder<T>}
1333
1363
  */
1334
1364
  /**
1335
1365
  * 注册 HTTP DELETE 处理函数
1336
1366
  * @overload
1337
1367
  * @param {TemplateStringsArray} template 要注册的路径模板
1338
1368
  * @param {...any} substitutions 要注册的路径模板代替内容
1339
- * @returns {Binder}
1369
+ * @returns {Binder<T>}
1340
1370
  */
1341
1371
  /**
1342
1372
  * @param {...any} p 要注册的路径
1343
- * @returns {Binder | (() => void)}
1373
+ * @returns {Binder<T> | (() => void)}
1344
1374
  */
1345
1375
  delete(...p) { return verb(this.#routes, ['DELETE'], p); }
1346
1376
  /**
1347
1377
  * 注册 HTTP HEAD 处理函数
1348
1378
  * @overload
1349
- * @param {Handler} handler 要注册的处理函数
1379
+ * @param {...Handler} handlers 要注册的处理函数
1350
1380
  * @returns {() => void}
1351
1381
  */
1352
1382
  /**
1353
1383
  * 注册 HTTP HEAD 处理函数
1354
1384
  * @overload
1355
1385
  * @param {string} path 要注册的路径
1356
- * @param {Handler} handler 要注册的处理函数
1386
+ * @param {...Handler} handlers 要注册的处理函数
1357
1387
  * @returns {() => void}
1358
1388
  */
1359
1389
  /**
1360
1390
  * 注册 HTTP HEAD 处理函数
1361
1391
  * @overload
1362
1392
  * @param {string} path 要注册的路径
1363
- * @returns {Binder}
1393
+ * @returns {Binder<T>}
1364
1394
  */
1365
1395
  /**
1366
1396
  * 注册 HTTP HEAD 处理函数
1367
1397
  * @overload
1368
1398
  * @param {TemplateStringsArray} template 要注册的路径模板
1369
1399
  * @param {...any} substitutions 要注册的路径模板代替内容
1370
- * @returns {Binder}
1400
+ * @returns {Binder<T>}
1371
1401
  */
1372
1402
  /**
1373
1403
  * @param {...any} p 要注册的路径
1374
- * @returns {Binder | (() => void)}
1404
+ * @returns {Binder<T> | (() => void)}
1375
1405
  */
1376
1406
  head(...p) { return verb(this.#routes, ['HEAD'], p); }
1377
1407
  /**
1378
1408
  * 注册 HTTP OPTIONS 处理函数
1379
1409
  * @overload
1380
- * @param {Handler} handler 要注册的处理函数
1410
+ * @param {...Handler} handlers 要注册的处理函数
1381
1411
  * @returns {() => void}
1382
1412
  */
1383
1413
  /**
1384
1414
  * 注册 HTTP OPTIONS 处理函数
1385
1415
  * @overload
1386
1416
  * @param {string} path 要注册的路径
1387
- * @param {Handler} handler 要注册的处理函数
1417
+ * @param {...Handler} handlers 要注册的处理函数
1388
1418
  * @returns {() => void}
1389
1419
  */
1390
1420
  /**
1391
1421
  * 注册 HTTP OPTIONS 处理函数
1392
1422
  * @overload
1393
1423
  * @param {string} path 要注册的路径
1394
- * @returns {Binder}
1424
+ * @returns {Binder<T>}
1395
1425
  */
1396
1426
  /**
1397
1427
  * 注册 HTTP OPTIONS 处理函数
1398
1428
  * @overload
1399
1429
  * @param {TemplateStringsArray} template 要注册的路径模板
1400
1430
  * @param {...any} substitutions 要注册的路径模板代替内容
1401
- * @returns {Binder}
1431
+ * @returns {Binder<T>}
1402
1432
  */
1403
1433
  /**
1404
1434
  * @param {...any} p 要注册的路径
1405
- * @returns {Binder | (() => void)}
1435
+ * @returns {Binder<T> | (() => void)}
1406
1436
  */
1407
1437
  options(...p) { return verb(this.#routes, ['OPTIONS'], p); }
1408
1438
  }
@@ -1430,30 +1460,6 @@ function createFetch(run, notFound) {
1430
1460
  };
1431
1461
  }
1432
1462
 
1433
- /** @import { Context, Handler, HandlerResult } from './main/types' */
1434
- /**
1435
- * @callback Onionskin
1436
- * @param {Context} ctx
1437
- * @param {() => Promise<HandlerResult>} next
1438
- * @returns {PromiseLike<HandlerResult> | HandlerResult}
1439
- */
1440
-
1441
- const noop = () => {};
1442
- /**
1443
- *
1444
- * @param {...(Onionskin | Onionskin[])} handlers
1445
- * @returns {Handler}
1446
- */
1447
- function onionskin(...handlers) {
1448
- /** @type {Handler} */
1449
- let handler = noop;
1450
- for (const os of handlers.flat()) {
1451
- const currentHandler = handler;
1452
- handler = async ctx => os(ctx, async () => currentHandler(ctx));
1453
- }
1454
- return handler;
1455
- }
1456
-
1457
1463
  /** @import { Context } from './main/types.js' */
1458
1464
  class Param {
1459
1465
  #symbol = Symbol();
@@ -1490,4 +1496,4 @@ class Param {
1490
1496
  }
1491
1497
  }
1492
1498
 
1493
- export { ApiRouter, Param, Router, createFetch, main, make, merge, onionskin, packer, service, stateService, storeService };
1499
+ export { MapRouter, Param, Router, bind$1 as bind, createFetch, main, make, service, stateService, storeService };