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.cjs 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
  'use strict';
@@ -13,20 +13,6 @@
13
13
  function str2utf8bin(str) {
14
14
  return new TextEncoder().encode(str);
15
15
  }
16
- /**
17
- *
18
- * @param {unknown} chunk
19
- * @returns {chunk is ArrayBuffer | SharedArrayBuffer}
20
- */
21
- function isBufferSource(chunk) {
22
- if (chunk instanceof ArrayBuffer) { return true; }
23
- try {
24
- if (chunk instanceof SharedArrayBuffer) { return true; }
25
- } catch {
26
-
27
- }
28
- return false;
29
- }
30
16
  /**
31
17
  *
32
18
  * @template T
@@ -51,7 +37,7 @@ async function write(writer, chunk) {
51
37
  if (ArrayBuffer.isView(chunk)) {
52
38
  return writer.write(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
53
39
  }
54
- if (isBufferSource(chunk)) {
40
+ if (chunk instanceof ArrayBuffer) {
55
41
  return writer.write(new Uint8Array(chunk));
56
42
  }
57
43
  if (!isIterable(chunk)) {
@@ -95,7 +81,7 @@ function toBodyData(result, aborted) {
95
81
  if (result instanceof FormData) {
96
82
  return [result, 0, ''];
97
83
  }
98
- if (ArrayBuffer.isView(result) || isBufferSource(result)) {
84
+ if (ArrayBuffer.isView(result) || result instanceof ArrayBuffer) {
99
85
  return [result, result.byteLength, ''];
100
86
  }
101
87
  if (typeof result === 'string') {
@@ -120,8 +106,7 @@ function toBodyData(result, aborted) {
120
106
  if (!data) { continue; }
121
107
  await write(writer, data);
122
108
  }
123
- await writable.close();
124
- })().catch(() => {});
109
+ })().then(() => writable.close(), r => writable.abort(r)).catch(() => {});
125
110
  return [readable, 0, ''];
126
111
  }
127
112
  /**
@@ -144,11 +129,12 @@ function toBody(result, headers, aborted) {
144
129
  return body;
145
130
  }
146
131
 
132
+ /** @import { Cookie, CookieOption } from './types' */
147
133
  /**
148
134
  *
149
- * @param {import('./types').Cookie[]} sentCookies
135
+ * @param {Cookie[]} sentCookies
150
136
  * @param {string} [name]
151
- * @returns {Iterable<import('./types').Cookie>}
137
+ * @returns {Iterable<Cookie>}
152
138
  */
153
139
  function *getCookie(sentCookies, name) {
154
140
  const list = sentCookies;
@@ -161,7 +147,7 @@ function *getCookie(sentCookies, name) {
161
147
  /**
162
148
  *
163
149
  * @param {Headers} headers
164
- * @param {import('./types').Cookie[]} cookies
150
+ * @param {Cookie[]} cookies
165
151
  * @returns {void}
166
152
  */
167
153
  function setCookiesHeader(headers, cookies) {
@@ -195,10 +181,10 @@ function getRequestCookies(cookie) {
195
181
  }
196
182
  /**
197
183
  *
198
- * @param {import('./types').Cookie[]} sentCookies
184
+ * @param {Cookie[]} sentCookies
199
185
  * @param {Record<string, string>} cookies
200
- * @param {string | import('./types').CookieOption} [name]
201
- * @param {import('./types').CookieOption | boolean} [opt]
186
+ * @param {string | CookieOption} [name]
187
+ * @param {CookieOption | boolean} [opt]
202
188
  * @returns {void}
203
189
  */
204
190
  function clearCookie(
@@ -210,11 +196,11 @@ function clearCookie(
210
196
  let expire = 'Fri, 31 Dec 1999 16:00:00 GMT';
211
197
  if (typeof name === 'string') {
212
198
  if (!name) { return; }
213
- /** @type {import('./types').CookieOption} */
199
+ /** @type {CookieOption} */
214
200
  const { domain, path, secure, httpOnly } = opt !== true && opt || {};
215
201
  sentCookies.push({ name, value: 'delete', expire, domain, path, secure, httpOnly });
216
202
  } else {
217
- /** @type {import('./types').CookieOption} */
203
+ /** @type {CookieOption} */
218
204
  const { domain, path, secure, httpOnly } = name || {};
219
205
  sentCookies.length = 0;
220
206
  if (opt) {
@@ -225,6 +211,9 @@ function clearCookie(
225
211
  }
226
212
  }
227
213
 
214
+ /** @import { Context, Cookie, CookieOption, FindHandler, Method, Options, Params, Service } from './types' */
215
+
216
+
228
217
  const noBodyMethods = new Set(['GET', 'OPTIONS']);
229
218
  /**
230
219
  *
@@ -261,7 +250,7 @@ function setHeader(headers, name, value) {
261
250
  *
262
251
  * @param {Request} request
263
252
  * @param {string | ((request: Request) => string)} [toMethod]
264
- * @returns {import('./types').Method}
253
+ * @returns {Method}
265
254
  */
266
255
  function getMethod(request, toMethod) {
267
256
  let methodStr = '';
@@ -273,13 +262,13 @@ function getMethod(request, toMethod) {
273
262
  if (!methodStr || typeof methodStr !== 'string') {
274
263
  methodStr = request.method || 'GET';
275
264
  }
276
- return /** @type {import('./types').Method} */(methodStr.toUpperCase());
265
+ return /** @type {Method} */(methodStr.toUpperCase());
277
266
  }
278
267
  /**
279
268
  *
280
269
  * @param {Request} request
281
- * @param {import('./types').FindHandler} getHandler
282
- * @param {import('./types').Options} [options]
270
+ * @param {FindHandler} getHandler
271
+ * @param {Options} [options]
283
272
  * @returns {Promise<Response | null>}
284
273
  */
285
274
  function main(
@@ -289,7 +278,7 @@ function main(
289
278
  /**
290
279
  *
291
280
  * @param {Request} request
292
- * @param {import('./types').Context} [parent]
281
+ * @param {Context} [parent]
293
282
  * @returns {Promise<Response | null>}
294
283
  */
295
284
  function exec(request, parent) {
@@ -297,10 +286,10 @@ function main(
297
286
  const url = new URL(request.url);
298
287
  const { signal, headers } = request;
299
288
  const aborted = signal2promise(signal);
300
- /** @type {Map<import('./types').Service<any, any>, Function>} */
289
+ /** @type {Map<Service<any, any>, Function>} */
301
290
  const services = new Map();
302
291
  const cookies = getRequestCookies(headers.get('cookie') || '');
303
- /** @type {import('./types').Cookie[]} */
292
+ /** @type {Cookie[]} */
304
293
  const sentCookies = [];
305
294
  const responseHeaders = new Headers();
306
295
  const root = parent?.root;
@@ -316,9 +305,9 @@ function main(
316
305
  const donePromise = new Promise((a, b) => { resolve = a; reject = b; });
317
306
  donePromise.catch(() => {});
318
307
 
319
- /** @type {any} */
308
+ /** @type {Params} */
320
309
  let params = {};
321
- /** @type {import('./types').Context} */
310
+ /** @type {Context} */
322
311
  const context = {
323
312
  environment,
324
313
  parent,
@@ -382,8 +371,8 @@ function main(
382
371
  },
383
372
  /**
384
373
  *
385
- * @param {string | import('./types').CookieOption} [name]
386
- * @param {import('./types').CookieOption | boolean} [opt]
374
+ * @param {string | CookieOption} [name]
375
+ * @param {CookieOption | boolean} [opt]
387
376
  * @returns {void}
388
377
  */
389
378
  clearCookie(name, opt) {
@@ -420,69 +409,72 @@ function main(
420
409
  return exec(request);
421
410
  }
422
411
 
412
+ /** @import { FindHandler, Options } from './main/types' */
413
+
423
414
  /**
424
415
  *
425
- * @param {import('./main/types').FindHandler} getHandler
426
- * @param {import('./main/types').Options} options
416
+ * @param {FindHandler} getHandler
417
+ * @param {Options} options
427
418
  * @returns {(request: Request) => Promise<Response | null>}
428
419
  */
429
420
  function make(getHandler, options) {
430
421
  return r => main(r, getHandler, options);
431
422
  }
432
423
 
424
+ /** @import { Context, Handler } from './main/types' */
433
425
  /**
434
426
  *
435
- * @param {import('./main/types').Context} context
436
- * @param {import('./main/types').Handler[]} handlers
427
+ * @param {Context} context
428
+ * @param {Handler[]} handlers
437
429
  * @returns {Promise<string | boolean | object | undefined>}
438
430
  */
439
431
  async function runHandles(context, handlers) {
440
432
  for (const handle of handlers) {
441
433
  const result = await handle(context);
442
- if (typeof result === 'boolean') { return result; }
443
- if (!result) { continue; }
434
+ if (result === undefined) { continue; }
444
435
  return result;
445
436
  }
446
437
  }
447
438
 
448
439
  /**
449
440
  *
450
- * @param {...(import('./main/types').Handler | import('./main/types').Handler[])} handlers
451
- * @returns {import('./main/types').Handler}
441
+ * @param {...(Handler | Handler[])} handlers
442
+ * @returns {Handler}
452
443
  */
453
444
  function merge(...handlers) {
454
445
  return ctx => runHandles(ctx, handlers.flat());
455
446
  }
456
447
 
448
+ /** @import { Context, Service } from './main/types' */
457
449
  /**
458
450
  *
459
451
  * @template T
460
452
  * @template {any[]} P
461
453
  * @overload
462
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
463
- * @param {((ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
464
- * @param {import('./main/types').Service.Options?} [options]
465
- * @returns {import('./main/types').Service<T, P>}
454
+ * @param {(ctx: Context, ...p: P) => T} exec
455
+ * @param {((ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
456
+ * @param {Service.Options?} [options]
457
+ * @returns {Service<T, P>}
466
458
  */
467
459
  /**
468
460
  *
469
461
  * @template T
470
462
  * @template {any[]} P
471
463
  * @overload
472
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
473
- * @param {import('./main/types').Service.Options?} [options]
474
- * @returns {import('./main/types').Service<T, P>}
464
+ * @param {(ctx: Context, ...p: P) => T} exec
465
+ * @param {Service.Options?} [options]
466
+ * @returns {Service<T, P>}
475
467
  */
476
468
  /**
477
469
  * @template T
478
470
  * @template {any[]} P
479
- * @param {(ctx: import('./main/types').Context, ...p: P) => T} exec
480
- * @param {((ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
481
- * @param {import('./main/types').Service.Options?} [options]
482
- * @returns {import('./main/types').Service<T, P>}
471
+ * @param {(ctx: Context, ...p: P) => T} exec
472
+ * @param {((ctx: Context, error?: unknown) => PromiseLike<void> | void) | Service.Options | null} [destroy]
473
+ * @param {Service.Options?} [options]
474
+ * @returns {Service<T, P>}
483
475
  */
484
476
  function service(exec, destroy, options) {
485
- /** @type {import('./main/types').Service<T, P>} */
477
+ /** @type {Service<T, P>} */
486
478
  const service = function (ctx) {
487
479
  if (typeof destroy === 'function') {
488
480
  ctx.done(() => destroy(ctx), error => destroy(ctx, error));
@@ -501,43 +493,44 @@ function service(exec, destroy, options) {
501
493
  return service;
502
494
  }
503
495
 
496
+ /** @import { Context, Service, StateService } from './main/types' */
504
497
  /**
505
498
  *
506
499
  * @template T
507
500
  * @overload
508
- * @param {(ctx: import('./main/types').Context) => T} init
509
- * @param {import('./main/types').Service.Options} [options]
510
- * @returns {import('./main/types').StateService<T>}
501
+ * @param {(ctx: Context) => T} init
502
+ * @param {Service.Options} [options]
503
+ * @returns {StateService<T>}
511
504
  */
512
505
  /**
513
506
  *
514
507
  * @template T
515
508
  * @overload
516
- * @param {(ctx: import('./main/types').Context) => T} init
517
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
518
- * @param {import('./main/types').Service.Options?} [options]
519
- * @returns {import('./main/types').StateService<T>}
509
+ * @param {(ctx: Context) => T} init
510
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
511
+ * @param {Service.Options?} [options]
512
+ * @returns {StateService<T>}
520
513
  */
521
514
  /**
522
515
  *
523
516
  * @template T
524
517
  * @overload
525
- * @param {(ctx: import('./main/types').Context) => T} init
526
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
527
- * @param {((state: T, ctx: import('./main/types').Context) => any)?} [exec]
528
- * @param {import('./main/types').Service.Options?} [options]
529
- * @returns {import('./main/types').StateService<T>}
518
+ * @param {(ctx: Context) => T} init
519
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
520
+ * @param {((state: T, ctx: Context) => any)?} [exec]
521
+ * @param {Service.Options?} [options]
522
+ * @returns {StateService<T>}
530
523
  */
531
524
  /**
532
525
  * @template T
533
- * @param {(ctx: import('./main/types').Context) => T} init
534
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
535
- * @param {((state: T, ctx: import('./main/types').Context) => any) | import('./main/types').Service.Options | null} [exec]
536
- * @param {import('./main/types').Service.Options?} [options]
537
- * @returns {import('./main/types').StateService<T>}
526
+ * @param {(ctx: Context) => T} init
527
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void) | Service.Options | null} [destroy]
528
+ * @param {((state: T, ctx: Context) => any) | Service.Options | null} [exec]
529
+ * @param {Service.Options?} [options]
530
+ * @returns {StateService<T>}
538
531
  */
539
532
  function stateService(init, destroy, exec, options) {
540
- /** @type {import('./main/types').StateService<T>} */
533
+ /** @type {StateService<T>} */
541
534
  const service = function (ctx) {
542
535
  const state = init(ctx) || /** @type {T} */({});
543
536
  if (typeof destroy === 'function') {
@@ -561,39 +554,40 @@ function stateService(init, destroy, exec, options) {
561
554
  return service;
562
555
  }
563
556
 
557
+ /** @import { Context, Service, StoreService } from './main/types' */
564
558
  /**
565
559
  *
566
560
  * @template T
567
561
  * @overload
568
- * @param {import('./main/types').Service.Options?} [options]
569
- * @returns {import('./main/types').StoreService<T>}
562
+ * @param {Service.Options?} [options]
563
+ * @returns {StoreService<T>}
570
564
  */
571
565
  /**
572
566
  *
573
567
  * @template T
574
568
  * @overload
575
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
576
- * @param {import('./main/types').Service.Options?} [options]
577
- * @returns {import('./main/types').StoreService<T>}
569
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
570
+ * @param {Service.Options?} [options]
571
+ * @returns {StoreService<T>}
578
572
  */
579
573
  /**
580
574
  *
581
575
  * @template T
582
576
  * @overload
583
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
584
- * @param {((state: T | undefined, ctx: import('./main/types').Context) => any)?} [exec]
585
- * @param {import('./main/types').Service.Options?} [options]
586
- * @returns {import('./main/types').StoreService<T>}
577
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void)?} [destroy]
578
+ * @param {((state: T | undefined, ctx: Context) => any)?} [exec]
579
+ * @param {Service.Options?} [options]
580
+ * @returns {StoreService<T>}
587
581
  */
588
582
  /**
589
583
  * @template T
590
- * @param {((state: T | undefined, ctx: import('./main/types').Context, error?: unknown) => PromiseLike<void> | void) | import('./main/types').Service.Options | null} [destroy]
591
- * @param {((state: T | undefined, ctx: import('./main/types').Context) => any) | import('./main/types').Service.Options | null} [exec]
592
- * @param {import('./main/types').Service.Options?} [options]
593
- * @returns {import('./main/types').StoreService<T>}
584
+ * @param {((state: T | undefined, ctx: Context, error?: unknown) => PromiseLike<void> | void) | Service.Options | null} [destroy]
585
+ * @param {((state: T | undefined, ctx: Context) => any) | Service.Options | null} [exec]
586
+ * @param {Service.Options?} [options]
587
+ * @returns {StoreService<T>}
594
588
  */
595
589
  function storeService(destroy, exec, options) {
596
- /** @type {import('./main/types').StoreService<T>} */
590
+ /** @type {StoreService<T>} */
597
591
  const service = function (ctx) {
598
592
  /** @type {T | undefined} */
599
593
  let state;
@@ -630,16 +624,18 @@ function storeService(destroy, exec, options) {
630
624
  return service;
631
625
  }
632
626
 
627
+ /** @import { Handler } from './main/types' */
628
+ /** @import { Onionskin } from './onionskin.mjs' */
633
629
  /**
634
630
  * @callback Packer
635
- * @param {import('./main/types').Handler} handler
636
- * @returns {import('./main/types').Handler}
631
+ * @param {Handler} handler
632
+ * @returns {Handler}
637
633
  */
638
634
  /** @type {Packer} */
639
635
  const noop$1 = h => h;
640
636
  /**
641
637
  *
642
- * @param {import('./onionskin.mjs').Onionskin} onionskin
638
+ * @param {Onionskin} onionskin
643
639
  * @param {Packer} [packer]
644
640
  * @returns {Packer}
645
641
  */
@@ -650,30 +646,33 @@ function packer(onionskin, packer = noop$1) {
650
646
  };
651
647
  }
652
648
 
649
+ /** @import { Context, FindHandler, Handler, Method, Params } from './main/types' */
650
+ /** @import { Onionskin } from './onionskin.mjs' */
651
+
653
652
  /**
654
653
  * @callback Guard
655
- * @param {import('./main/types').Context} ctx
656
- * @returns {PromiseLike<boolean | import('./main/types').Handler | void> | boolean | import('./main/types').Handler | void}
654
+ * @param {Context} ctx
655
+ * @returns {PromiseLike<boolean | Handler | void> | boolean | Handler | void}
657
656
  */
658
657
  /**
659
- * @typedef {[import('./main/types').Handler | Router, Record<string, any>, string[]]} FindItem
658
+ * @typedef {[Handler | Router, Record<string | symbol, any>, string[]]} FindItem
660
659
  */
661
660
  /**
662
661
  * @callback Finder
663
662
  * @this {Router}
664
- * @param {import('./main/types').Method} method
663
+ * @param {Method} method
665
664
  * @param {string[]} path
666
- * @param {import('./main/types').Context} ctx
665
+ * @param {Context} ctx
667
666
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
668
667
  */
669
668
 
670
669
  /**
671
670
  *
672
671
  * @param {Set<Guard>} guards
673
- * @param {import('./main/types').Context} ctx
672
+ * @param {Context} ctx
674
673
  * @param {(v: any) => void} setParams
675
674
  * @param {object} params
676
- * @returns {Promise<boolean | import('./main/types').Handler>}
675
+ * @returns {Promise<boolean | Handler>}
677
676
  */
678
677
  async function execGuard(guards, ctx, setParams, params) {
679
678
  if (!guards.size) { return true; }
@@ -692,12 +691,12 @@ async function execGuard(guards, ctx, setParams, params) {
692
691
 
693
692
  /**
694
693
  *
695
- * @param {Router | import('./main/types').Handler} route
694
+ * @param {Router | Handler} route
696
695
  * @param {string[]} path
697
- * @param {import('./main/types').Context} ctx
698
- * @param {(v: any) => void} setParams
699
- * @param {object} params
700
- * @returns {Promise<import('./main/types').Handler | null>}
696
+ * @param {Context} ctx
697
+ * @param {(v: Params) => void} setParams
698
+ * @param {Params} params
699
+ * @returns {Promise<Handler | null>}
701
700
  */
702
701
  async function find(route, path, ctx, setParams, params) {
703
702
  if (!(route instanceof Router)) {
@@ -736,16 +735,16 @@ class Router {
736
735
  disabled = false;
737
736
  /**
738
737
  * @abstract
739
- * @param {import('./main/types').Method} method
738
+ * @param {Method} method
740
739
  * @param {string[]} path
741
- * @param {import('./main/types').Context} ctx
740
+ * @param {Context} ctx
742
741
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
743
742
  */
744
743
  find(method, path, ctx) { return []; }
745
744
  /**
746
745
  *
747
746
  * @param {Router[]} routers
748
- * @returns {import('./main/types').FindHandler}
747
+ * @returns {FindHandler}
749
748
  */
750
749
  static make(routers) {
751
750
  return async (ctx, setParams) => {
@@ -773,19 +772,21 @@ class Router {
773
772
  guards = new Set();
774
773
  /**
775
774
  *
776
- * @param {import('./main/types').Handler} h
777
- * @returns {import('./main/types').Handler}
775
+ * @param {Handler} h
776
+ * @returns {Handler}
778
777
  */
779
778
  __onionskin = (h) => h;
780
- /** @param {import('./onionskin.mjs').Onionskin} os */
779
+ /** @param {Onionskin} os */
781
780
  onionskin(os) { this.__onionskin = packer(os, this.__onionskin); }
782
781
  }
783
782
 
783
+ /** @import { Match } from './index.mjs' */
784
+ /** @import { Params } from '../main/types.js' */
784
785
  /**
785
786
  * @typedef {object} Pattern
786
- * @property {string} name
787
- * @property {boolean} optional
788
- * @property {boolean} many
787
+ * @property {string | symbol} name
788
+ * @property {boolean} [optional]
789
+ * @property {boolean} [many]
789
790
  * @property {RegExp} pattern
790
791
  */
791
792
 
@@ -856,7 +857,7 @@ function parse(p) {
856
857
  * @returns {[Record<string, string | string[]>, string[]] | undefined}
857
858
  */
858
859
  function exec(match, path, end) {
859
- /** @type {Record<string, string | string[]>} */
860
+ /** @type {Params} */
860
861
  const params = {};
861
862
  for (let i = 0; i < match.length; i++) {
862
863
  const m = match[i];
@@ -879,38 +880,101 @@ function exec(match, path, end) {
879
880
  return [params, []];
880
881
 
881
882
  }
883
+ /**
884
+ *
885
+ * @param {string[]} paths
886
+ * @param {*} values
887
+ * @returns {Iterable<[string[], any[]]>}
888
+ */
889
+ function* split([...paths], [...values]) {
890
+ let els = (paths.shift() || '').split('/');
891
+ let list = [els.pop() || ''];
892
+ for (const f of els) {
893
+ yield [[f], []];
894
+ }
895
+ for (const path of paths) {
896
+ const els = path.split('/');
897
+ if (els.length <= 1) {
898
+ list.push(path);
899
+ continue;
900
+ }
901
+ const pathValue = values.splice(0, list.length);
902
+ list.push(els.shift() || '');
903
+ yield [list, pathValue];
904
+ list = [els.pop() || ''];
905
+ for (const f of els) {
906
+ yield [[f], []];
907
+ }
908
+ }
909
+ yield [list, values];
910
+ }
882
911
  /**
883
912
  *
884
- * @param {string} path
913
+ * @param {string | [string[], any[]]} path
885
914
  * @param {boolean} end
886
- * @returns {import('./index.mjs').Match | undefined}
915
+ * @returns {Match | undefined}
887
916
  */
888
917
  function toMatch(path, end) {
889
918
  /** @type {(Pattern | string)[]} */
890
919
  const list = [];
891
- for (const p of path.split('/')) {
892
- if (!p || /^\.+$/.test(p)) { continue; }
893
- list.push(parse(p));
920
+ if (typeof path === 'string') {
921
+ for (const p of path.split('/')) {
922
+ if (!p || /^\.+$/.test(p)) { continue; }
923
+ list.push(parse(p));
924
+ }
925
+ } else {
926
+ for (const [paths, values] of split(...path)) {
927
+ if (paths.length === 2 && !paths[0]) {
928
+ const modifier = paths[1];
929
+ if (['', '?', '+', '*'].includes(modifier)) {
930
+ const value = values[0];
931
+ if (typeof value === 'symbol') {
932
+ list.push({
933
+ name: value, pattern: /^.*$/,
934
+ optional: modifier === '?' || modifier === '*',
935
+ many: modifier === '+' || modifier === '*',
936
+ });
937
+ continue;
938
+ } else if (value && typeof value === 'object') {
939
+ const {name, pattern} = value;
940
+ if (typeof name === 'symbol') {
941
+ list.push({
942
+ name, pattern: pattern instanceof RegExp ? pattern : /^.*$/,
943
+ optional: modifier === '?' || modifier === '*',
944
+ many: modifier === '+' || modifier === '*',
945
+ });
946
+ }
947
+ }
948
+ }
949
+ }
950
+ const last = paths.pop() || '';
951
+ const strings = paths.map((v, i) => [v, values[i]]).flat();
952
+ strings.push(last);
953
+ const p = strings.join('');
954
+ if (!p || /^\.+$/.test(p)) { continue; }
955
+ list.push(parse(p));
956
+ continue;
957
+ }
958
+
894
959
  }
895
960
  if (!list.length) { return; }
896
961
  return path => exec(list, path, end);
897
962
  }
898
963
 
964
+ /** @import { Handler, Method } from '../main/types' */
965
+ /** @import { Binder, Match, Route, RouterRoute } from './index.mjs' */
966
+
899
967
  /**
900
968
  *
901
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
902
- * @param {import('../main/types').Method[]} methods
903
- * @param {string} path
904
- * @param {import('../main/types').Handler} handler
969
+ * @param {(Route | RouterRoute)[]} routes
970
+ * @param {Set<Method>} methods
971
+ * @param {Match | undefined} match
972
+ * @param {Handler[]} handlers
905
973
  * @returns {() => void}
906
974
  */
907
- function bind(routes, methods, path, handler) {
908
- /** @type {import('./index.mjs').Route} */
909
- const route = {
910
- match: toMatch(path || '', true),
911
- methods: new Set(methods),
912
- handler,
913
- };
975
+ function bind(routes, methods, match, handlers) {
976
+ /** @type {Route} */
977
+ const route = { match, methods, handler: ctx => runHandles(ctx, handlers) };
914
978
  routes.push(route);
915
979
  let removed = false;
916
980
  return () => {
@@ -921,43 +985,49 @@ function bind(routes, methods, path, handler) {
921
985
  routes.splice(index, 1);
922
986
  };
923
987
  }
924
- /** @type {(v: any) => v is import('../main/types').Handler} */
988
+ /** @type {(v: any) => v is Handler} */
925
989
  const findHandler = v => typeof v === 'function';
926
990
  /**
927
991
  *
928
- * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
929
- * @param {import('../main/types').Method[]} methods
992
+ * @param {(Route | RouterRoute)[]} routes
993
+ * @param {Iterable<Method>} methods
930
994
  * @param {any[]} p
931
- * @returns {import('./index.mjs').Binder | (() => void)}
995
+ * @returns {Binder | (() => void)}
932
996
  */
933
997
  function verb(routes, methods, p) {
998
+ const methodSet = new Set(methods);
934
999
  if (!p.length) {
935
- return handler => bind(routes, methods, '', handler);
1000
+ const match = undefined;
1001
+ /** @type {Binder} */
1002
+ return (...handlers) => bind(routes, methodSet, match, handlers);
936
1003
  }
937
- const [a, b] = p;
938
- if (a && typeof a === 'object') {
939
- const path = String.raw(a, ...p.slice(1));
940
- return handler => bind(routes, methods, path, handler);
1004
+ const [path] = p;
1005
+ if (path && typeof path === 'object') {
1006
+ const match = toMatch([path, p.slice(1)], true);
1007
+ /** @type {Binder} */
1008
+ return (...handlers) => bind(routes, methodSet, match, handlers);
941
1009
  }
942
- const path = typeof a === 'string' ? a : '';
943
- const handler = [a, b].find(findHandler);
944
- if (!handler) {
945
- return handler => bind(routes, methods, path, handler);
1010
+ const match = toMatch(typeof path === 'string' ? path : '', true);
1011
+ const handlers = p.filter(findHandler);
1012
+ if (!handlers.length) {
1013
+ /** @type {Binder} */
1014
+ return (...handlers) => bind(routes, methodSet, match, handlers);
946
1015
  }
947
- return bind(routes, methods, path, handler);
1016
+ return bind(routes, methodSet, match, handlers);
948
1017
  }
949
1018
 
1019
+ /** @import { Method } from '../main/types' */
950
1020
  const methods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']);
951
1021
  /**
952
1022
  *
953
1023
  * @param {any} v
954
- * @returns {v is import('../main/types').Method}
1024
+ * @returns {v is Method}
955
1025
  */
956
1026
  function isMethod(v) { return methods.has(v); }
957
1027
  /**
958
1028
  *
959
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} [methods]
960
- * @returns {import('../main/types').Method[]}
1029
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} [methods]
1030
+ * @returns {Method[]}
961
1031
  */
962
1032
  function getMethods(methods) {
963
1033
  if (!methods) {
@@ -971,25 +1041,24 @@ function getMethods(methods) {
971
1041
  .filter(isMethod);
972
1042
  }
973
1043
 
1044
+ /** @import { Context, Handler, Method, Params } from '../main/types' */
1045
+ /** @import { Finder, FindItem } from '../Router.mjs' */
1046
+
974
1047
  /**
975
1048
  * @callback Match
976
1049
  * @param {string[]} paths
977
- * @returns {[Record<string, string | string[]>, string[]] | undefined}
1050
+ * @returns {[Params, string[]] | undefined}
978
1051
  */
979
1052
 
980
- /**
981
- * @callback Binder
982
- * @param {import('../main/types').Handler} handler
983
- * @returns {() => void}
984
- */
1053
+ /** @typedef {(handler: Handler, ...handlers: Handler[]) => () => void} Binder */
985
1054
 
986
1055
  /**
987
1056
  * @typedef {object} Route
988
1057
  * @property {Match} [match] 路径匹配
989
1058
  * @property {null} [router]
990
1059
  * @property {string} [plugin] 所属插件
991
- * @property {import('../main/types').Handler} handler 处理函数
992
- * @property {Set<import('../main/types').Method>} methods 方法列表
1060
+ * @property {Handler} handler 处理函数
1061
+ * @property {Set<Method>} methods 方法列表
993
1062
  */
994
1063
 
995
1064
  /**
@@ -1000,16 +1069,16 @@ function getMethods(methods) {
1000
1069
 
1001
1070
 
1002
1071
  /**
1003
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1072
+ * @template {Router | Finder} [T=ApiRouter]
1004
1073
  * @callback RouteBinder
1005
1074
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1006
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1075
+ * @returns {T extends Finder ? Router : T}
1007
1076
  */
1008
1077
  /**
1009
1078
  *
1010
1079
  * @param {(Route | RouterRoute)[]} routes
1011
- * @param {string} path
1012
- * @param {Router | import('../Router.mjs').Finder} [r]
1080
+ * @param {string | [string[], any[]]} path
1081
+ * @param {Router | Finder} [r]
1013
1082
  * @returns {Router}
1014
1083
  */
1015
1084
  function bindRouter(routes, path, r) {
@@ -1024,18 +1093,18 @@ class ApiRouter extends Router {
1024
1093
  #routes = [];
1025
1094
  /**
1026
1095
  * 添加子路由
1027
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1096
+ * @template {Router | Finder} [T=ApiRouter]
1028
1097
  * @overload
1029
1098
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1030
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1099
+ * @returns {T extends Finder ? Router : T}
1031
1100
  */
1032
1101
  /**
1033
1102
  * 添加子路由
1034
- * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1103
+ * @template {Router | Finder} [T=ApiRouter]
1035
1104
  * @overload
1036
1105
  * @param {string} path 要注册的路径
1037
1106
  * @param {T} [router] 要注册的子路由或子路由的 Finder
1038
- * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1107
+ * @returns {T extends Finder ? Router : T}
1039
1108
  */
1040
1109
  /**
1041
1110
  * 添加子路由
@@ -1052,12 +1121,11 @@ class ApiRouter extends Router {
1052
1121
  route(...p) {
1053
1122
  const [a] = p;
1054
1123
  if (a && typeof a === 'object' && !(a instanceof Router)) {
1055
- const path = String.raw(a, ...p.slice(1));
1056
1124
  /**
1057
- * @param { import('../Router.mjs').Finder | Router} [r];
1125
+ * @param { Finder | Router} [r];
1058
1126
  * @returns {any}
1059
1127
  */
1060
- return r => bindRouter(this.#routes, path, r);
1128
+ return r => bindRouter(this.#routes, [a, p.slice(1)], r);
1061
1129
  }
1062
1130
  const path = typeof a === 'string' ? a : '';
1063
1131
  const r = typeof a === 'string' ? p[1] : a;
@@ -1065,10 +1133,10 @@ class ApiRouter extends Router {
1065
1133
  }
1066
1134
  /**
1067
1135
  *
1068
- * @param {import('../main/types').Method} method
1136
+ * @param {Method} method
1069
1137
  * @param {string[]} path
1070
- * @param {import('../main/types').Context} ctx
1071
- * @returns {Iterable<import('../Router.mjs').FindItem>}
1138
+ * @param {Context} ctx
1139
+ * @returns {Iterable<FindItem>}
1072
1140
  */
1073
1141
  *find(method, path, ctx) {
1074
1142
  for (const route of Array.from(this.#routes)) {
@@ -1089,29 +1157,29 @@ class ApiRouter extends Router {
1089
1157
  /**
1090
1158
  * 注册处理函数
1091
1159
  * @overload
1092
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1093
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1160
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1161
+ * @param {Handler} handler 要注册的处理函数
1094
1162
  * @returns {() => void}
1095
1163
  */
1096
1164
  /**
1097
1165
  * 注册处理函数
1098
1166
  * @overload
1099
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1167
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1100
1168
  * @param {string} path 要注册的路径
1101
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1169
+ * @param {Handler} handler 要注册的处理函数
1102
1170
  * @returns {() => void}
1103
1171
  */
1104
1172
  /**
1105
1173
  * 注册处理函数
1106
1174
  * @overload
1107
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1175
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} method 要注册的方法
1108
1176
  * @param {string} path 要注册的路径
1109
1177
  * @returns {Binder}
1110
1178
  */
1111
1179
  /**
1112
- * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} methods
1113
- * @param {string| import('../main/types').Handler} [path]
1114
- * @param {import('../main/types').Handler} [handler]
1180
+ * @param {Method | Iterable<Method> | ArrayLike<Method>} methods
1181
+ * @param {string| Handler} [path]
1182
+ * @param {Handler} [handler]
1115
1183
  * @returns {Binder | (() => void)}
1116
1184
  */
1117
1185
  verb(methods, path, handler) {
@@ -1122,14 +1190,14 @@ class ApiRouter extends Router {
1122
1190
  /**
1123
1191
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1124
1192
  * @overload
1125
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1193
+ * @param {Handler} handler 要注册的处理函数
1126
1194
  * @returns {() => void}
1127
1195
  */
1128
1196
  /**
1129
1197
  * 注册处理函数
1130
1198
  * @overload
1131
1199
  * @param {string} path 要注册的路径
1132
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1200
+ * @param {Handler} handler 要注册的处理函数
1133
1201
  * @returns {() => void}
1134
1202
  */
1135
1203
  /**
@@ -1155,14 +1223,14 @@ class ApiRouter extends Router {
1155
1223
  /**
1156
1224
  * 注册 HTTP GET 处理函数
1157
1225
  * @overload
1158
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1226
+ * @param {Handler} handler 要注册的处理函数
1159
1227
  * @returns {() => void}
1160
1228
  */
1161
1229
  /**
1162
1230
  * 注册 HTTP GET 处理函数
1163
1231
  * @overload
1164
1232
  * @param {string} path 要注册的路径
1165
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1233
+ * @param {Handler} handler 要注册的处理函数
1166
1234
  * @returns {() => void}
1167
1235
  */
1168
1236
  /**
@@ -1186,14 +1254,14 @@ class ApiRouter extends Router {
1186
1254
  /**
1187
1255
  * 注册 HTTP POST 处理函数
1188
1256
  * @overload
1189
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1257
+ * @param {Handler} handler 要注册的处理函数
1190
1258
  * @returns {() => void}
1191
1259
  */
1192
1260
  /**
1193
1261
  * 注册 HTTP POST 处理函数
1194
1262
  * @overload
1195
1263
  * @param {string} path 要注册的路径
1196
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1264
+ * @param {Handler} handler 要注册的处理函数
1197
1265
  * @returns {() => void}
1198
1266
  */
1199
1267
  /**
@@ -1217,14 +1285,14 @@ class ApiRouter extends Router {
1217
1285
  /**
1218
1286
  * 注册 HTTP PUT 处理函数
1219
1287
  * @overload
1220
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1288
+ * @param {Handler} handler 要注册的处理函数
1221
1289
  * @returns {() => void}
1222
1290
  */
1223
1291
  /**
1224
1292
  * 注册 HTTP PUT 处理函数
1225
1293
  * @overload
1226
1294
  * @param {string} path 要注册的路径
1227
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1295
+ * @param {Handler} handler 要注册的处理函数
1228
1296
  * @returns {() => void}
1229
1297
  */
1230
1298
  /**
@@ -1248,14 +1316,14 @@ class ApiRouter extends Router {
1248
1316
  /**
1249
1317
  * 注册 HTTP DELETE 处理函数
1250
1318
  * @overload
1251
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1319
+ * @param {Handler} handler 要注册的处理函数
1252
1320
  * @returns {() => void}
1253
1321
  */
1254
1322
  /**
1255
1323
  * 注册 HTTP DELETE 处理函数
1256
1324
  * @overload
1257
1325
  * @param {string} path 要注册的路径
1258
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1326
+ * @param {Handler} handler 要注册的处理函数
1259
1327
  * @returns {() => void}
1260
1328
  */
1261
1329
  /**
@@ -1279,14 +1347,14 @@ class ApiRouter extends Router {
1279
1347
  /**
1280
1348
  * 注册 HTTP HEAD 处理函数
1281
1349
  * @overload
1282
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1350
+ * @param {Handler} handler 要注册的处理函数
1283
1351
  * @returns {() => void}
1284
1352
  */
1285
1353
  /**
1286
1354
  * 注册 HTTP HEAD 处理函数
1287
1355
  * @overload
1288
1356
  * @param {string} path 要注册的路径
1289
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1357
+ * @param {Handler} handler 要注册的处理函数
1290
1358
  * @returns {() => void}
1291
1359
  */
1292
1360
  /**
@@ -1310,14 +1378,14 @@ class ApiRouter extends Router {
1310
1378
  /**
1311
1379
  * 注册 HTTP OPTIONS 处理函数
1312
1380
  * @overload
1313
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1381
+ * @param {Handler} handler 要注册的处理函数
1314
1382
  * @returns {() => void}
1315
1383
  */
1316
1384
  /**
1317
1385
  * 注册 HTTP OPTIONS 处理函数
1318
1386
  * @overload
1319
1387
  * @param {string} path 要注册的路径
1320
- * @param {import('../main/types').Handler} handler 要注册的处理函数
1388
+ * @param {Handler} handler 要注册的处理函数
1321
1389
  * @returns {() => void}
1322
1390
  */
1323
1391
  /**
@@ -1363,21 +1431,22 @@ function createFetch(run, notFound) {
1363
1431
  };
1364
1432
  }
1365
1433
 
1434
+ /** @import { Context, Handler, HandlerResult } from './main/types' */
1366
1435
  /**
1367
1436
  * @callback Onionskin
1368
- * @param {import('./main/types').Context} ctx
1369
- * @param {() => Promise<import('./main/types').HandlerResult>} next
1370
- * @returns {PromiseLike<import('./main/types').HandlerResult> | import('./main/types').HandlerResult}
1437
+ * @param {Context} ctx
1438
+ * @param {() => Promise<HandlerResult>} next
1439
+ * @returns {PromiseLike<HandlerResult> | HandlerResult}
1371
1440
  */
1372
1441
 
1373
1442
  const noop = () => {};
1374
1443
  /**
1375
1444
  *
1376
1445
  * @param {...(Onionskin | Onionskin[])} handlers
1377
- * @returns {import('./main/types').Handler}
1446
+ * @returns {Handler}
1378
1447
  */
1379
1448
  function onionskin(...handlers) {
1380
- /** @type {import('./main/types').Handler} */
1449
+ /** @type {Handler} */
1381
1450
  let handler = noop;
1382
1451
  for (const os of handlers.flat()) {
1383
1452
  const currentHandler = handler;
@@ -1386,7 +1455,44 @@ function onionskin(...handlers) {
1386
1455
  return handler;
1387
1456
  }
1388
1457
 
1458
+ /** @import { Context } from './main/types.js' */
1459
+ class Param {
1460
+ #symbol = Symbol();
1461
+ get name() { return this.#symbol}
1462
+ #pattern
1463
+ get pattern() { return this.#pattern; }
1464
+ /**
1465
+ *
1466
+ * @param {RegExp} pattern
1467
+ */
1468
+ constructor(pattern) {
1469
+ this.#pattern = pattern;
1470
+ }
1471
+ /**
1472
+ *
1473
+ * @param {Context} ctx
1474
+ * @returns {string?}
1475
+ */
1476
+ param(ctx) {
1477
+ const param = ctx.params[this.#symbol];
1478
+ if (Array.isArray(param)) { return param[0] ?? null; }
1479
+ return param ?? null;
1480
+ }
1481
+ /**
1482
+ *
1483
+ * @param {Context} ctx
1484
+ * @returns {string[]?}
1485
+ */
1486
+ params(ctx) {
1487
+ const param = ctx.params[this.#symbol];
1488
+ if (typeof param === 'string') { return [param]; }
1489
+ if (Array.isArray(param)) { return param; }
1490
+ return null;
1491
+ }
1492
+ }
1493
+
1389
1494
  exports.ApiRouter = ApiRouter;
1495
+ exports.Param = Param;
1390
1496
  exports.Router = Router;
1391
1497
  exports.createFetch = createFetch;
1392
1498
  exports.main = main;