k99 0.6.1 → 0.7.1

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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * k99 v0.6.1
2
+ * k99 v0.7.1
3
3
  * (c) 2019-2025 猛火Fierflame
4
4
  * @license MIT
5
5
  */
@@ -106,8 +106,7 @@ function toBodyData(result, aborted) {
106
106
  if (!data) { continue; }
107
107
  await write(writer, data);
108
108
  }
109
- await writable.close();
110
- })().catch(() => {});
109
+ })().then(() => writable.close(), r => writable.abort(r)).catch(() => {});
111
110
  return [readable, 0, ''];
112
111
  }
113
112
  /**
@@ -130,11 +129,12 @@ function toBody(result, headers, aborted) {
130
129
  return body;
131
130
  }
132
131
 
132
+ /** @import { Cookie, CookieOption } from './types' */
133
133
  /**
134
134
  *
135
- * @param {import('./types').Cookie[]} sentCookies
135
+ * @param {Cookie[]} sentCookies
136
136
  * @param {string} [name]
137
- * @returns {Iterable<import('./types').Cookie>}
137
+ * @returns {Iterable<Cookie>}
138
138
  */
139
139
  function *getCookie(sentCookies, name) {
140
140
  const list = sentCookies;
@@ -147,7 +147,7 @@ function *getCookie(sentCookies, name) {
147
147
  /**
148
148
  *
149
149
  * @param {Headers} headers
150
- * @param {import('./types').Cookie[]} cookies
150
+ * @param {Cookie[]} cookies
151
151
  * @returns {void}
152
152
  */
153
153
  function setCookiesHeader(headers, cookies) {
@@ -181,10 +181,10 @@ function getRequestCookies(cookie) {
181
181
  }
182
182
  /**
183
183
  *
184
- * @param {import('./types').Cookie[]} sentCookies
184
+ * @param {Cookie[]} sentCookies
185
185
  * @param {Record<string, string>} cookies
186
- * @param {string | import('./types').CookieOption} [name]
187
- * @param {import('./types').CookieOption | boolean} [opt]
186
+ * @param {string | CookieOption} [name]
187
+ * @param {CookieOption | boolean} [opt]
188
188
  * @returns {void}
189
189
  */
190
190
  function clearCookie(
@@ -196,11 +196,11 @@ function clearCookie(
196
196
  let expire = 'Fri, 31 Dec 1999 16:00:00 GMT';
197
197
  if (typeof name === 'string') {
198
198
  if (!name) { return; }
199
- /** @type {import('./types').CookieOption} */
199
+ /** @type {CookieOption} */
200
200
  const { domain, path, secure, httpOnly } = opt !== true && opt || {};
201
201
  sentCookies.push({ name, value: 'delete', expire, domain, path, secure, httpOnly });
202
202
  } else {
203
- /** @type {import('./types').CookieOption} */
203
+ /** @type {CookieOption} */
204
204
  const { domain, path, secure, httpOnly } = name || {};
205
205
  sentCookies.length = 0;
206
206
  if (opt) {
@@ -211,6 +211,9 @@ function clearCookie(
211
211
  }
212
212
  }
213
213
 
214
+ /** @import { Context, Cookie, CookieOption, FindHandler, Method, Options, Params, Service } from './types' */
215
+
216
+
214
217
  const noBodyMethods = new Set(['GET', 'OPTIONS']);
215
218
  /**
216
219
  *
@@ -247,7 +250,7 @@ function setHeader(headers, name, value) {
247
250
  *
248
251
  * @param {Request} request
249
252
  * @param {string | ((request: Request) => string)} [toMethod]
250
- * @returns {import('./types').Method}
253
+ * @returns {Method}
251
254
  */
252
255
  function getMethod(request, toMethod) {
253
256
  let methodStr = '';
@@ -259,13 +262,13 @@ function getMethod(request, toMethod) {
259
262
  if (!methodStr || typeof methodStr !== 'string') {
260
263
  methodStr = request.method || 'GET';
261
264
  }
262
- return /** @type {import('./types').Method} */(methodStr.toUpperCase());
265
+ return /** @type {Method} */(methodStr.toUpperCase());
263
266
  }
264
267
  /**
265
268
  *
266
269
  * @param {Request} request
267
- * @param {import('./types').FindHandler} getHandler
268
- * @param {import('./types').Options} [options]
270
+ * @param {FindHandler} getHandler
271
+ * @param {Options} [options]
269
272
  * @returns {Promise<Response | null>}
270
273
  */
271
274
  function main(
@@ -275,7 +278,7 @@ function main(
275
278
  /**
276
279
  *
277
280
  * @param {Request} request
278
- * @param {import('./types').Context} [parent]
281
+ * @param {Context} [parent]
279
282
  * @returns {Promise<Response | null>}
280
283
  */
281
284
  function exec(request, parent) {
@@ -283,10 +286,10 @@ function main(
283
286
  const url = new URL(request.url);
284
287
  const { signal, headers } = request;
285
288
  const aborted = signal2promise(signal);
286
- /** @type {Map<import('./types').Service<any, any>, Function>} */
289
+ /** @type {Map<Service<any, any>, Function>} */
287
290
  const services = new Map();
288
291
  const cookies = getRequestCookies(headers.get('cookie') || '');
289
- /** @type {import('./types').Cookie[]} */
292
+ /** @type {Cookie[]} */
290
293
  const sentCookies = [];
291
294
  const responseHeaders = new Headers();
292
295
  const root = parent?.root;
@@ -302,9 +305,9 @@ function main(
302
305
  const donePromise = new Promise((a, b) => { resolve = a; reject = b; });
303
306
  donePromise.catch(() => {});
304
307
 
305
- /** @type {any} */
308
+ /** @type {Params} */
306
309
  let params = {};
307
- /** @type {import('./types').Context} */
310
+ /** @type {Context} */
308
311
  const context = {
309
312
  environment,
310
313
  parent,
@@ -368,8 +371,8 @@ function main(
368
371
  },
369
372
  /**
370
373
  *
371
- * @param {string | import('./types').CookieOption} [name]
372
- * @param {import('./types').CookieOption | boolean} [opt]
374
+ * @param {string | CookieOption} [name]
375
+ * @param {CookieOption | boolean} [opt]
373
376
  * @returns {void}
374
377
  */
375
378
  clearCookie(name, opt) {
@@ -406,69 +409,72 @@ function main(
406
409
  return exec(request);
407
410
  }
408
411
 
412
+ /** @import { FindHandler, Options } from './main/types' */
413
+
409
414
  /**
410
415
  *
411
- * @param {import('./main/types').FindHandler} getHandler
412
- * @param {import('./main/types').Options} options
416
+ * @param {FindHandler} getHandler
417
+ * @param {Options} options
413
418
  * @returns {(request: Request) => Promise<Response | null>}
414
419
  */
415
420
  function make(getHandler, options) {
416
421
  return r => main(r, getHandler, options);
417
422
  }
418
423
 
424
+ /** @import { Context, Handler } from './main/types' */
419
425
  /**
420
426
  *
421
- * @param {import('./main/types').Context} context
422
- * @param {import('./main/types').Handler[]} handlers
427
+ * @param {Context} context
428
+ * @param {Handler[]} handlers
423
429
  * @returns {Promise<string | boolean | object | undefined>}
424
430
  */
425
431
  async function runHandles(context, handlers) {
426
432
  for (const handle of handlers) {
427
433
  const result = await handle(context);
428
- if (typeof result === 'boolean') { return result; }
429
- if (!result) { continue; }
434
+ if (result === undefined) { continue; }
430
435
  return result;
431
436
  }
432
437
  }
433
438
 
434
439
  /**
435
440
  *
436
- * @param {...(import('./main/types').Handler | import('./main/types').Handler[])} handlers
437
- * @returns {import('./main/types').Handler}
441
+ * @param {...(Handler | Handler[])} handlers
442
+ * @returns {Handler}
438
443
  */
439
444
  function merge(...handlers) {
440
445
  return ctx => runHandles(ctx, handlers.flat());
441
446
  }
442
447
 
448
+ /** @import { Context, Service } from './main/types' */
443
449
  /**
444
450
  *
445
451
  * @template T
446
452
  * @template {any[]} P
447
453
  * @overload
448
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
449
- * @param {((ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
450
- * @param {import('./main/types').Service.Options?} [options]
451
- * @returns {import('./main/types').Service<T, P>}
454
+ * @param {(ctx: Context, ...p: P) => T} exec
455
+ * @param {((ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
456
+ * @param {Service.Options?} [options]
457
+ * @returns {Service<T, P>}
452
458
  */
453
459
  /**
454
460
  *
455
461
  * @template T
456
462
  * @template {any[]} P
457
463
  * @overload
458
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
459
- * @param {import('./main/types').Service.Options?} [options]
460
- * @returns {import('./main/types').Service<T, P>}
464
+ * @param {(ctx: Context, ...p: P) => T} exec
465
+ * @param {Service.Options?} [options]
466
+ * @returns {Service<T, P>}
461
467
  */
462
468
  /**
463
469
  * @template T
464
470
  * @template {any[]} P
465
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
466
- * @param {((ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
467
- * @param {import('./main/types').Service.Options?} [options]
468
- * @returns {import('./main/types').Service<T, P>}
471
+ * @param {(ctx: Context, ...p: P) => T} exec
472
+ * @param {((ctx: Context, error?: unknown) => PromiseLike<void> | void) | Service.Options | null} [destroy]
473
+ * @param {Service.Options?} [options]
474
+ * @returns {Service<T, P>}
469
475
  */
470
476
  function service(exec, destroy, options) {
471
- /** @type {import('./main/types').Service<T, P>} */
477
+ /** @type {Service<T, P>} */
472
478
  const service = function (ctx) {
473
479
  if (typeof destroy === 'function') {
474
480
  ctx.done(() => destroy(ctx), error => destroy(ctx, error));
@@ -487,43 +493,44 @@ function service(exec, destroy, options) {
487
493
  return service;
488
494
  }
489
495
 
496
+ /** @import { Context, Service, StateService } from './main/types' */
490
497
  /**
491
498
  *
492
499
  * @template T
493
500
  * @overload
494
- * @param {(ctx: import('./main/types').Context) => T} init
495
- * @param {import('./main/types').Service.Options} [options]
496
- * @returns {import('./main/types').StateService<T>}
501
+ * @param {(ctx: Context) => T} init
502
+ * @param {Service.Options} [options]
503
+ * @returns {StateService<T>}
497
504
  */
498
505
  /**
499
506
  *
500
507
  * @template T
501
508
  * @overload
502
- * @param {(ctx: import('./main/types').Context) => T} init
503
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
504
- * @param {import('./main/types').Service.Options?} [options]
505
- * @returns {import('./main/types').StateService<T>}
509
+ * @param {(ctx: Context) => T} init
510
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
511
+ * @param {Service.Options?} [options]
512
+ * @returns {StateService<T>}
506
513
  */
507
514
  /**
508
515
  *
509
516
  * @template T
510
517
  * @overload
511
- * @param {(ctx: import('./main/types').Context) => T} init
512
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
513
- * @param {((state: T, ctx: import('./main/types').Context) => any)?} [exec]
514
- * @param {import('./main/types').Service.Options?} [options]
515
- * @returns {import('./main/types').StateService<T>}
518
+ * @param {(ctx: Context) => T} init
519
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
520
+ * @param {((state: T, ctx: Context) => any)?} [exec]
521
+ * @param {Service.Options?} [options]
522
+ * @returns {StateService<T>}
516
523
  */
517
524
  /**
518
525
  * @template T
519
- * @param {(ctx: import('./main/types').Context) => T} init
520
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
521
- * @param {((state: T, ctx: import('./main/types').Context) => any) | import('./main/types').Service.Options | null} [exec]
522
- * @param {import('./main/types').Service.Options?} [options]
523
- * @returns {import('./main/types').StateService<T>}
526
+ * @param {(ctx: Context) => T} init
527
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void) | Service.Options | null} [destroy]
528
+ * @param {((state: T, ctx: Context) => any) | Service.Options | null} [exec]
529
+ * @param {Service.Options?} [options]
530
+ * @returns {StateService<T>}
524
531
  */
525
532
  function stateService(init, destroy, exec, options) {
526
- /** @type {import('./main/types').StateService<T>} */
533
+ /** @type {StateService<T>} */
527
534
  const service = function (ctx) {
528
535
  const state = init(ctx) || /** @type {T} */({});
529
536
  if (typeof destroy === 'function') {
@@ -547,39 +554,40 @@ function stateService(init, destroy, exec, options) {
547
554
  return service;
548
555
  }
549
556
 
557
+ /** @import { Context, Service, StoreService } from './main/types' */
550
558
  /**
551
559
  *
552
560
  * @template T
553
561
  * @overload
554
- * @param {import('./main/types').Service.Options?} [options]
555
- * @returns {import('./main/types').StoreService<T>}
562
+ * @param {Service.Options?} [options]
563
+ * @returns {StoreService<T>}
556
564
  */
557
565
  /**
558
566
  *
559
567
  * @template T
560
568
  * @overload
561
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
562
- * @param {import('./main/types').Service.Options?} [options]
563
- * @returns {import('./main/types').StoreService<T>}
569
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
570
+ * @param {Service.Options?} [options]
571
+ * @returns {StoreService<T>}
564
572
  */
565
573
  /**
566
574
  *
567
575
  * @template T
568
576
  * @overload
569
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
570
- * @param {((state: T | undefined, ctx: import('./main/types').Context) => any)?} [exec]
571
- * @param {import('./main/types').Service.Options?} [options]
572
- * @returns {import('./main/types').StoreService<T>}
577
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
578
+ * @param {((state: T | undefined, ctx: Context) => any)?} [exec]
579
+ * @param {Service.Options?} [options]
580
+ * @returns {StoreService<T>}
573
581
  */
574
582
  /**
575
583
  * @template T
576
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
577
- * @param {((state: T | undefined, ctx: import('./main/types').Context) => any) | import('./main/types').Service.Options | null} [exec]
578
- * @param {import('./main/types').Service.Options?} [options]
579
- * @returns {import('./main/types').StoreService<T>}
584
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void) | Service.Options | null} [destroy]
585
+ * @param {((state: T | undefined, ctx: Context) => any) | Service.Options | null} [exec]
586
+ * @param {Service.Options?} [options]
587
+ * @returns {StoreService<T>}
580
588
  */
581
589
  function storeService(destroy, exec, options) {
582
- /** @type {import('./main/types').StoreService<T>} */
590
+ /** @type {StoreService<T>} */
583
591
  const service = function (ctx) {
584
592
  /** @type {T | undefined} */
585
593
  let state;
@@ -616,16 +624,18 @@ function storeService(destroy, exec, options) {
616
624
  return service;
617
625
  }
618
626
 
627
+ /** @import { Handler } from './main/types' */
628
+ /** @import { Onionskin } from './onionskin.mjs' */
619
629
  /**
620
630
  * @callback Packer
621
- * @param {import('./main/types').Handler} handler
622
- * @returns {import('./main/types').Handler}
631
+ * @param {Handler} handler
632
+ * @returns {Handler}
623
633
  */
624
634
  /** @type {Packer} */
625
635
  const noop$1 = h => h;
626
636
  /**
627
637
  *
628
- * @param {import('./onionskin.mjs').Onionskin} onionskin
638
+ * @param {Onionskin} onionskin
629
639
  * @param {Packer} [packer]
630
640
  * @returns {Packer}
631
641
  */
@@ -636,30 +646,33 @@ function packer(onionskin, packer = noop$1) {
636
646
  };
637
647
  }
638
648
 
649
+ /** @import { Context, FindHandler, Handler, Method, Params } from './main/types' */
650
+ /** @import { Onionskin } from './onionskin.mjs' */
651
+
639
652
  /**
640
653
  * @callback Guard
641
- * @param {import('./main/types').Context} ctx
642
- * @returns {PromiseLike<boolean | import('./main/types').Handler | void> | boolean | import('./main/types').Handler | void}
654
+ * @param {Context} ctx
655
+ * @returns {PromiseLike<boolean | Handler | void> | boolean | Handler | void}
643
656
  */
644
657
  /**
645
- * @typedef {[import('./main/types').Handler | Router, Record<string, any>, string[]]} FindItem
658
+ * @typedef {[Handler | Router, Record<string | symbol, any>, string[]]} FindItem
646
659
  */
647
660
  /**
648
661
  * @callback Finder
649
662
  * @this {Router}
650
- * @param {import('./main/types').Method} method
663
+ * @param {Method} method
651
664
  * @param {string[]} path
652
- * @param {import('./main/types').Context} ctx
665
+ * @param {Context} ctx
653
666
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
654
667
  */
655
668
 
656
669
  /**
657
670
  *
658
671
  * @param {Set<Guard>} guards
659
- * @param {import('./main/types').Context} ctx
672
+ * @param {Context} ctx
660
673
  * @param {(v: any) => void} setParams
661
674
  * @param {object} params
662
- * @returns {Promise<boolean | import('./main/types').Handler>}
675
+ * @returns {Promise<boolean | Handler>}
663
676
  */
664
677
  async function execGuard(guards, ctx, setParams, params) {
665
678
  if (!guards.size) { return true; }
@@ -678,12 +691,12 @@ async function execGuard(guards, ctx, setParams, params) {
678
691
 
679
692
  /**
680
693
  *
681
- * @param {Router | import('./main/types').Handler} route
694
+ * @param {Router | Handler} route
682
695
  * @param {string[]} path
683
- * @param {import('./main/types').Context} ctx
684
- * @param {(v: any) => void} setParams
685
- * @param {object} params
686
- * @returns {Promise<import('./main/types').Handler | null>}
696
+ * @param {Context} ctx
697
+ * @param {(v: Params) => void} setParams
698
+ * @param {Params} params
699
+ * @returns {Promise<Handler | null>}
687
700
  */
688
701
  async function find(route, path, ctx, setParams, params) {
689
702
  if (!(route instanceof Router)) {
@@ -722,16 +735,16 @@ class Router {
722
735
  disabled = false;
723
736
  /**
724
737
  * @abstract
725
- * @param {import('./main/types').Method} method
738
+ * @param {Method} method
726
739
  * @param {string[]} path
727
- * @param {import('./main/types').Context} ctx
740
+ * @param {Context} ctx
728
741
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
729
742
  */
730
743
  find(method, path, ctx) { return []; }
731
744
  /**
732
745
  *
733
746
  * @param {Router[]} routers
734
- * @returns {import('./main/types').FindHandler}
747
+ * @returns {FindHandler}
735
748
  */
736
749
  static make(routers) {
737
750
  return async (ctx, setParams) => {
@@ -759,19 +772,21 @@ class Router {
759
772
  guards = new Set();
760
773
  /**
761
774
  *
762
- * @param {import('./main/types').Handler} h
763
- * @returns {import('./main/types').Handler}
775
+ * @param {Handler} h
776
+ * @returns {Handler}
764
777
  */
765
778
  __onionskin = (h) => h;
766
- /** @param {import('./onionskin.mjs').Onionskin} os */
779
+ /** @param {Onionskin} os */
767
780
  onionskin(os) { this.__onionskin = packer(os, this.__onionskin); }
768
781
  }
769
782
 
783
+ /** @import { Match } from './index.mjs' */
784
+ /** @import { Params } from '../main/types.js' */
770
785
  /**
771
786
  * @typedef {object} Pattern
772
- * @property {string} name
773
- * @property {boolean} optional
774
- * @property {boolean} many
787
+ * @property {string | symbol} name
788
+ * @property {boolean} [optional]
789
+ * @property {boolean} [many]
775
790
  * @property {RegExp} pattern
776
791
  */
777
792
 
@@ -842,7 +857,7 @@ function parse(p) {
842
857
  * @returns {[Record<string, string | string[]>, string[]] | undefined}
843
858
  */
844
859
  function exec(match, path, end) {
845
- /** @type {Record<string, string | string[]>} */
860
+ /** @type {Params} */
846
861
  const params = {};
847
862
  for (let i = 0; i < match.length; i++) {
848
863
  const m = match[i];
@@ -865,38 +880,102 @@ function exec(match, path, end) {
865
880
  return [params, []];
866
881
 
867
882
  }
883
+ /**
884
+ *
885
+ * @param {string[]} paths
886
+ * @param {*} values
887
+ * @returns {Iterable<[string[], any[]]>}
888
+ */
889
+ function* split([...paths], [...values]) {
890
+ let els = (paths.shift() || '').split('/');
891
+ let list = [els.pop() || ''];
892
+ for (const f of els) {
893
+ yield [[f], []];
894
+ }
895
+ for (const path of paths) {
896
+ const els = path.split('/');
897
+ if (els.length <= 1) {
898
+ list.push(path);
899
+ continue;
900
+ }
901
+ const pathValue = values.splice(0, list.length);
902
+ list.push(els.shift() || '');
903
+ yield [list, pathValue];
904
+ list = [els.pop() || ''];
905
+ for (const f of els) {
906
+ yield [[f], []];
907
+ }
908
+ }
909
+ yield [list, values];
910
+ }
868
911
  /**
869
912
  *
870
- * @param {string} path
913
+ * @param {string | [string[], any[]]} path
871
914
  * @param {boolean} end
872
- * @returns {import('./index.mjs').Match | undefined}
915
+ * @returns {Match | undefined}
873
916
  */
874
917
  function toMatch(path, end) {
875
918
  /** @type {(Pattern | string)[]} */
876
919
  const list = [];
877
- for (const p of path.split('/')) {
878
- if (!p || /^\.+$/.test(p)) { continue; }
879
- list.push(parse(p));
920
+ if (typeof path === 'string') {
921
+ for (const p of path.split('/')) {
922
+ if (!p || /^\.+$/.test(p)) { continue; }
923
+ list.push(parse(p));
924
+ }
925
+ } else {
926
+ for (const [paths, values] of split(...path)) {
927
+ if (paths.length === 2 && !paths[0]) {
928
+ const modifier = paths[1];
929
+ if (['', '?', '+', '*'].includes(modifier)) {
930
+ const value = values[0];
931
+ if (typeof value === 'symbol') {
932
+ list.push({
933
+ name: value, pattern: /^.*$/,
934
+ optional: modifier === '?' || modifier === '*',
935
+ many: modifier === '+' || modifier === '*',
936
+ });
937
+ continue;
938
+ } else if (value && typeof value === 'object') {
939
+ const {name, pattern} = value;
940
+ if (typeof name === 'symbol') {
941
+ list.push({
942
+ name, pattern: pattern instanceof RegExp ? pattern : /^.*$/,
943
+ optional: modifier === '?' || modifier === '*',
944
+ many: modifier === '+' || modifier === '*',
945
+ });
946
+ continue;
947
+ }
948
+ }
949
+ }
950
+ }
951
+ const last = paths.pop() || '';
952
+ const strings = paths.map((v, i) => [v, values[i]]).flat();
953
+ strings.push(last);
954
+ const p = strings.join('');
955
+ if (!p || /^\.+$/.test(p)) { continue; }
956
+ list.push(parse(p));
957
+ continue;
958
+ }
959
+
880
960
  }
881
961
  if (!list.length) { return; }
882
962
  return path => exec(list, path, end);
883
963
  }
884
964
 
965
+ /** @import { Handler, Method } from '../main/types' */
966
+ /** @import { Binder, Match, Route, RouterRoute } from './index.mjs' */
967
+
885
968
  /**
886
969
  *
887
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
888
- * @param {import('../main/types').Method[]} methods
889
- * @param {string} path
890
- * @param {import('../main/types').Handler} handler
970
+ * @param {(Route | RouterRoute)[]} routes
971
+ * @param {Set<Method>} methods
972
+ * @param {Match | undefined} match
973
+ * @param {Handler[]} handlers
891
974
  * @returns {() => void}
892
975
  */
893
- function bind(routes, methods, path, handler) {
894
- /** @type {import('./index.mjs').Route} */
895
- const route = {
896
- match: toMatch(path || '', true),
897
- methods: new Set(methods),
898
- handler,
899
- };
976
+ function bind(routes, methods, match, handlers) {
977
+ /** @type {Route} */
978
+ const route = { match, methods, handler: ctx => runHandles(ctx, handlers) };
900
979
  routes.push(route);
901
980
  let removed = false;
902
981
  return () => {
@@ -907,43 +986,49 @@ function bind(routes, methods, path, handler) {
907
986
  routes.splice(index, 1);
908
987
  };
909
988
  }
910
- /** @type {(v: any) => v is import('../main/types').Handler} */
989
+ /** @type {(v: any) => v is Handler} */
911
990
  const findHandler = v => typeof v === 'function';
912
991
  /**
913
992
  *
914
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
915
- * @param {import('../main/types').Method[]} methods
993
+ * @param {(Route | RouterRoute)[]} routes
994
+ * @param {Iterable<Method>} methods
916
995
  * @param {any[]} p
917
- * @returns {import('./index.mjs').Binder | (() => void)}
996
+ * @returns {Binder | (() => void)}
918
997
  */
919
998
  function verb(routes, methods, p) {
999
+ const methodSet = new Set(methods);
920
1000
  if (!p.length) {
921
- return handler => bind(routes, methods, '', handler);
1001
+ const match = undefined;
1002
+ /** @type {Binder} */
1003
+ return (...handlers) => bind(routes, methodSet, match, handlers);
922
1004
  }
923
- const [a, b] = p;
924
- if (a && typeof a === 'object') {
925
- const path = String.raw(a, ...p.slice(1));
926
- return handler => bind(routes, methods, path, handler);
1005
+ const [path] = p;
1006
+ if (path && typeof path === 'object') {
1007
+ const match = toMatch([path, p.slice(1)], true);
1008
+ /** @type {Binder} */
1009
+ return (...handlers) => bind(routes, methodSet, match, handlers);
927
1010
  }
928
- const path = typeof a === 'string' ? a : '';
929
- const handler = [a, b].find(findHandler);
930
- if (!handler) {
931
- return handler => bind(routes, methods, path, handler);
1011
+ const match = toMatch(typeof path === 'string' ? path : '', true);
1012
+ const handlers = p.filter(findHandler);
1013
+ if (!handlers.length) {
1014
+ /** @type {Binder} */
1015
+ return (...handlers) => bind(routes, methodSet, match, handlers);
932
1016
  }
933
- return bind(routes, methods, path, handler);
1017
+ return bind(routes, methodSet, match, handlers);
934
1018
  }
935
1019
 
1020
+ /** @import { Method } from '../main/types' */
936
1021
  const methods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']);
937
1022
  /**
938
1023
  *
939
1024
  * @param {any} v
940
- * @returns {v is import('../main/types').Method}
1025
+ * @returns {v is Method}
941
1026
  */
942
1027
  function isMethod(v) { return methods.has(v); }
943
1028
  /**
944
1029
  *
945
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} [methods]
946
- * @returns {import('../main/types').Method[]}
1030
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} [methods]
1031
+ * @returns {Method[]}
947
1032
  */
948
1033
  function getMethods(methods) {
949
1034
  if (!methods) {
@@ -957,25 +1042,24 @@ function getMethods(methods) {
957
1042
  .filter(isMethod);
958
1043
  }
959
1044
 
1045
+ /** @import { Context, Handler, Method, Params } from '../main/types' */
1046
+ /** @import { Finder, FindItem } from '../Router.mjs' */
1047
+
960
1048
  /**
961
1049
  * @callback Match
962
1050
  * @param {string[]} paths
963
- * @returns {[Record<string, string | string[]>, string[]] | undefined}
1051
+ * @returns {[Params, string[]] | undefined}
964
1052
  */
965
1053
 
966
- /**
967
- * @callback Binder
968
- * @param {import('../main/types').Handler} handler
969
- * @returns {() => void}
970
- */
1054
+ /** @typedef {(handler: Handler, ...handlers: Handler[]) => () => void} Binder */
971
1055
 
972
1056
  /**
973
1057
  * @typedef {object} Route
974
1058
  * @property {Match} [match] 路径匹配
975
1059
  * @property {null} [router]
976
1060
  * @property {string} [plugin] 所属插件
977
- * @property {import('../main/types').Handler} handler 处理函数
978
- * @property {Set<import('../main/types').Method>} methods 方法列表
1061
+ * @property {Handler} handler 处理函数
1062
+ * @property {Set<Method>} methods 方法列表
979
1063
  */
980
1064
 
981
1065
  /**
@@ -986,16 +1070,16 @@ function getMethods(methods) {
986
1070
 
987
1071
 
988
1072
  /**
989
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1073
+ * @template {Router | Finder} [T=ApiRouter]
990
1074
  * @callback RouteBinder
991
1075
  * @param {T} [router] 要注册的子路由或子路由的 Finder
992
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1076
+ * @returns {T extends Finder ? Router : T}
993
1077
  */
994
1078
  /**
995
1079
  *
996
1080
  * @param {(Route | RouterRoute)[]} routes
997
- * @param {string} path
998
- * @param {Router | import('../Router.mjs').Finder} [r]
1081
+ * @param {string | [string[], any[]]} path
1082
+ * @param {Router | Finder} [r]
999
1083
  * @returns {Router}
1000
1084
  */
1001
1085
  function bindRouter(routes, path, r) {
@@ -1010,18 +1094,18 @@ class ApiRouter extends Router {
1010
1094
  #routes = [];
1011
1095
  /**
1012
1096
  * 添加子路由
1013
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1097
+ * @template {Router | Finder} [T=ApiRouter]
1014
1098
  * @overload
1015
1099
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1016
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1100
+ * @returns {T extends Finder ? Router : T}
1017
1101
  */
1018
1102
  /**
1019
1103
  * 添加子路由
1020
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1104
+ * @template {Router | Finder} [T=ApiRouter]
1021
1105
  * @overload
1022
1106
  * @param {string} path 要注册的路径
1023
1107
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1024
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1108
+ * @returns {T extends Finder ? Router : T}
1025
1109
  */
1026
1110
  /**
1027
1111
  * 添加子路由
@@ -1038,12 +1122,11 @@ class ApiRouter extends Router {
1038
1122
  route(...p) {
1039
1123
  const [a] = p;
1040
1124
  if (a && typeof a === 'object' && !(a instanceof Router)) {
1041
- const path = String.raw(a, ...p.slice(1));
1042
1125
  /**
1043
- * @param { import('../Router.mjs').Finder | Router} [r];
1126
+ * @param { Finder | Router} [r];
1044
1127
  * @returns {any}
1045
1128
  */
1046
- return r => bindRouter(this.#routes, path, r);
1129
+ return r => bindRouter(this.#routes, [a, p.slice(1)], r);
1047
1130
  }
1048
1131
  const path = typeof a === 'string' ? a : '';
1049
1132
  const r = typeof a === 'string' ? p[1] : a;
@@ -1051,10 +1134,10 @@ class ApiRouter extends Router {
1051
1134
  }
1052
1135
  /**
1053
1136
  *
1054
- * @param {import('../main/types').Method} method
1137
+ * @param {Method} method
1055
1138
  * @param {string[]} path
1056
- * @param {import('../main/types').Context} ctx
1057
- * @returns {Iterable<import('../Router.mjs').FindItem>}
1139
+ * @param {Context} ctx
1140
+ * @returns {Iterable<FindItem>}
1058
1141
  */
1059
1142
  *find(method, path, ctx) {
1060
1143
  for (const route of Array.from(this.#routes)) {
@@ -1075,29 +1158,29 @@ class ApiRouter extends Router {
1075
1158
  /**
1076
1159
  * 注册处理函数
1077
1160
  * @overload
1078
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1079
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1161
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1162
+ * @param {Handler} handler 要注册的处理函数
1080
1163
  * @returns {() => void}
1081
1164
  */
1082
1165
  /**
1083
1166
  * 注册处理函数
1084
1167
  * @overload
1085
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1168
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1086
1169
  * @param {string} path 要注册的路径
1087
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1170
+ * @param {Handler} handler 要注册的处理函数
1088
1171
  * @returns {() => void}
1089
1172
  */
1090
1173
  /**
1091
1174
  * 注册处理函数
1092
1175
  * @overload
1093
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1176
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1094
1177
  * @param {string} path 要注册的路径
1095
1178
  * @returns {Binder}
1096
1179
  */
1097
1180
  /**
1098
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} methods
1099
- * @param {string| import('../main/types').Handler} [path]
1100
- * @param {import('../main/types').Handler} [handler]
1181
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} methods
1182
+ * @param {string| Handler} [path]
1183
+ * @param {Handler} [handler]
1101
1184
  * @returns {Binder | (() => void)}
1102
1185
  */
1103
1186
  verb(methods, path, handler) {
@@ -1108,14 +1191,14 @@ class ApiRouter extends Router {
1108
1191
  /**
1109
1192
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1110
1193
  * @overload
1111
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1194
+ * @param {Handler} handler 要注册的处理函数
1112
1195
  * @returns {() => void}
1113
1196
  */
1114
1197
  /**
1115
1198
  * 注册处理函数
1116
1199
  * @overload
1117
1200
  * @param {string} path 要注册的路径
1118
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1201
+ * @param {Handler} handler 要注册的处理函数
1119
1202
  * @returns {() => void}
1120
1203
  */
1121
1204
  /**
@@ -1141,14 +1224,14 @@ class ApiRouter extends Router {
1141
1224
  /**
1142
1225
  * 注册 HTTP GET 处理函数
1143
1226
  * @overload
1144
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1227
+ * @param {Handler} handler 要注册的处理函数
1145
1228
  * @returns {() => void}
1146
1229
  */
1147
1230
  /**
1148
1231
  * 注册 HTTP GET 处理函数
1149
1232
  * @overload
1150
1233
  * @param {string} path 要注册的路径
1151
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1234
+ * @param {Handler} handler 要注册的处理函数
1152
1235
  * @returns {() => void}
1153
1236
  */
1154
1237
  /**
@@ -1172,14 +1255,14 @@ class ApiRouter extends Router {
1172
1255
  /**
1173
1256
  * 注册 HTTP POST 处理函数
1174
1257
  * @overload
1175
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1258
+ * @param {Handler} handler 要注册的处理函数
1176
1259
  * @returns {() => void}
1177
1260
  */
1178
1261
  /**
1179
1262
  * 注册 HTTP POST 处理函数
1180
1263
  * @overload
1181
1264
  * @param {string} path 要注册的路径
1182
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1265
+ * @param {Handler} handler 要注册的处理函数
1183
1266
  * @returns {() => void}
1184
1267
  */
1185
1268
  /**
@@ -1203,14 +1286,14 @@ class ApiRouter extends Router {
1203
1286
  /**
1204
1287
  * 注册 HTTP PUT 处理函数
1205
1288
  * @overload
1206
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1289
+ * @param {Handler} handler 要注册的处理函数
1207
1290
  * @returns {() => void}
1208
1291
  */
1209
1292
  /**
1210
1293
  * 注册 HTTP PUT 处理函数
1211
1294
  * @overload
1212
1295
  * @param {string} path 要注册的路径
1213
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1296
+ * @param {Handler} handler 要注册的处理函数
1214
1297
  * @returns {() => void}
1215
1298
  */
1216
1299
  /**
@@ -1234,14 +1317,14 @@ class ApiRouter extends Router {
1234
1317
  /**
1235
1318
  * 注册 HTTP DELETE 处理函数
1236
1319
  * @overload
1237
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1320
+ * @param {Handler} handler 要注册的处理函数
1238
1321
  * @returns {() => void}
1239
1322
  */
1240
1323
  /**
1241
1324
  * 注册 HTTP DELETE 处理函数
1242
1325
  * @overload
1243
1326
  * @param {string} path 要注册的路径
1244
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1327
+ * @param {Handler} handler 要注册的处理函数
1245
1328
  * @returns {() => void}
1246
1329
  */
1247
1330
  /**
@@ -1265,14 +1348,14 @@ class ApiRouter extends Router {
1265
1348
  /**
1266
1349
  * 注册 HTTP HEAD 处理函数
1267
1350
  * @overload
1268
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1351
+ * @param {Handler} handler 要注册的处理函数
1269
1352
  * @returns {() => void}
1270
1353
  */
1271
1354
  /**
1272
1355
  * 注册 HTTP HEAD 处理函数
1273
1356
  * @overload
1274
1357
  * @param {string} path 要注册的路径
1275
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1358
+ * @param {Handler} handler 要注册的处理函数
1276
1359
  * @returns {() => void}
1277
1360
  */
1278
1361
  /**
@@ -1296,14 +1379,14 @@ class ApiRouter extends Router {
1296
1379
  /**
1297
1380
  * 注册 HTTP OPTIONS 处理函数
1298
1381
  * @overload
1299
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1382
+ * @param {Handler} handler 要注册的处理函数
1300
1383
  * @returns {() => void}
1301
1384
  */
1302
1385
  /**
1303
1386
  * 注册 HTTP OPTIONS 处理函数
1304
1387
  * @overload
1305
1388
  * @param {string} path 要注册的路径
1306
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1389
+ * @param {Handler} handler 要注册的处理函数
1307
1390
  * @returns {() => void}
1308
1391
  */
1309
1392
  /**
@@ -1349,21 +1432,22 @@ function createFetch(run, notFound) {
1349
1432
  };
1350
1433
  }
1351
1434
 
1435
+ /** @import { Context, Handler, HandlerResult } from './main/types' */
1352
1436
  /**
1353
1437
  * @callback Onionskin
1354
- * @param {import('./main/types').Context} ctx
1355
- * @param {() => Promise<import('./main/types').HandlerResult>} next
1356
- * @returns {PromiseLike<import('./main/types').HandlerResult> | import('./main/types').HandlerResult}
1438
+ * @param {Context} ctx
1439
+ * @param {() => Promise<HandlerResult>} next
1440
+ * @returns {PromiseLike<HandlerResult> | HandlerResult}
1357
1441
  */
1358
1442
 
1359
1443
  const noop = () => {};
1360
1444
  /**
1361
1445
  *
1362
1446
  * @param {...(Onionskin | Onionskin[])} handlers
1363
- * @returns {import('./main/types').Handler}
1447
+ * @returns {Handler}
1364
1448
  */
1365
1449
  function onionskin(...handlers) {
1366
- /** @type {import('./main/types').Handler} */
1450
+ /** @type {Handler} */
1367
1451
  let handler = noop;
1368
1452
  for (const os of handlers.flat()) {
1369
1453
  const currentHandler = handler;
@@ -1372,7 +1456,44 @@ function onionskin(...handlers) {
1372
1456
  return handler;
1373
1457
  }
1374
1458
 
1459
+ /** @import { Context } from './main/types.js' */
1460
+ class Param {
1461
+ #symbol = Symbol();
1462
+ get name() { return this.#symbol}
1463
+ #pattern
1464
+ get pattern() { return this.#pattern; }
1465
+ /**
1466
+ *
1467
+ * @param {RegExp?} [pattern]
1468
+ */
1469
+ constructor(pattern) {
1470
+ this.#pattern = pattern;
1471
+ }
1472
+ /**
1473
+ *
1474
+ * @param {Context} ctx
1475
+ * @returns {string?}
1476
+ */
1477
+ param(ctx) {
1478
+ const param = ctx.params[this.#symbol];
1479
+ if (Array.isArray(param)) { return param[0] ?? null; }
1480
+ return param ?? null;
1481
+ }
1482
+ /**
1483
+ *
1484
+ * @param {Context} ctx
1485
+ * @returns {string[]?}
1486
+ */
1487
+ params(ctx) {
1488
+ const param = ctx.params[this.#symbol];
1489
+ if (typeof param === 'string') { return [param]; }
1490
+ if (Array.isArray(param)) { return param; }
1491
+ return null;
1492
+ }
1493
+ }
1494
+
1375
1495
  exports.ApiRouter = ApiRouter;
1496
+ exports.Param = Param;
1376
1497
  exports.Router = Router;
1377
1498
  exports.createFetch = createFetch;
1378
1499
  exports.main = main;