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