k99 0.6.1 → 0.7.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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * k99 v0.6.1
2
+ * k99 v0.7.0
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,101 @@ 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
+ }
947
+ }
948
+ }
949
+ }
950
+ const last = paths.pop() || '';
951
+ const strings = paths.map((v, i) => [v, values[i]]).flat();
952
+ strings.push(last);
953
+ const p = strings.join('');
954
+ if (!p || /^\.+$/.test(p)) { continue; }
955
+ list.push(parse(p));
956
+ continue;
957
+ }
958
+
880
959
  }
881
960
  if (!list.length) { return; }
882
961
  return path => exec(list, path, end);
883
962
  }
884
963
 
964
+ /** @import { Handler, Method } from '../main/types' */
965
+ /** @import { Binder, Match, Route, RouterRoute } from './index.mjs' */
966
+
885
967
  /**
886
968
  *
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
969
+ * @param {(Route | RouterRoute)[]} routes
970
+ * @param {Set<Method>} methods
971
+ * @param {Match | undefined} match
972
+ * @param {Handler[]} handlers
891
973
  * @returns {() => void}
892
974
  */
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
- };
975
+ function bind(routes, methods, match, handlers) {
976
+ /** @type {Route} */
977
+ const route = { match, methods, handler: ctx => runHandles(ctx, handlers) };
900
978
  routes.push(route);
901
979
  let removed = false;
902
980
  return () => {
@@ -907,43 +985,49 @@ function bind(routes, methods, path, handler) {
907
985
  routes.splice(index, 1);
908
986
  };
909
987
  }
910
- /** @type {(v: any) => v is import('../main/types').Handler} */
988
+ /** @type {(v: any) => v is Handler} */
911
989
  const findHandler = v => typeof v === 'function';
912
990
  /**
913
991
  *
914
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
915
- * @param {import('../main/types').Method[]} methods
992
+ * @param {(Route | RouterRoute)[]} routes
993
+ * @param {Iterable<Method>} methods
916
994
  * @param {any[]} p
917
- * @returns {import('./index.mjs').Binder | (() => void)}
995
+ * @returns {Binder | (() => void)}
918
996
  */
919
997
  function verb(routes, methods, p) {
998
+ const methodSet = new Set(methods);
920
999
  if (!p.length) {
921
- return handler => bind(routes, methods, '', handler);
1000
+ const match = undefined;
1001
+ /** @type {Binder} */
1002
+ return (...handlers) => bind(routes, methodSet, match, handlers);
922
1003
  }
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);
1004
+ const [path] = p;
1005
+ if (path && typeof path === 'object') {
1006
+ const match = toMatch([path, p.slice(1)], true);
1007
+ /** @type {Binder} */
1008
+ return (...handlers) => bind(routes, methodSet, match, handlers);
927
1009
  }
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);
1010
+ const match = toMatch(typeof path === 'string' ? path : '', true);
1011
+ const handlers = p.filter(findHandler);
1012
+ if (!handlers.length) {
1013
+ /** @type {Binder} */
1014
+ return (...handlers) => bind(routes, methodSet, match, handlers);
932
1015
  }
933
- return bind(routes, methods, path, handler);
1016
+ return bind(routes, methodSet, match, handlers);
934
1017
  }
935
1018
 
1019
+ /** @import { Method } from '../main/types' */
936
1020
  const methods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']);
937
1021
  /**
938
1022
  *
939
1023
  * @param {any} v
940
- * @returns {v is import('../main/types').Method}
1024
+ * @returns {v is Method}
941
1025
  */
942
1026
  function isMethod(v) { return methods.has(v); }
943
1027
  /**
944
1028
  *
945
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} [methods]
946
- * @returns {import('../main/types').Method[]}
1029
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} [methods]
1030
+ * @returns {Method[]}
947
1031
  */
948
1032
  function getMethods(methods) {
949
1033
  if (!methods) {
@@ -957,25 +1041,24 @@ function getMethods(methods) {
957
1041
  .filter(isMethod);
958
1042
  }
959
1043
 
1044
+ /** @import { Context, Handler, Method, Params } from '../main/types' */
1045
+ /** @import { Finder, FindItem } from '../Router.mjs' */
1046
+
960
1047
  /**
961
1048
  * @callback Match
962
1049
  * @param {string[]} paths
963
- * @returns {[Record<string, string | string[]>, string[]] | undefined}
1050
+ * @returns {[Params, string[]] | undefined}
964
1051
  */
965
1052
 
966
- /**
967
- * @callback Binder
968
- * @param {import('../main/types').Handler} handler
969
- * @returns {() => void}
970
- */
1053
+ /** @typedef {(handler: Handler, ...handlers: Handler[]) => () => void} Binder */
971
1054
 
972
1055
  /**
973
1056
  * @typedef {object} Route
974
1057
  * @property {Match} [match] 路径匹配
975
1058
  * @property {null} [router]
976
1059
  * @property {string} [plugin] 所属插件
977
- * @property {import('../main/types').Handler} handler 处理函数
978
- * @property {Set<import('../main/types').Method>} methods 方法列表
1060
+ * @property {Handler} handler 处理函数
1061
+ * @property {Set<Method>} methods 方法列表
979
1062
  */
980
1063
 
981
1064
  /**
@@ -986,16 +1069,16 @@ function getMethods(methods) {
986
1069
 
987
1070
 
988
1071
  /**
989
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1072
+ * @template {Router | Finder} [T=ApiRouter]
990
1073
  * @callback RouteBinder
991
1074
  * @param {T} [router] 要注册的子路由或子路由的 Finder
992
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1075
+ * @returns {T extends Finder ? Router : T}
993
1076
  */
994
1077
  /**
995
1078
  *
996
1079
  * @param {(Route | RouterRoute)[]} routes
997
- * @param {string} path
998
- * @param {Router | import('../Router.mjs').Finder} [r]
1080
+ * @param {string | [string[], any[]]} path
1081
+ * @param {Router | Finder} [r]
999
1082
  * @returns {Router}
1000
1083
  */
1001
1084
  function bindRouter(routes, path, r) {
@@ -1010,18 +1093,18 @@ class ApiRouter extends Router {
1010
1093
  #routes = [];
1011
1094
  /**
1012
1095
  * 添加子路由
1013
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1096
+ * @template {Router | Finder} [T=ApiRouter]
1014
1097
  * @overload
1015
1098
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1016
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1099
+ * @returns {T extends Finder ? Router : T}
1017
1100
  */
1018
1101
  /**
1019
1102
  * 添加子路由
1020
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1103
+ * @template {Router | Finder} [T=ApiRouter]
1021
1104
  * @overload
1022
1105
  * @param {string} path 要注册的路径
1023
1106
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1024
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1107
+ * @returns {T extends Finder ? Router : T}
1025
1108
  */
1026
1109
  /**
1027
1110
  * 添加子路由
@@ -1038,12 +1121,11 @@ class ApiRouter extends Router {
1038
1121
  route(...p) {
1039
1122
  const [a] = p;
1040
1123
  if (a && typeof a === 'object' && !(a instanceof Router)) {
1041
- const path = String.raw(a, ...p.slice(1));
1042
1124
  /**
1043
- * @param { import('../Router.mjs').Finder | Router} [r];
1125
+ * @param { Finder | Router} [r];
1044
1126
  * @returns {any}
1045
1127
  */
1046
- return r => bindRouter(this.#routes, path, r);
1128
+ return r => bindRouter(this.#routes, [a, p.slice(1)], r);
1047
1129
  }
1048
1130
  const path = typeof a === 'string' ? a : '';
1049
1131
  const r = typeof a === 'string' ? p[1] : a;
@@ -1051,10 +1133,10 @@ class ApiRouter extends Router {
1051
1133
  }
1052
1134
  /**
1053
1135
  *
1054
- * @param {import('../main/types').Method} method
1136
+ * @param {Method} method
1055
1137
  * @param {string[]} path
1056
- * @param {import('../main/types').Context} ctx
1057
- * @returns {Iterable<import('../Router.mjs').FindItem>}
1138
+ * @param {Context} ctx
1139
+ * @returns {Iterable<FindItem>}
1058
1140
  */
1059
1141
  *find(method, path, ctx) {
1060
1142
  for (const route of Array.from(this.#routes)) {
@@ -1075,29 +1157,29 @@ class ApiRouter extends Router {
1075
1157
  /**
1076
1158
  * 注册处理函数
1077
1159
  * @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 要注册的处理函数
1160
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1161
+ * @param {Handler} handler 要注册的处理函数
1080
1162
  * @returns {() => void}
1081
1163
  */
1082
1164
  /**
1083
1165
  * 注册处理函数
1084
1166
  * @overload
1085
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1167
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1086
1168
  * @param {string} path 要注册的路径
1087
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1169
+ * @param {Handler} handler 要注册的处理函数
1088
1170
  * @returns {() => void}
1089
1171
  */
1090
1172
  /**
1091
1173
  * 注册处理函数
1092
1174
  * @overload
1093
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1175
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1094
1176
  * @param {string} path 要注册的路径
1095
1177
  * @returns {Binder}
1096
1178
  */
1097
1179
  /**
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]
1180
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} methods
1181
+ * @param {string| Handler} [path]
1182
+ * @param {Handler} [handler]
1101
1183
  * @returns {Binder | (() => void)}
1102
1184
  */
1103
1185
  verb(methods, path, handler) {
@@ -1108,14 +1190,14 @@ class ApiRouter extends Router {
1108
1190
  /**
1109
1191
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1110
1192
  * @overload
1111
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1193
+ * @param {Handler} handler 要注册的处理函数
1112
1194
  * @returns {() => void}
1113
1195
  */
1114
1196
  /**
1115
1197
  * 注册处理函数
1116
1198
  * @overload
1117
1199
  * @param {string} path 要注册的路径
1118
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1200
+ * @param {Handler} handler 要注册的处理函数
1119
1201
  * @returns {() => void}
1120
1202
  */
1121
1203
  /**
@@ -1141,14 +1223,14 @@ class ApiRouter extends Router {
1141
1223
  /**
1142
1224
  * 注册 HTTP GET 处理函数
1143
1225
  * @overload
1144
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1226
+ * @param {Handler} handler 要注册的处理函数
1145
1227
  * @returns {() => void}
1146
1228
  */
1147
1229
  /**
1148
1230
  * 注册 HTTP GET 处理函数
1149
1231
  * @overload
1150
1232
  * @param {string} path 要注册的路径
1151
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1233
+ * @param {Handler} handler 要注册的处理函数
1152
1234
  * @returns {() => void}
1153
1235
  */
1154
1236
  /**
@@ -1172,14 +1254,14 @@ class ApiRouter extends Router {
1172
1254
  /**
1173
1255
  * 注册 HTTP POST 处理函数
1174
1256
  * @overload
1175
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1257
+ * @param {Handler} handler 要注册的处理函数
1176
1258
  * @returns {() => void}
1177
1259
  */
1178
1260
  /**
1179
1261
  * 注册 HTTP POST 处理函数
1180
1262
  * @overload
1181
1263
  * @param {string} path 要注册的路径
1182
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1264
+ * @param {Handler} handler 要注册的处理函数
1183
1265
  * @returns {() => void}
1184
1266
  */
1185
1267
  /**
@@ -1203,14 +1285,14 @@ class ApiRouter extends Router {
1203
1285
  /**
1204
1286
  * 注册 HTTP PUT 处理函数
1205
1287
  * @overload
1206
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1288
+ * @param {Handler} handler 要注册的处理函数
1207
1289
  * @returns {() => void}
1208
1290
  */
1209
1291
  /**
1210
1292
  * 注册 HTTP PUT 处理函数
1211
1293
  * @overload
1212
1294
  * @param {string} path 要注册的路径
1213
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1295
+ * @param {Handler} handler 要注册的处理函数
1214
1296
  * @returns {() => void}
1215
1297
  */
1216
1298
  /**
@@ -1234,14 +1316,14 @@ class ApiRouter extends Router {
1234
1316
  /**
1235
1317
  * 注册 HTTP DELETE 处理函数
1236
1318
  * @overload
1237
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1319
+ * @param {Handler} handler 要注册的处理函数
1238
1320
  * @returns {() => void}
1239
1321
  */
1240
1322
  /**
1241
1323
  * 注册 HTTP DELETE 处理函数
1242
1324
  * @overload
1243
1325
  * @param {string} path 要注册的路径
1244
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1326
+ * @param {Handler} handler 要注册的处理函数
1245
1327
  * @returns {() => void}
1246
1328
  */
1247
1329
  /**
@@ -1265,14 +1347,14 @@ class ApiRouter extends Router {
1265
1347
  /**
1266
1348
  * 注册 HTTP HEAD 处理函数
1267
1349
  * @overload
1268
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1350
+ * @param {Handler} handler 要注册的处理函数
1269
1351
  * @returns {() => void}
1270
1352
  */
1271
1353
  /**
1272
1354
  * 注册 HTTP HEAD 处理函数
1273
1355
  * @overload
1274
1356
  * @param {string} path 要注册的路径
1275
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1357
+ * @param {Handler} handler 要注册的处理函数
1276
1358
  * @returns {() => void}
1277
1359
  */
1278
1360
  /**
@@ -1296,14 +1378,14 @@ class ApiRouter extends Router {
1296
1378
  /**
1297
1379
  * 注册 HTTP OPTIONS 处理函数
1298
1380
  * @overload
1299
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1381
+ * @param {Handler} handler 要注册的处理函数
1300
1382
  * @returns {() => void}
1301
1383
  */
1302
1384
  /**
1303
1385
  * 注册 HTTP OPTIONS 处理函数
1304
1386
  * @overload
1305
1387
  * @param {string} path 要注册的路径
1306
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1388
+ * @param {Handler} handler 要注册的处理函数
1307
1389
  * @returns {() => void}
1308
1390
  */
1309
1391
  /**
@@ -1349,21 +1431,22 @@ function createFetch(run, notFound) {
1349
1431
  };
1350
1432
  }
1351
1433
 
1434
+ /** @import { Context, Handler, HandlerResult } from './main/types' */
1352
1435
  /**
1353
1436
  * @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}
1437
+ * @param {Context} ctx
1438
+ * @param {() => Promise<HandlerResult>} next
1439
+ * @returns {PromiseLike<HandlerResult> | HandlerResult}
1357
1440
  */
1358
1441
 
1359
1442
  const noop = () => {};
1360
1443
  /**
1361
1444
  *
1362
1445
  * @param {...(Onionskin | Onionskin[])} handlers
1363
- * @returns {import('./main/types').Handler}
1446
+ * @returns {Handler}
1364
1447
  */
1365
1448
  function onionskin(...handlers) {
1366
- /** @type {import('./main/types').Handler} */
1449
+ /** @type {Handler} */
1367
1450
  let handler = noop;
1368
1451
  for (const os of handlers.flat()) {
1369
1452
  const currentHandler = handler;
@@ -1372,7 +1455,44 @@ function onionskin(...handlers) {
1372
1455
  return handler;
1373
1456
  }
1374
1457
 
1458
+ /** @import { Context } from './main/types.js' */
1459
+ class Param {
1460
+ #symbol = Symbol();
1461
+ get name() { return this.#symbol}
1462
+ #pattern
1463
+ get pattern() { return this.#pattern; }
1464
+ /**
1465
+ *
1466
+ * @param {RegExp} pattern
1467
+ */
1468
+ constructor(pattern) {
1469
+ this.#pattern = pattern;
1470
+ }
1471
+ /**
1472
+ *
1473
+ * @param {Context} ctx
1474
+ * @returns {string?}
1475
+ */
1476
+ param(ctx) {
1477
+ const param = ctx.params[this.#symbol];
1478
+ if (Array.isArray(param)) { return param[0] ?? null; }
1479
+ return param ?? null;
1480
+ }
1481
+ /**
1482
+ *
1483
+ * @param {Context} ctx
1484
+ * @returns {string[]?}
1485
+ */
1486
+ params(ctx) {
1487
+ const param = ctx.params[this.#symbol];
1488
+ if (typeof param === 'string') { return [param]; }
1489
+ if (Array.isArray(param)) { return param; }
1490
+ return null;
1491
+ }
1492
+ }
1493
+
1375
1494
  exports.ApiRouter = ApiRouter;
1495
+ exports.Param = Param;
1376
1496
  exports.Router = Router;
1377
1497
  exports.createFetch = createFetch;
1378
1498
  exports.main = main;