k99 0.6.0 → 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,6 +1,6 @@
1
1
  /*!
2
- * k99 v0.6.0
3
- * (c) 2019-2024 猛火Fierflame
2
+ * k99 v0.7.0
3
+ * (c) 2019-2025 猛火Fierflame
4
4
  * @license MIT
5
5
  */
6
6
  (function (global, factory) {
@@ -17,20 +17,6 @@
17
17
  function str2utf8bin(str) {
18
18
  return new TextEncoder().encode(str);
19
19
  }
20
- /**
21
- *
22
- * @param {unknown} chunk
23
- * @returns {chunk is ArrayBuffer | SharedArrayBuffer}
24
- */
25
- function isBufferSource(chunk) {
26
- if (chunk instanceof ArrayBuffer) { return true; }
27
- try {
28
- if (chunk instanceof SharedArrayBuffer) { return true; }
29
- } catch {
30
-
31
- }
32
- return false;
33
- }
34
20
  /**
35
21
  *
36
22
  * @template T
@@ -55,7 +41,7 @@
55
41
  if (ArrayBuffer.isView(chunk)) {
56
42
  return writer.write(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
57
43
  }
58
- if (isBufferSource(chunk)) {
44
+ if (chunk instanceof ArrayBuffer) {
59
45
  return writer.write(new Uint8Array(chunk));
60
46
  }
61
47
  if (!isIterable(chunk)) {
@@ -99,7 +85,7 @@
99
85
  if (result instanceof FormData) {
100
86
  return [result, 0, ''];
101
87
  }
102
- if (ArrayBuffer.isView(result) || isBufferSource(result)) {
88
+ if (ArrayBuffer.isView(result) || result instanceof ArrayBuffer) {
103
89
  return [result, result.byteLength, ''];
104
90
  }
105
91
  if (typeof result === 'string') {
@@ -124,8 +110,7 @@
124
110
  if (!data) { continue; }
125
111
  await write(writer, data);
126
112
  }
127
- await writable.close();
128
- })().catch(() => {});
113
+ })().then(() => writable.close(), r => writable.abort(r)).catch(() => {});
129
114
  return [readable, 0, ''];
130
115
  }
131
116
  /**
@@ -148,11 +133,12 @@
148
133
  return body;
149
134
  }
150
135
 
136
+ /** @import { Cookie, CookieOption } from './types' */
151
137
  /**
152
138
  *
153
- * @param {import('./types').Cookie[]} sentCookies
139
+ * @param {Cookie[]} sentCookies
154
140
  * @param {string} [name]
155
- * @returns {Iterable<import('./types').Cookie>}
141
+ * @returns {Iterable<Cookie>}
156
142
  */
157
143
  function *getCookie(sentCookies, name) {
158
144
  const list = sentCookies;
@@ -165,7 +151,7 @@
165
151
  /**
166
152
  *
167
153
  * @param {Headers} headers
168
- * @param {import('./types').Cookie[]} cookies
154
+ * @param {Cookie[]} cookies
169
155
  * @returns {void}
170
156
  */
171
157
  function setCookiesHeader(headers, cookies) {
@@ -199,10 +185,10 @@
199
185
  }
200
186
  /**
201
187
  *
202
- * @param {import('./types').Cookie[]} sentCookies
188
+ * @param {Cookie[]} sentCookies
203
189
  * @param {Record<string, string>} cookies
204
- * @param {string | import('./types').CookieOption} [name]
205
- * @param {import('./types').CookieOption | boolean} [opt]
190
+ * @param {string | CookieOption} [name]
191
+ * @param {CookieOption | boolean} [opt]
206
192
  * @returns {void}
207
193
  */
208
194
  function clearCookie(
@@ -214,11 +200,11 @@
214
200
  let expire = 'Fri, 31 Dec 1999 16:00:00 GMT';
215
201
  if (typeof name === 'string') {
216
202
  if (!name) { return; }
217
- /** @type {import('./types').CookieOption} */
203
+ /** @type {CookieOption} */
218
204
  const { domain, path, secure, httpOnly } = opt !== true && opt || {};
219
205
  sentCookies.push({ name, value: 'delete', expire, domain, path, secure, httpOnly });
220
206
  } else {
221
- /** @type {import('./types').CookieOption} */
207
+ /** @type {CookieOption} */
222
208
  const { domain, path, secure, httpOnly } = name || {};
223
209
  sentCookies.length = 0;
224
210
  if (opt) {
@@ -229,6 +215,9 @@
229
215
  }
230
216
  }
231
217
 
218
+ /** @import { Context, Cookie, CookieOption, FindHandler, Method, Options, Params, Service } from './types' */
219
+
220
+
232
221
  const noBodyMethods = new Set(['GET', 'OPTIONS']);
233
222
  /**
234
223
  *
@@ -265,7 +254,7 @@
265
254
  *
266
255
  * @param {Request} request
267
256
  * @param {string | ((request: Request) => string)} [toMethod]
268
- * @returns {import('./types').Method}
257
+ * @returns {Method}
269
258
  */
270
259
  function getMethod(request, toMethod) {
271
260
  let methodStr = '';
@@ -277,13 +266,13 @@
277
266
  if (!methodStr || typeof methodStr !== 'string') {
278
267
  methodStr = request.method || 'GET';
279
268
  }
280
- return /** @type {import('./types').Method} */(methodStr.toUpperCase());
269
+ return /** @type {Method} */(methodStr.toUpperCase());
281
270
  }
282
271
  /**
283
272
  *
284
273
  * @param {Request} request
285
- * @param {import('./types').FindHandler} getHandler
286
- * @param {import('./types').Options} [options]
274
+ * @param {FindHandler} getHandler
275
+ * @param {Options} [options]
287
276
  * @returns {Promise<Response | null>}
288
277
  */
289
278
  function main(
@@ -293,7 +282,7 @@
293
282
  /**
294
283
  *
295
284
  * @param {Request} request
296
- * @param {import('./types').Context} [parent]
285
+ * @param {Context} [parent]
297
286
  * @returns {Promise<Response | null>}
298
287
  */
299
288
  function exec(request, parent) {
@@ -301,10 +290,10 @@
301
290
  const url = new URL(request.url);
302
291
  const { signal, headers } = request;
303
292
  const aborted = signal2promise(signal);
304
- /** @type {Map<import('./types').Service<any, any>, Function>} */
293
+ /** @type {Map<Service<any, any>, Function>} */
305
294
  const services = new Map();
306
295
  const cookies = getRequestCookies(headers.get('cookie') || '');
307
- /** @type {import('./types').Cookie[]} */
296
+ /** @type {Cookie[]} */
308
297
  const sentCookies = [];
309
298
  const responseHeaders = new Headers();
310
299
  const root = parent?.root;
@@ -320,9 +309,9 @@
320
309
  const donePromise = new Promise((a, b) => { resolve = a; reject = b; });
321
310
  donePromise.catch(() => {});
322
311
 
323
- /** @type {any} */
312
+ /** @type {Params} */
324
313
  let params = {};
325
- /** @type {import('./types').Context} */
314
+ /** @type {Context} */
326
315
  const context = {
327
316
  environment,
328
317
  parent,
@@ -386,8 +375,8 @@
386
375
  },
387
376
  /**
388
377
  *
389
- * @param {string | import('./types').CookieOption} [name]
390
- * @param {import('./types').CookieOption | boolean} [opt]
378
+ * @param {string | CookieOption} [name]
379
+ * @param {CookieOption | boolean} [opt]
391
380
  * @returns {void}
392
381
  */
393
382
  clearCookie(name, opt) {
@@ -424,69 +413,72 @@
424
413
  return exec(request);
425
414
  }
426
415
 
416
+ /** @import { FindHandler, Options } from './main/types' */
417
+
427
418
  /**
428
419
  *
429
- * @param {import('./main/types').FindHandler} getHandler
430
- * @param {import('./main/types').Options} options
420
+ * @param {FindHandler} getHandler
421
+ * @param {Options} options
431
422
  * @returns {(request: Request) => Promise<Response | null>}
432
423
  */
433
424
  function make(getHandler, options) {
434
425
  return r => main(r, getHandler, options);
435
426
  }
436
427
 
428
+ /** @import { Context, Handler } from './main/types' */
437
429
  /**
438
430
  *
439
- * @param {import('./main/types').Context} context
440
- * @param {import('./main/types').Handler[]} handlers
431
+ * @param {Context} context
432
+ * @param {Handler[]} handlers
441
433
  * @returns {Promise<string | boolean | object | undefined>}
442
434
  */
443
435
  async function runHandles(context, handlers) {
444
436
  for (const handle of handlers) {
445
437
  const result = await handle(context);
446
- if (typeof result === 'boolean') { return result; }
447
- if (!result) { continue; }
438
+ if (result === undefined) { continue; }
448
439
  return result;
449
440
  }
450
441
  }
451
442
 
452
443
  /**
453
444
  *
454
- * @param {...(import('./main/types').Handler | import('./main/types').Handler[])} handlers
455
- * @returns {import('./main/types').Handler}
445
+ * @param {...(Handler | Handler[])} handlers
446
+ * @returns {Handler}
456
447
  */
457
448
  function merge(...handlers) {
458
449
  return ctx => runHandles(ctx, handlers.flat());
459
450
  }
460
451
 
452
+ /** @import { Context, Service } from './main/types' */
461
453
  /**
462
454
  *
463
455
  * @template T
464
456
  * @template {any[]} P
465
457
  * @overload
466
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
467
- * @param {((ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
468
- * @param {import('./main/types').Service.Options?} [options]
469
- * @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>}
470
462
  */
471
463
  /**
472
464
  *
473
465
  * @template T
474
466
  * @template {any[]} P
475
467
  * @overload
476
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
477
- * @param {import('./main/types').Service.Options?} [options]
478
- * @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>}
479
471
  */
480
472
  /**
481
473
  * @template T
482
474
  * @template {any[]} P
483
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
484
- * @param {((ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
485
- * @param {import('./main/types').Service.Options?} [options]
486
- * @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>}
487
479
  */
488
480
  function service(exec, destroy, options) {
489
- /** @type {import('./main/types').Service<T, P>} */
481
+ /** @type {Service<T, P>} */
490
482
  const service = function (ctx) {
491
483
  if (typeof destroy === 'function') {
492
484
  ctx.done(() => destroy(ctx), error => destroy(ctx, error));
@@ -505,43 +497,44 @@
505
497
  return service;
506
498
  }
507
499
 
500
+ /** @import { Context, Service, StateService } from './main/types' */
508
501
  /**
509
502
  *
510
503
  * @template T
511
504
  * @overload
512
- * @param {(ctx: import('./main/types').Context) => T} init
513
- * @param {import('./main/types').Service.Options} [options]
514
- * @returns {import('./main/types').StateService<T>}
505
+ * @param {(ctx: Context) => T} init
506
+ * @param {Service.Options} [options]
507
+ * @returns {StateService<T>}
515
508
  */
516
509
  /**
517
510
  *
518
511
  * @template T
519
512
  * @overload
520
- * @param {(ctx: import('./main/types').Context) => T} init
521
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
522
- * @param {import('./main/types').Service.Options?} [options]
523
- * @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>}
524
517
  */
525
518
  /**
526
519
  *
527
520
  * @template T
528
521
  * @overload
529
- * @param {(ctx: import('./main/types').Context) => T} init
530
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
531
- * @param {((state: T, ctx: import('./main/types').Context) => any)?} [exec]
532
- * @param {import('./main/types').Service.Options?} [options]
533
- * @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>}
534
527
  */
535
528
  /**
536
529
  * @template T
537
- * @param {(ctx: import('./main/types').Context) => T} init
538
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
539
- * @param {((state: T, ctx: import('./main/types').Context) => any) | import('./main/types').Service.Options | null} [exec]
540
- * @param {import('./main/types').Service.Options?} [options]
541
- * @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>}
542
535
  */
543
536
  function stateService(init, destroy, exec, options) {
544
- /** @type {import('./main/types').StateService<T>} */
537
+ /** @type {StateService<T>} */
545
538
  const service = function (ctx) {
546
539
  const state = init(ctx) || /** @type {T} */({});
547
540
  if (typeof destroy === 'function') {
@@ -565,39 +558,40 @@
565
558
  return service;
566
559
  }
567
560
 
561
+ /** @import { Context, Service, StoreService } from './main/types' */
568
562
  /**
569
563
  *
570
564
  * @template T
571
565
  * @overload
572
- * @param {import('./main/types').Service.Options?} [options]
573
- * @returns {import('./main/types').StoreService<T>}
566
+ * @param {Service.Options?} [options]
567
+ * @returns {StoreService<T>}
574
568
  */
575
569
  /**
576
570
  *
577
571
  * @template T
578
572
  * @overload
579
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
580
- * @param {import('./main/types').Service.Options?} [options]
581
- * @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>}
582
576
  */
583
577
  /**
584
578
  *
585
579
  * @template T
586
580
  * @overload
587
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
588
- * @param {((state: T | undefined, ctx: import('./main/types').Context) => any)?} [exec]
589
- * @param {import('./main/types').Service.Options?} [options]
590
- * @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>}
591
585
  */
592
586
  /**
593
587
  * @template T
594
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
595
- * @param {((state: T | undefined, ctx: import('./main/types').Context) => any) | import('./main/types').Service.Options | null} [exec]
596
- * @param {import('./main/types').Service.Options?} [options]
597
- * @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>}
598
592
  */
599
593
  function storeService(destroy, exec, options) {
600
- /** @type {import('./main/types').StoreService<T>} */
594
+ /** @type {StoreService<T>} */
601
595
  const service = function (ctx) {
602
596
  /** @type {T | undefined} */
603
597
  let state;
@@ -634,16 +628,18 @@
634
628
  return service;
635
629
  }
636
630
 
631
+ /** @import { Handler } from './main/types' */
632
+ /** @import { Onionskin } from './onionskin.mjs' */
637
633
  /**
638
634
  * @callback Packer
639
- * @param {import('./main/types').Handler} handler
640
- * @returns {import('./main/types').Handler}
635
+ * @param {Handler} handler
636
+ * @returns {Handler}
641
637
  */
642
638
  /** @type {Packer} */
643
639
  const noop$1 = h => h;
644
640
  /**
645
641
  *
646
- * @param {import('./onionskin.mjs').Onionskin} onionskin
642
+ * @param {Onionskin} onionskin
647
643
  * @param {Packer} [packer]
648
644
  * @returns {Packer}
649
645
  */
@@ -654,30 +650,33 @@
654
650
  };
655
651
  }
656
652
 
653
+ /** @import { Context, FindHandler, Handler, Method, Params } from './main/types' */
654
+ /** @import { Onionskin } from './onionskin.mjs' */
655
+
657
656
  /**
658
657
  * @callback Guard
659
- * @param {import('./main/types').Context} ctx
660
- * @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}
661
660
  */
662
661
  /**
663
- * @typedef {[import('./main/types').Handler | Router, Record<string, any>, string[]]} FindItem
662
+ * @typedef {[Handler | Router, Record<string | symbol, any>, string[]]} FindItem
664
663
  */
665
664
  /**
666
665
  * @callback Finder
667
666
  * @this {Router}
668
- * @param {import('./main/types').Method} method
667
+ * @param {Method} method
669
668
  * @param {string[]} path
670
- * @param {import('./main/types').Context} ctx
669
+ * @param {Context} ctx
671
670
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
672
671
  */
673
672
 
674
673
  /**
675
674
  *
676
675
  * @param {Set<Guard>} guards
677
- * @param {import('./main/types').Context} ctx
676
+ * @param {Context} ctx
678
677
  * @param {(v: any) => void} setParams
679
678
  * @param {object} params
680
- * @returns {Promise<boolean | import('./main/types').Handler>}
679
+ * @returns {Promise<boolean | Handler>}
681
680
  */
682
681
  async function execGuard(guards, ctx, setParams, params) {
683
682
  if (!guards.size) { return true; }
@@ -696,12 +695,12 @@
696
695
 
697
696
  /**
698
697
  *
699
- * @param {Router | import('./main/types').Handler} route
698
+ * @param {Router | Handler} route
700
699
  * @param {string[]} path
701
- * @param {import('./main/types').Context} ctx
702
- * @param {(v: any) => void} setParams
703
- * @param {object} params
704
- * @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>}
705
704
  */
706
705
  async function find(route, path, ctx, setParams, params) {
707
706
  if (!(route instanceof Router)) {
@@ -740,16 +739,16 @@
740
739
  disabled = false;
741
740
  /**
742
741
  * @abstract
743
- * @param {import('./main/types').Method} method
742
+ * @param {Method} method
744
743
  * @param {string[]} path
745
- * @param {import('./main/types').Context} ctx
744
+ * @param {Context} ctx
746
745
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
747
746
  */
748
747
  find(method, path, ctx) { return []; }
749
748
  /**
750
749
  *
751
750
  * @param {Router[]} routers
752
- * @returns {import('./main/types').FindHandler}
751
+ * @returns {FindHandler}
753
752
  */
754
753
  static make(routers) {
755
754
  return async (ctx, setParams) => {
@@ -777,19 +776,21 @@
777
776
  guards = new Set();
778
777
  /**
779
778
  *
780
- * @param {import('./main/types').Handler} h
781
- * @returns {import('./main/types').Handler}
779
+ * @param {Handler} h
780
+ * @returns {Handler}
782
781
  */
783
782
  __onionskin = (h) => h;
784
- /** @param {import('./onionskin.mjs').Onionskin} os */
783
+ /** @param {Onionskin} os */
785
784
  onionskin(os) { this.__onionskin = packer(os, this.__onionskin); }
786
785
  }
787
786
 
787
+ /** @import { Match } from './index.mjs' */
788
+ /** @import { Params } from '../main/types.js' */
788
789
  /**
789
790
  * @typedef {object} Pattern
790
- * @property {string} name
791
- * @property {boolean} optional
792
- * @property {boolean} many
791
+ * @property {string | symbol} name
792
+ * @property {boolean} [optional]
793
+ * @property {boolean} [many]
793
794
  * @property {RegExp} pattern
794
795
  */
795
796
 
@@ -860,7 +861,7 @@
860
861
  * @returns {[Record<string, string | string[]>, string[]] | undefined}
861
862
  */
862
863
  function exec(match, path, end) {
863
- /** @type {Record<string, string | string[]>} */
864
+ /** @type {Params} */
864
865
  const params = {};
865
866
  for (let i = 0; i < match.length; i++) {
866
867
  const m = match[i];
@@ -883,38 +884,101 @@
883
884
  return [params, []];
884
885
 
885
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
+ }
886
915
  /**
887
916
  *
888
- * @param {string} path
917
+ * @param {string | [string[], any[]]} path
889
918
  * @param {boolean} end
890
- * @returns {import('./index.mjs').Match | undefined}
919
+ * @returns {Match | undefined}
891
920
  */
892
921
  function toMatch(path, end) {
893
922
  /** @type {(Pattern | string)[]} */
894
923
  const list = [];
895
- for (const p of path.split('/')) {
896
- if (!p || /^\.+$/.test(p)) { continue; }
897
- 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
+
898
963
  }
899
964
  if (!list.length) { return; }
900
965
  return path => exec(list, path, end);
901
966
  }
902
967
 
968
+ /** @import { Handler, Method } from '../main/types' */
969
+ /** @import { Binder, Match, Route, RouterRoute } from './index.mjs' */
970
+
903
971
  /**
904
972
  *
905
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
906
- * @param {import('../main/types').Method[]} methods
907
- * @param {string} path
908
- * @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
909
977
  * @returns {() => void}
910
978
  */
911
- function bind(routes, methods, path, handler) {
912
- /** @type {import('./index.mjs').Route} */
913
- const route = {
914
- match: toMatch(path || '', true),
915
- methods: new Set(methods),
916
- handler,
917
- };
979
+ function bind(routes, methods, match, handlers) {
980
+ /** @type {Route} */
981
+ const route = { match, methods, handler: ctx => runHandles(ctx, handlers) };
918
982
  routes.push(route);
919
983
  let removed = false;
920
984
  return () => {
@@ -925,43 +989,49 @@
925
989
  routes.splice(index, 1);
926
990
  };
927
991
  }
928
- /** @type {(v: any) => v is import('../main/types').Handler} */
992
+ /** @type {(v: any) => v is Handler} */
929
993
  const findHandler = v => typeof v === 'function';
930
994
  /**
931
995
  *
932
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
933
- * @param {import('../main/types').Method[]} methods
996
+ * @param {(Route | RouterRoute)[]} routes
997
+ * @param {Iterable<Method>} methods
934
998
  * @param {any[]} p
935
- * @returns {import('./index.mjs').Binder | (() => void)}
999
+ * @returns {Binder | (() => void)}
936
1000
  */
937
1001
  function verb(routes, methods, p) {
1002
+ const methodSet = new Set(methods);
938
1003
  if (!p.length) {
939
- return handler => bind(routes, methods, '', handler);
1004
+ const match = undefined;
1005
+ /** @type {Binder} */
1006
+ return (...handlers) => bind(routes, methodSet, match, handlers);
940
1007
  }
941
- const [a, b] = p;
942
- if (a && typeof a === 'object') {
943
- const path = String.raw(a, ...p.slice(1));
944
- 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);
945
1013
  }
946
- const path = typeof a === 'string' ? a : '';
947
- const handler = [a, b].find(findHandler);
948
- if (!handler) {
949
- 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);
950
1019
  }
951
- return bind(routes, methods, path, handler);
1020
+ return bind(routes, methodSet, match, handlers);
952
1021
  }
953
1022
 
1023
+ /** @import { Method } from '../main/types' */
954
1024
  const methods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']);
955
1025
  /**
956
1026
  *
957
1027
  * @param {any} v
958
- * @returns {v is import('../main/types').Method}
1028
+ * @returns {v is Method}
959
1029
  */
960
1030
  function isMethod(v) { return methods.has(v); }
961
1031
  /**
962
1032
  *
963
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} [methods]
964
- * @returns {import('../main/types').Method[]}
1033
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} [methods]
1034
+ * @returns {Method[]}
965
1035
  */
966
1036
  function getMethods(methods) {
967
1037
  if (!methods) {
@@ -975,25 +1045,24 @@
975
1045
  .filter(isMethod);
976
1046
  }
977
1047
 
1048
+ /** @import { Context, Handler, Method, Params } from '../main/types' */
1049
+ /** @import { Finder, FindItem } from '../Router.mjs' */
1050
+
978
1051
  /**
979
1052
  * @callback Match
980
1053
  * @param {string[]} paths
981
- * @returns {[Record<string, string | string[]>, string[]] | undefined}
1054
+ * @returns {[Params, string[]] | undefined}
982
1055
  */
983
1056
 
984
- /**
985
- * @callback Binder
986
- * @param {import('../main/types').Handler} handler
987
- * @returns {() => void}
988
- */
1057
+ /** @typedef {(handler: Handler, ...handlers: Handler[]) => () => void} Binder */
989
1058
 
990
1059
  /**
991
1060
  * @typedef {object} Route
992
1061
  * @property {Match} [match] 路径匹配
993
1062
  * @property {null} [router]
994
1063
  * @property {string} [plugin] 所属插件
995
- * @property {import('../main/types').Handler} handler 处理函数
996
- * @property {Set<import('../main/types').Method>} methods 方法列表
1064
+ * @property {Handler} handler 处理函数
1065
+ * @property {Set<Method>} methods 方法列表
997
1066
  */
998
1067
 
999
1068
  /**
@@ -1004,16 +1073,16 @@
1004
1073
 
1005
1074
 
1006
1075
  /**
1007
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1076
+ * @template {Router | Finder} [T=ApiRouter]
1008
1077
  * @callback RouteBinder
1009
1078
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1010
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1079
+ * @returns {T extends Finder ? Router : T}
1011
1080
  */
1012
1081
  /**
1013
1082
  *
1014
1083
  * @param {(Route | RouterRoute)[]} routes
1015
- * @param {string} path
1016
- * @param {Router | import('../Router.mjs').Finder} [r]
1084
+ * @param {string | [string[], any[]]} path
1085
+ * @param {Router | Finder} [r]
1017
1086
  * @returns {Router}
1018
1087
  */
1019
1088
  function bindRouter(routes, path, r) {
@@ -1028,18 +1097,18 @@
1028
1097
  #routes = [];
1029
1098
  /**
1030
1099
  * 添加子路由
1031
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1100
+ * @template {Router | Finder} [T=ApiRouter]
1032
1101
  * @overload
1033
1102
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1034
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1103
+ * @returns {T extends Finder ? Router : T}
1035
1104
  */
1036
1105
  /**
1037
1106
  * 添加子路由
1038
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1107
+ * @template {Router | Finder} [T=ApiRouter]
1039
1108
  * @overload
1040
1109
  * @param {string} path 要注册的路径
1041
1110
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1042
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1111
+ * @returns {T extends Finder ? Router : T}
1043
1112
  */
1044
1113
  /**
1045
1114
  * 添加子路由
@@ -1056,12 +1125,11 @@
1056
1125
  route(...p) {
1057
1126
  const [a] = p;
1058
1127
  if (a && typeof a === 'object' && !(a instanceof Router)) {
1059
- const path = String.raw(a, ...p.slice(1));
1060
1128
  /**
1061
- * @param { import('../Router.mjs').Finder | Router} [r];
1129
+ * @param { Finder | Router} [r];
1062
1130
  * @returns {any}
1063
1131
  */
1064
- return r => bindRouter(this.#routes, path, r);
1132
+ return r => bindRouter(this.#routes, [a, p.slice(1)], r);
1065
1133
  }
1066
1134
  const path = typeof a === 'string' ? a : '';
1067
1135
  const r = typeof a === 'string' ? p[1] : a;
@@ -1069,10 +1137,10 @@
1069
1137
  }
1070
1138
  /**
1071
1139
  *
1072
- * @param {import('../main/types').Method} method
1140
+ * @param {Method} method
1073
1141
  * @param {string[]} path
1074
- * @param {import('../main/types').Context} ctx
1075
- * @returns {Iterable<import('../Router.mjs').FindItem>}
1142
+ * @param {Context} ctx
1143
+ * @returns {Iterable<FindItem>}
1076
1144
  */
1077
1145
  *find(method, path, ctx) {
1078
1146
  for (const route of Array.from(this.#routes)) {
@@ -1093,29 +1161,29 @@
1093
1161
  /**
1094
1162
  * 注册处理函数
1095
1163
  * @overload
1096
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1097
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1164
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1165
+ * @param {Handler} handler 要注册的处理函数
1098
1166
  * @returns {() => void}
1099
1167
  */
1100
1168
  /**
1101
1169
  * 注册处理函数
1102
1170
  * @overload
1103
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1171
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1104
1172
  * @param {string} path 要注册的路径
1105
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1173
+ * @param {Handler} handler 要注册的处理函数
1106
1174
  * @returns {() => void}
1107
1175
  */
1108
1176
  /**
1109
1177
  * 注册处理函数
1110
1178
  * @overload
1111
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1179
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1112
1180
  * @param {string} path 要注册的路径
1113
1181
  * @returns {Binder}
1114
1182
  */
1115
1183
  /**
1116
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} methods
1117
- * @param {string| import('../main/types').Handler} [path]
1118
- * @param {import('../main/types').Handler} [handler]
1184
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} methods
1185
+ * @param {string| Handler} [path]
1186
+ * @param {Handler} [handler]
1119
1187
  * @returns {Binder | (() => void)}
1120
1188
  */
1121
1189
  verb(methods, path, handler) {
@@ -1126,14 +1194,14 @@
1126
1194
  /**
1127
1195
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1128
1196
  * @overload
1129
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1197
+ * @param {Handler} handler 要注册的处理函数
1130
1198
  * @returns {() => void}
1131
1199
  */
1132
1200
  /**
1133
1201
  * 注册处理函数
1134
1202
  * @overload
1135
1203
  * @param {string} path 要注册的路径
1136
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1204
+ * @param {Handler} handler 要注册的处理函数
1137
1205
  * @returns {() => void}
1138
1206
  */
1139
1207
  /**
@@ -1159,14 +1227,14 @@
1159
1227
  /**
1160
1228
  * 注册 HTTP GET 处理函数
1161
1229
  * @overload
1162
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1230
+ * @param {Handler} handler 要注册的处理函数
1163
1231
  * @returns {() => void}
1164
1232
  */
1165
1233
  /**
1166
1234
  * 注册 HTTP GET 处理函数
1167
1235
  * @overload
1168
1236
  * @param {string} path 要注册的路径
1169
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1237
+ * @param {Handler} handler 要注册的处理函数
1170
1238
  * @returns {() => void}
1171
1239
  */
1172
1240
  /**
@@ -1190,14 +1258,14 @@
1190
1258
  /**
1191
1259
  * 注册 HTTP POST 处理函数
1192
1260
  * @overload
1193
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1261
+ * @param {Handler} handler 要注册的处理函数
1194
1262
  * @returns {() => void}
1195
1263
  */
1196
1264
  /**
1197
1265
  * 注册 HTTP POST 处理函数
1198
1266
  * @overload
1199
1267
  * @param {string} path 要注册的路径
1200
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1268
+ * @param {Handler} handler 要注册的处理函数
1201
1269
  * @returns {() => void}
1202
1270
  */
1203
1271
  /**
@@ -1221,14 +1289,14 @@
1221
1289
  /**
1222
1290
  * 注册 HTTP PUT 处理函数
1223
1291
  * @overload
1224
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1292
+ * @param {Handler} handler 要注册的处理函数
1225
1293
  * @returns {() => void}
1226
1294
  */
1227
1295
  /**
1228
1296
  * 注册 HTTP PUT 处理函数
1229
1297
  * @overload
1230
1298
  * @param {string} path 要注册的路径
1231
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1299
+ * @param {Handler} handler 要注册的处理函数
1232
1300
  * @returns {() => void}
1233
1301
  */
1234
1302
  /**
@@ -1252,14 +1320,14 @@
1252
1320
  /**
1253
1321
  * 注册 HTTP DELETE 处理函数
1254
1322
  * @overload
1255
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1323
+ * @param {Handler} handler 要注册的处理函数
1256
1324
  * @returns {() => void}
1257
1325
  */
1258
1326
  /**
1259
1327
  * 注册 HTTP DELETE 处理函数
1260
1328
  * @overload
1261
1329
  * @param {string} path 要注册的路径
1262
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1330
+ * @param {Handler} handler 要注册的处理函数
1263
1331
  * @returns {() => void}
1264
1332
  */
1265
1333
  /**
@@ -1283,14 +1351,14 @@
1283
1351
  /**
1284
1352
  * 注册 HTTP HEAD 处理函数
1285
1353
  * @overload
1286
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1354
+ * @param {Handler} handler 要注册的处理函数
1287
1355
  * @returns {() => void}
1288
1356
  */
1289
1357
  /**
1290
1358
  * 注册 HTTP HEAD 处理函数
1291
1359
  * @overload
1292
1360
  * @param {string} path 要注册的路径
1293
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1361
+ * @param {Handler} handler 要注册的处理函数
1294
1362
  * @returns {() => void}
1295
1363
  */
1296
1364
  /**
@@ -1314,14 +1382,14 @@
1314
1382
  /**
1315
1383
  * 注册 HTTP OPTIONS 处理函数
1316
1384
  * @overload
1317
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1385
+ * @param {Handler} handler 要注册的处理函数
1318
1386
  * @returns {() => void}
1319
1387
  */
1320
1388
  /**
1321
1389
  * 注册 HTTP OPTIONS 处理函数
1322
1390
  * @overload
1323
1391
  * @param {string} path 要注册的路径
1324
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1392
+ * @param {Handler} handler 要注册的处理函数
1325
1393
  * @returns {() => void}
1326
1394
  */
1327
1395
  /**
@@ -1367,21 +1435,22 @@
1367
1435
  };
1368
1436
  }
1369
1437
 
1438
+ /** @import { Context, Handler, HandlerResult } from './main/types' */
1370
1439
  /**
1371
1440
  * @callback Onionskin
1372
- * @param {import('./main/types').Context} ctx
1373
- * @param {() => Promise<import('./main/types').HandlerResult>} next
1374
- * @returns {PromiseLike<import('./main/types').HandlerResult> | import('./main/types').HandlerResult}
1441
+ * @param {Context} ctx
1442
+ * @param {() => Promise<HandlerResult>} next
1443
+ * @returns {PromiseLike<HandlerResult> | HandlerResult}
1375
1444
  */
1376
1445
 
1377
1446
  const noop = () => {};
1378
1447
  /**
1379
1448
  *
1380
1449
  * @param {...(Onionskin | Onionskin[])} handlers
1381
- * @returns {import('./main/types').Handler}
1450
+ * @returns {Handler}
1382
1451
  */
1383
1452
  function onionskin(...handlers) {
1384
- /** @type {import('./main/types').Handler} */
1453
+ /** @type {Handler} */
1385
1454
  let handler = noop;
1386
1455
  for (const os of handlers.flat()) {
1387
1456
  const currentHandler = handler;
@@ -1390,7 +1459,44 @@
1390
1459
  return handler;
1391
1460
  }
1392
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
+
1393
1498
  exports.ApiRouter = ApiRouter;
1499
+ exports.Param = Param;
1394
1500
  exports.Router = Router;
1395
1501
  exports.createFetch = createFetch;
1396
1502
  exports.main = main;