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