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