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