k99 0.6.0-alpha.8 → 0.6.0-beta.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,1907 +1,1334 @@
1
1
  /*!
2
- * k99 v0.6.0-alpha.8
3
- * (c) 2019-2023 猛火Fierflame
2
+ * k99 v0.6.0-beta.0
3
+ * (c) 2019-2024 猛火Fierflame
4
4
  * @license MIT
5
5
  */
6
6
  'use strict';
7
7
 
8
+ /**
9
+ *
10
+ * @param {string} str
11
+ * @returns {Uint8Array}
12
+ */
8
13
  function str2utf8bin(str) {
9
- let out = [];
10
- let i = 0;
11
- for (; i < str.length; i++) {
12
- const c = str.codePointAt(i);
13
- if (typeof c !== 'number') {
14
- break;
15
- }
16
- if (c >= 0x10000) {
17
- i++;
18
- }
19
- if (c < 0x80) {
20
- out.push(c);
21
- } else if (c < 0x800) {
22
- // 11 = 5 + 6
23
- out.push(0xC0 | 0x1F & c >> 6);
24
- out.push(0x80 | 0x3F & c);
25
- } else if (c < 0x10000) {
26
- // 16 = 4 + 6 * 2
27
- out.push(0xE0 | 0x0F & c >> 12);
28
- out.push(0x80 | 0x3F & c >> 6);
29
- out.push(0x80 | 0x3F & c);
30
- } else {
31
- // 21 = 3 + 6 * 3
32
- out.push(0xF0 | 0x07 & c >> 18);
33
- out.push(0x80 | 0x3F & c >> 12);
34
- out.push(0x80 | 0x3F & c >> 6);
35
- out.push(0x80 | 0x3F & c);
36
- }
37
- }
38
- return new Uint8Array(out);
39
- }
40
- let base64chars$1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
41
- function base2bin(str, chars = base64chars$1) {
42
- const n = Math.floor(Math.log2(chars.length));
43
- str = str.replace(/[=\s]+/g, '');
44
- let list = new Uint8Array(str.length * n >> 3);
45
- let next = 0;
46
- let v = 0,
47
- l = 0;
48
- for (let c of str) {
49
- v = v << n | chars.indexOf(c);
50
- l += n;
51
- while (l >= 8) {
52
- l -= 8;
53
- list[next++] = v >> l & 0xff;
54
- v &= (1 << l) - 1;
55
- }
56
- }
57
- return list;
58
- }
59
- let hexChars$1 = '0123456789ABCDEF';
60
- function hex2bin(str) {
61
- let list = [];
62
- for (let i = 0; i < str.length; i += 2) {
63
- list.push(hexChars$1.indexOf(str[i]) * 16 + hexChars$1.indexOf(str[i + 1] || '0'));
64
- }
65
- return new Uint8Array(list);
66
- }
67
- function str2bin(value, encoding) {
68
- if (ArrayBuffer.isView(value)) {
69
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
70
- }
71
- if (value instanceof ArrayBuffer) {
72
- return new Uint8Array(value);
73
- }
74
- if (typeof value !== 'string') {
75
- return null;
76
- }
77
- switch (encoding) {
78
- default:
79
- case 'utf8':
80
- return str2utf8bin(value);
81
- case 'base64':
82
- return base2bin(value);
83
- case 'hex':
84
- return hex2bin(value);
85
- }
14
+ return new TextEncoder().encode(str);
86
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 {
87
26
 
88
- function mergeArrayBuffer(data, length) {
89
- if (data.length === 1) {
90
- return new Uint8Array(data[0]);
91
- }
92
- const array = new Uint8Array(length);
93
- let offset = 0;
94
- for (const b of data) {
95
- array.set(b, offset);
96
- offset += b.byteLength;
97
- }
98
- return array;
99
- }
100
- function getBuffer(data, size) {
101
- if (!size) {
102
- const ret = mergeArrayBuffer(data, data.reduce((a, b) => a + b.byteLength, 0));
103
- data.length = 0;
104
- return ret;
105
- }
106
- let length = 0;
107
- const list = [];
108
- // eslint-disable-next-line no-cond-assign
109
- for (let it; it = data.shift();) {
110
- if (length + it.byteLength > size) {
111
- const buffer = it.slice(0, size - length);
112
- list.push(buffer);
113
- length += buffer.byteLength;
114
- data.unshift(it.slice(buffer.byteLength));
115
- break;
116
- }
117
- list.push(it);
118
- length += it.byteLength;
119
- if (length === size) {
120
- break;
121
- }
122
- }
123
- return mergeArrayBuffer(list, length);
124
- }
125
- async function* toAsyncIterable(chunk) {
126
- if (!chunk) {
127
- return;
128
- }
129
- if (typeof chunk === 'string') {
130
- return yield str2bin(chunk);
131
- }
132
- if (typeof chunk !== 'object') {
133
- return;
134
- }
135
- if (chunk instanceof ArrayBuffer) {
136
- return yield str2bin(chunk);
137
- }
138
- if (ArrayBuffer.isView(chunk)) {
139
- return yield str2bin(chunk);
140
- }
141
- if (!(Symbol.asyncIterator in chunk || Symbol.iterator in chunk)) {
142
- return;
143
- }
144
- for await (const data of chunk) {
145
- yield* toAsyncIterable(data);
146
- }
147
- }
148
- async function main$1(req, getNext) {
149
- const data = [];
150
- let length = 0;
151
- let [size, cb] = await getNext();
152
- for await (const buffer of toAsyncIterable(req)) {
153
- data.push(buffer);
154
- length += buffer.byteLength;
155
- if (size <= 0) {
156
- length = 0;
157
- cb(getBuffer(data));
158
- [size, cb] = await getNext();
159
- continue;
160
- }
161
- if (length >= size) {
162
- length -= size;
163
- cb(getBuffer(data, size));
164
- [size, cb] = await getNext();
165
- }
166
- while (length && (size <= 0 || size <= length)) {
167
- if (size <= 0) {
168
- length = 0;
169
- cb(getBuffer(data));
170
- [size, cb] = await getNext();
171
- break;
172
- }
173
- length -= size;
174
- cb(getBuffer(data, size));
175
- [size, cb] = await getNext();
176
- }
177
- }
178
- if (length) {
179
- cb(getBuffer(data));
180
- [size, cb] = await getNext();
181
- }
182
- cb(null);
183
- for (;;) {
184
- [size, cb] = await getNext();
185
- cb(null);
186
- }
187
- }
188
- function createRead$1(req) {
189
- if (!req) {
190
- return () => Promise.resolve(null);
191
- }
192
- const list = [];
193
- let next = null;
194
- main$1(req, function getNext() {
195
- return new Promise(r => {
196
- const t = list.shift();
197
- if (t) {
198
- return r(t);
199
- }
200
- next = v => {
201
- next = null;
202
- r(v);
203
- };
204
- });
205
- });
206
- function read(size = 0) {
207
- return new Promise(resolve => {
208
- size = Math.max(size, 0);
209
- if (next) {
210
- next([size, resolve]);
211
- return;
212
- }
213
- list.push([size, resolve]);
214
- });
215
- }
216
- return read;
217
- }
218
- const urlRegex = /^(?:(?:https?:)?\/\/(?:(?:[^/?#]*@)?)(?:\d+|(?:\[[0-9a-f:]+\])|(?:\d{1,3}\.){3}\d{1,3}|(?:(?:[a-z0-9\u0100-\uffff]+-*)+\.)+[a-z]+)(?::\d+)?)?((?:\/?[^?#]*)?)((?:\?[^#]*)?)(?:#.*)?$/i;
219
- function getNameValue(s) {
220
- const index = s.indexOf('=');
221
- if (index < 0) {
222
- return [decodeURIComponent(s), ''];
223
- }
224
- return [decodeURIComponent(s.substring(0, index)), decodeURIComponent(s.substring(index + 1))];
225
- }
226
- function parseQuery(s) {
227
- const query = {};
228
- for (const k of s.split('&').filter(Boolean)) {
229
- const [index, value] = getNameValue(k);
230
- if (index in query) {
231
- query[index] = [query[index], value].flat();
232
- } else {
233
- query[index] = [value];
234
- }
235
- }
236
- return query;
237
- }
238
- function parseUrl(uri) {
239
- const result = urlRegex.exec(uri);
240
- if (!result) {
241
- return ['/', ''];
242
- }
243
- let [, pathname, search] = result;
244
- const path = [];
245
- for (const p of pathname.replace(/^[\\/]+/, '').replace(/[\\/]+/g, '/').split('/')) {
246
- if (p === '.') {
247
- continue;
248
- }
249
- if (p === '..') {
250
- path.pop();
251
- continue;
252
- }
253
- path.push(p);
254
- }
255
- return [`/${path.join('/')}`, search];
256
- }
257
- function createRequest({
258
- method,
259
- path,
260
- signal,
261
- body,
262
- headers = {}
263
- }) {
264
- const [pathname, search] = parseUrl(path);
265
- return {
266
- method,
267
- url: `${pathname}${search}`,
268
- headers,
269
- pathname,
270
- search,
271
- signal: signal || new AbortController().signal,
272
- query: parseQuery(search.substring(1)),
273
- read: typeof body === 'function' ? body : createRead$1(body)
274
- };
27
+ }
28
+ return false;
275
29
  }
30
+ /**
31
+ *
32
+ * @template T
33
+ * @param {any} result
34
+ * @returns {result is Iterable<T> | AsyncIterable<T>}
35
+ */
36
+ function isIterable(result) {
37
+ return Symbol.asyncIterator in result || Symbol.iterator in result;
276
38
 
277
- function isBaseWriteType(chunk) {
278
- if (chunk instanceof ArrayBuffer) {
279
- return true;
280
- }
281
- if (ArrayBuffer.isView(chunk)) {
282
- return true;
283
- }
284
- return false;
285
39
  }
286
- function createWrite() {
287
- let finished = false;
288
- let list = [];
289
- let next = null;
290
- function done(callback) {
291
- if (finished) {
292
- return null;
293
- }
294
- finished = true;
295
- if (callback) {
296
- callback(false);
297
- }
298
- // eslint-disable-next-line no-cond-assign
299
- for (let value; value = list.shift();) {
300
- const [, cb] = value;
301
- cb(false);
302
- }
303
- return null;
304
- }
305
- let nextPromise = Promise.resolve(() => {});
306
- const readable = {
307
- next() {
308
- const promise = nextPromise.then(cb => {
309
- if (!cb) {
310
- return;
311
- }
312
- if (abortException) {
313
- cb(false);
314
- return abortedPromise;
315
- }
316
- cb(true);
317
- return Promise.race([abortedPromise, new Promise(r => {
318
- const value = list.shift();
319
- if (value) {
320
- return r(value);
321
- }
322
- next = value => {
323
- next = null;
324
- r(value);
325
- };
326
- }).then(value => {
327
- const [data, cb] = value;
328
- if (!abortException && data !== undefined) {
329
- return value;
330
- }
331
- cb(!abortException);
332
- })]);
333
- });
334
- nextPromise = promise.then(value => {
335
- if (!value) {
336
- return null;
337
- }
338
- const [, cb] = value;
339
- return cb;
340
- }, () => done());
341
- return promise.then(value => {
342
- if (!value) {
343
- return {
344
- done: true,
345
- value: undefined
346
- };
347
- }
348
- const [data, cb] = value;
349
- return {
350
- done: false,
351
- value: data
352
- };
353
- });
354
- },
355
- throw(e) {
356
- nextPromise = nextPromise.then(done);
357
- return nextPromise.then(() => Promise.reject(e));
358
- },
359
- return(r) {
360
- nextPromise = nextPromise.then(done);
361
- return nextPromise.then(() => r);
362
- },
363
- [Symbol.asyncIterator]() {
364
- return readable;
365
- }
366
- };
367
- let abortException;
368
- let abortReject;
369
- let abortedPromise = new Promise((_, reject) => {
370
- if (abortException) {
371
- reject(abortException);
372
- } else {
373
- abortReject = reject;
374
- }
375
- });
376
- function abort(e) {
377
- if (abortException) {
378
- return false;
379
- }
380
- abortException = e || new DOMException('The user aborted a request.');
381
- if (abortReject) {
382
- abortReject(abortException);
383
- }
384
- return true;
385
- }
386
- function run(chunk, cb) {
387
- if (finished) {
388
- return cb(false);
389
- }
390
- if (!next) {
391
- list.push([chunk, cb]);
392
- return;
393
- }
394
- next([chunk, cb]);
395
- }
396
- let writePromise = Promise.resolve(true);
397
- /**
398
- * 向可写流中写入数据
399
- * @param chunk 要写入的数据
400
- */
401
- async function write(chunk) {
402
- if (finished) {
403
- return Promise.resolve();
404
- }
405
- if (typeof chunk === 'string') {
406
- return new Promise(cb => run(str2bin(chunk), cb));
407
- }
408
- if (typeof chunk !== 'object') {
409
- return false;
410
- }
411
- if (isBaseWriteType(chunk)) {
412
- return new Promise(cb => run(str2bin(chunk), cb));
413
- }
414
- if (!(Symbol.asyncIterator in chunk || Symbol.iterator in chunk)) {
415
- return false;
416
- }
417
- for await (const data of chunk) {
418
- if (finished) {
419
- return Promise.resolve();
420
- }
421
- if (data && !(await write(data))) {
422
- return false;
423
- }
424
- }
425
- return true;
426
- }
427
- let ended = false;
428
- const writable = {
429
- write(chunk) {
430
- const promise = writePromise.then(v => {
431
- if (v === undefined) {
432
- return;
433
- }
434
- if (!chunk) {
435
- return true;
436
- }
437
- return write(chunk);
438
- });
439
- writePromise = promise;
440
- return promise.then(v => Boolean(v));
441
- },
442
- end() {
443
- ended = true;
444
- const promise = writePromise.then(v => new Promise(cb => {
445
- if (v === undefined || finished) {
446
- return cb();
447
- }
448
- if (!next) {
449
- list.push([undefined, () => cb()]);
450
- return;
451
- }
452
- next([undefined, () => cb()]);
453
- }));
454
- writePromise = promise;
455
- return promise;
456
- },
457
- get ended() {
458
- return finished || ended;
459
- }
460
- };
461
- return [writable, readable, abort];
40
+ /**
41
+ * 向可写流中写入数据
42
+ * @param {WritableStreamDefaultWriter<Uint8Array>} writer
43
+ * @param {unknown} chunk 要写入的数据
44
+ * @returns {Promise<void>}
45
+ */
46
+ async function write(writer, chunk) {
47
+ if (typeof chunk === 'string') {
48
+ return writer.write(str2utf8bin(chunk));
49
+ }
50
+ if (typeof chunk !== 'object') { return; }
51
+ if (ArrayBuffer.isView(chunk)) {
52
+ return writer.write(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
53
+ }
54
+ if (isBufferSource(chunk)) {
55
+ return writer.write(new Uint8Array(chunk));
56
+ }
57
+ if (!isIterable(chunk)) {
58
+ return;
59
+ }
60
+ for await (const data of chunk) {
61
+ if (!data) { continue; }
62
+ await write(writer, data);
63
+ }
462
64
  }
463
65
 
464
- function* getCookie(sentCookies, name) {
465
- const list = sentCookies;
466
- for (const item of list) {
467
- if (name && item.name !== name) {
468
- continue;
469
- }
470
- yield {
471
- ...item
472
- };
473
- }
474
- }
475
- function getCookieHeader(list) {
476
- return list.map(({
477
- name,
478
- value,
479
- expire,
480
- domain,
481
- path,
482
- secure,
483
- httpOnly
484
- }) => name && [`${encodeURI(name)}=${encodeURI(value || '')}`, expire && `Expires=${expire}`, domain && `Domain=${encodeURI(domain)}`, path && `Path=${encodeURI(path)}`, secure && 'Secure', httpOnly && 'HttpOnly'].filter(Boolean).join('; ')).filter(Boolean);
485
- }
486
- function getRequestCookies(cookie) {
487
- let cookies = {};
488
- for (const item of cookie.replace(/\s/g, '').split(';')) {
489
- const v = item.split('=');
490
- const name = decodeURIComponent(v.shift());
491
- cookies[name] = decodeURIComponent(v.join('='));
492
- }
493
- return cookies;
494
- }
495
- function clearCookie(sentCookies, cookies, name, opt) {
496
- let expire = 'Fri, 31 Dec 1999 16:00:00 GMT';
497
- if (typeof name === 'string') {
498
- if (!name) {
499
- return;
500
- }
501
- const {
502
- domain,
503
- path,
504
- secure,
505
- httpOnly
506
- } = opt !== true && opt || {};
507
- sentCookies.push({
508
- name,
509
- value: 'delete',
510
- expire,
511
- domain,
512
- path,
513
- secure,
514
- httpOnly
515
- });
516
- } else {
517
- const {
518
- domain,
519
- path,
520
- secure,
521
- httpOnly
522
- } = name || {};
523
- sentCookies.length = 0;
524
- if (opt) {
525
- for (let name in cookies) {
526
- sentCookies.push({
527
- name,
528
- value: 'delete',
529
- expire,
530
- domain,
531
- path,
532
- secure,
533
- httpOnly
534
- });
535
- }
536
- }
537
- }
66
+ /**
67
+ *
68
+ * @param {any} k
69
+ * @param {any} v
70
+ * @returns {any}
71
+ */
72
+ function replacer(k, v) {
73
+ if (typeof v === 'bigint') {
74
+ return String(v);
75
+ }
76
+ return v;
538
77
  }
539
78
 
540
- function utf8bin2str(code) {
541
- let ret = [];
542
- let index = 0;
543
- while (index < code.length) {
544
- let c = code[index++] | 0;
545
- if (c >= 0b11110000) {
546
- c = (c & 0x7) << 18;
547
- c |= (code[index++] & 0x3F) << 12;
548
- c |= (code[index++] & 0x3F) << 6;
549
- c |= code[index++] & 0x3F;
550
- } else if (c >= 0b11100000) {
551
- c = (c & 0xF) << 12;
552
- c |= (code[index++] & 0x3F) << 6;
553
- c |= code[index++] & 0x3F;
554
- } else if (c >= 0b11000000) {
555
- c = (c & 0x1F) << 6;
556
- c |= code[index++] & 0x3F;
557
- }
558
- ret.push(c);
559
- }
560
- return ret.map(x => String.fromCharCode(x)).join('');
561
- }
562
- let base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
563
- function bin2base(buff, chars = base64chars) {
564
- const n = Math.floor(Math.log2(chars.length));
565
- const mask = (1 << n) - 1;
566
- let list = new Array(Math.floor(((buff.byteLength << 3) + n - 1) / n));
567
- let next = 0;
568
- let v = 0,
569
- l = 0;
570
- for (let b of buff) {
571
- v = v << 8 | b;
572
- l += 8;
573
- while (l >= n) {
574
- l -= n;
575
- list[next++] = chars[v >> l & mask];
576
- v &= (1 << l) - 1;
577
- }
578
- }
579
- if (l) {
580
- list[next++] = chars[v << n - l & mask];
581
- }
582
- while (list.length % 4) {
583
- list.push('=');
584
- }
585
- return list.join('');
586
- }
587
- let hexChars = '0123456789ABCDEF';
588
- function bin2hex(buff) {
589
- let list = new Array(buff.byteLength * 2);
590
- for (let b of buff) {
591
- list.push(b >> 4 & 0x1111, b & 0x1111);
592
- }
593
- return list.map(v => hexChars[v]).join('');
594
- }
595
- function bin2str(value, encoding) {
596
- if (!value) {
597
- return null;
598
- }
599
- switch (encoding) {
600
- case 'utf8':
601
- return utf8bin2str(value);
602
- case 'base64':
603
- return bin2base(value);
604
- case 'hex':
605
- return bin2hex(value);
606
- }
607
- return value;
79
+ /**
80
+ *
81
+ * @param {any} result
82
+ * @param {Promise<never>} [aborted]
83
+ * @returns {[BodyInit, number, string] | null}
84
+ */
85
+ function toBodyData(result, aborted) {
86
+ if (result instanceof ReadableStream) {
87
+ return [result, 0, ''];
88
+ }
89
+ if (result instanceof URLSearchParams) {
90
+ return [result, 0, ''];
91
+ }
92
+ if (result instanceof Blob) {
93
+ return [result, result.size, result.type];
94
+ }
95
+ if (result instanceof FormData) {
96
+ return [result, 0, ''];
97
+ }
98
+ if (ArrayBuffer.isView(result) || isBufferSource(result)) {
99
+ return [result, result.byteLength, ''];
100
+ }
101
+ if (typeof result === 'string') {
102
+ const body = str2utf8bin(result);
103
+ return [body, body.byteLength, ''];
104
+ }
105
+ if (typeof result !== 'object') {
106
+ return null;
107
+ }
108
+ if (Array.isArray(result) || !isIterable(result)) {
109
+ const body = str2utf8bin(JSON.stringify(result, replacer));
110
+ return [body, body.byteLength, 'application/json'];
111
+ }
112
+ /** @type {TransformStream<Uint8Array, Uint8Array>} */
113
+ const { writable, readable } = new TransformStream();
114
+ const writer = writable.getWriter();
115
+ aborted?.catch((e) => {
116
+ writable.abort(e || new DOMException('The user aborted a request.')).catch(() => {});
117
+ });
118
+ (async () => {
119
+ for await (const data of result) {
120
+ if (!data) { continue; }
121
+ await write(writer, data);
122
+ }
123
+ await writable.close();
124
+ })().catch(() => {});
125
+ return [readable, 0, ''];
608
126
  }
609
-
610
- function createRead(get) {
611
- let readPromise = Promise.resolve(false);
612
- function read(size = 0, encoding) {
613
- const promise = readPromise.then(v => {
614
- if (v) {
615
- return null;
616
- }
617
- return get(size);
618
- });
619
- readPromise = promise.then(v => !v);
620
- return promise.then(value => bin2str(value, encoding));
621
- }
622
- return read;
127
+ /**
128
+ *
129
+ * @param {any} result
130
+ * @param {Headers} headers
131
+ * @param {Promise<never>} [aborted]
132
+ * @returns
133
+ */
134
+ function toBody(result, headers, aborted) {
135
+ const bodyData = toBodyData(result, aborted);
136
+ if (!bodyData) { return null; }
137
+ const [body, size, type] = bodyData;
138
+ if (type && !headers.get('Content-Type')) {
139
+ headers.set('Content-Type', type);
140
+ }
141
+ if (size > 0 && !headers.get('Content-Length')) {
142
+ headers.set('Content-Length', String(size));
143
+ }
144
+ return body;
623
145
  }
624
146
 
625
- function destroyServices(services, environment) {
626
- let promise = Promise.resolve();
627
- for (const [service, context] of [...services.entries()]) {
628
- promise = promise.then(() => service(Object.create(context, {
629
- destroying: {
630
- value: true,
631
- configurable: true,
632
- enumerable: true
633
- }
634
- }))).catch(e => environment?.error?.(e));
635
- }
636
- return promise;
637
- }
638
- const hostRegex = /^(\[[^\]]+\]|^:):(\d+)$/;
639
- function createContext({
640
- method,
641
- url,
642
- pathname,
643
- search,
644
- query,
645
- read,
646
- headers,
647
- signal
648
- }, request, environment, parent) {
649
- const services = new Map();
650
- const host = headers.host || '';
651
- const hostInfo = hostRegex.exec(host);
652
- const [hostname, port] = hostInfo ? [hostInfo[1], hostInfo[2]] : [host, ''];
653
- const cookies = getRequestCookies(headers['cookie'] || '');
654
- const sentCookies = [];
655
- let status = 200;
656
- let resHeaders = {};
657
- let headersSent = false;
658
- let destroyed = false;
659
- const root = parent?.root;
660
- let hasError = null;
661
- let params = {};
662
- const context = {
663
- environment,
664
- parent,
665
- get error() {
666
- return hasError;
667
- },
668
- get root() {
669
- return root || this;
670
- },
671
- signal,
672
- request,
673
- service(service, ...p) {
674
- if (service.rootOnly && root) {
675
- return root.service(service, ...p);
676
- }
677
- let serviceContext = services.get(service);
678
- if (!serviceContext) {
679
- let state;
680
- serviceContext = Object.create(context, {
681
- destroying: {
682
- value: false,
683
- configurable: true,
684
- enumerable: true
685
- },
686
- currentService: {
687
- value: service,
688
- configurable: true,
689
- enumerable: true
690
- },
691
- state: {
692
- configurable: true,
693
- enumerable: true,
694
- get() {
695
- return state;
696
- },
697
- set(s) {
698
- state = s;
699
- }
700
- }
701
- });
702
- services.set(service, serviceContext);
703
- }
704
- return service(serviceContext, ...p);
705
- },
706
- method,
707
- url,
708
- pathname,
709
- search,
710
- query,
711
- get params() {
712
- return params;
713
- },
714
- headers: Object.freeze({
715
- ...headers
716
- }),
717
- host,
718
- hostname,
719
- port,
720
- requestType: headers['content-type'] || '',
721
- referer: headers.referer || '',
722
- userAgent: headers['user-agent'] || '',
723
- accept: (headers.accept || '').split(/,\s*/).filter(Boolean),
724
- acceptLanguage: (headers['accept-language'] || '').split(/,\s*/).filter(Boolean),
725
- cookies,
726
- read: createRead(read),
727
- get destroyed() {
728
- return destroyed;
729
- },
730
- get headersSent() {
731
- return headersSent;
732
- },
733
- get status() {
734
- return status;
735
- },
736
- set status(v) {
737
- if (headersSent) {
738
- return;
739
- }
740
- status = v;
741
- },
742
- get location() {
743
- return resHeaders['location'] || '';
744
- },
745
- set location(url) {
746
- if (!headersSent) {
747
- resHeaders['location'] = url;
748
- }
749
- },
750
- get responseType() {
751
- return resHeaders['content-type'] || '';
752
- },
753
- set responseType(v) {
754
- if (!headersSent) {
755
- resHeaders['content-type'] = v;
756
- }
757
- },
758
- getCookie(name) {
759
- return getCookie(sentCookies, name);
760
- },
761
- setCookie(name, value, {
762
- expire,
763
- domain,
764
- path,
765
- secure,
766
- httpOnly
767
- } = {}) {
768
- if (headersSent) {
769
- return;
770
- }
771
- sentCookies.push({
772
- name,
773
- value,
774
- expire,
775
- domain,
776
- path,
777
- secure,
778
- httpOnly
779
- });
780
- resHeaders['set-cookie'] = getCookieHeader(sentCookies);
781
- },
782
- clearCookie(name, opt) {
783
- if (headersSent) {
784
- return;
785
- }
786
- clearCookie(sentCookies, cookies, name, opt);
787
- resHeaders['set-cookie'] = getCookieHeader(sentCookies);
788
- },
789
- hasHeader(n) {
790
- return n in resHeaders;
791
- },
792
- getHeaderNames() {
793
- return Object.keys(resHeaders);
794
- },
795
- getHeaders() {
796
- return {
797
- ...resHeaders
798
- };
799
- },
800
- getHeader(n) {
801
- return resHeaders[n];
802
- },
803
- setHeader(n, v) {
804
- if (headersSent) {
805
- return;
806
- }
807
- if (v === undefined) {
808
- delete resHeaders[n];
809
- } else {
810
- resHeaders[n] = v;
811
- }
812
- }
813
- };
814
- return {
815
- context,
816
- setParams: v => {
817
- params = v;
818
- },
819
- destroy: error => {
820
- if (destroyed) {
821
- return;
822
- }
823
- destroyed = true;
824
- headersSent = true;
825
- if (error) {
826
- hasError = error;
827
- }
828
- destroyServices(services, environment);
829
- },
830
- sendHeaders() {
831
- if (headersSent) {
832
- return false;
833
- }
834
- headersSent = true;
835
- return true;
836
- }
837
- };
147
+ /**
148
+ *
149
+ * @param {import('./types').Cookie[]} sentCookies
150
+ * @param {string} [name]
151
+ * @returns {Iterable<import('./types').Cookie>}
152
+ */
153
+ function *getCookie(sentCookies, name) {
154
+ const list = sentCookies;
155
+ for (const item of list) {
156
+ if (name && item.name !== name) { continue; }
157
+ yield { ...item };
158
+ }
838
159
  }
839
160
 
840
- function isJSON(result) {
841
- if (!result) {
842
- return false;
843
- }
844
- if (typeof result !== 'object') {
845
- return false;
846
- }
847
- if (Array.isArray(result)) {
848
- return true;
849
- }
850
- if (isBaseWriteType(result)) {
851
- return false;
852
- }
853
- if (Symbol.asyncIterator in result || Symbol.iterator in result) {
854
- return false;
855
- }
856
- return true;
857
- }
858
- function replacer(k, v) {
859
- if (typeof v === 'bigint') {
860
- return String(v);
861
- }
862
- return v;
161
+ /**
162
+ *
163
+ * @param {Headers} headers
164
+ * @param {import('./types').Cookie[]} cookies
165
+ * @returns {void}
166
+ */
167
+ function setCookiesHeader(headers, cookies) {
168
+ headers.delete('set-cookie');
169
+ for (const { name, value, expire, domain, path, secure, httpOnly } of cookies) {
170
+ if (!name) { continue; }
171
+ headers.append('set-cookie', [
172
+ `${ encodeURI(name) }=${ encodeURI(value || '') }`,
173
+ expire && `Expires=${ expire }`,
174
+ domain && `Domain=${ encodeURI(domain) }`,
175
+ path && `Path=${ encodeURI(path) }`,
176
+ secure && 'Secure',
177
+ httpOnly && 'HttpOnly',
178
+ ].filter(Boolean).join('; '));
179
+ }
863
180
  }
864
- async function runHandle(context, handle) {
865
- if (context.headersSent) {
866
- return;
867
- }
868
- const result = await handle(context);
869
- if (context.finished) {
870
- return;
871
- }
872
- if (isJSON(result)) {
873
- if (context.headersSent) {
874
- return;
875
- }
876
- context.responseType = 'application/json';
877
- await context.write(JSON.stringify(result, replacer));
878
- return;
879
- }
880
- return context.write(result);
181
+ /**
182
+ *
183
+ * @param {string} cookie
184
+ * @returns {Record<string, string>}
185
+ */
186
+ function getRequestCookies(cookie) {
187
+ /** @type {{ [key: string]: string; }} */
188
+ let cookies = {};
189
+ for (const item of cookie.replace(/\s/g, '').split(';')) {
190
+ const v = item.split('=');
191
+ const name = decodeURIComponent(/** @type {string} */(v.shift()));
192
+ cookies[name] = decodeURIComponent(v.join('='));
193
+ }
194
+ return cookies;
881
195
  }
196
+ /**
197
+ *
198
+ * @param {import('./types').Cookie[]} sentCookies
199
+ * @param {Record<string, string>} cookies
200
+ * @param {string | import('./types').CookieOption} [name]
201
+ * @param {import('./types').CookieOption | boolean} [opt]
202
+ * @returns {void}
203
+ */
204
+ function clearCookie(
205
+ sentCookies,
206
+ cookies,
207
+ name,
208
+ opt,
209
+ ) {
210
+ let expire = 'Fri, 31 Dec 1999 16:00:00 GMT';
211
+ if (typeof name === 'string') {
212
+ if (!name) { return; }
213
+ /** @type {import('./types').CookieOption} */
214
+ const { domain, path, secure, httpOnly } = opt !== true && opt || {};
215
+ sentCookies.push({ name, value: 'delete', expire, domain, path, secure, httpOnly });
216
+ } else {
217
+ /** @type {import('./types').CookieOption} */
218
+ const { domain, path, secure, httpOnly } = name || {};
219
+ sentCookies.length = 0;
220
+ if (opt) {
221
+ for (let name in cookies) {
222
+ sentCookies.push({ name, value: 'delete', expire, domain, path, secure, httpOnly });
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ const noBodyMethods = new Set(['GET', 'OPTIONS']);
229
+ /**
230
+ *
231
+ * @param {AbortSignal} signal
232
+ * @returns {Promise<never>}
233
+ */
882
234
  function signal2promise(signal) {
883
- return new Promise((_, reject) => {
884
- if (signal.aborted) {
885
- return reject(signal.reason);
886
- }
887
- signal.addEventListener('abort', () => reject(signal.reason), {
888
- once: true
889
- });
890
- });
891
- }
892
- function main(req, getHandler, environment, runner, parent) {
893
- const aborted = signal2promise(req.signal);
894
- const {
895
- context,
896
- setParams,
897
- destroy,
898
- sendHeaders
899
- } = createContext(req, opt => main(createRequest(opt), getHandler, environment, runner, context), environment, parent);
900
- function run() {
901
- return Promise.race([aborted, Promise.resolve().then(() => getHandler(context, setParams))]).then(handler => new Promise(resolve => {
902
- if (!handler) {
903
- destroy();
904
- return resolve(null);
905
- }
906
- const [writable, readable, abortResponse] = createWrite();
907
- aborted.catch(e => abortResponse(e));
908
- function send() {
909
- if (!sendHeaders()) {
910
- return;
911
- }
912
- const {
913
- status
914
- } = context;
915
- resolve({
916
- ...readable,
917
- get status() {
918
- return status;
919
- },
920
- get finished() {
921
- return writable.ended;
922
- },
923
- headers: Object.freeze(context.getHeaders()),
924
- [Symbol.asyncIterator]() {
925
- return readable;
926
- }
927
- });
928
- }
929
- const contextS = {
930
- get finished() {
931
- return writable.ended;
932
- },
933
- write(chunk) {
934
- send();
935
- return writable.write(chunk);
936
- }
937
- };
938
- const actionContext = Object.create(context, Object.getOwnPropertyDescriptors(contextS));
939
- Promise.race([aborted, runHandle(actionContext, handler)]).then(() => {
940
- send();
941
- destroy();
942
- writable.end();
943
- }, e => {
944
- context.status = 500;
945
- send();
946
- abortResponse(e);
947
- destroy(e || true);
948
- writable.end();
949
- });
950
- }), e => {
951
- destroy(e || true);
952
- return Promise.reject(e);
953
- });
954
- }
955
- return runner ? runner(context, run) : run();
956
- }
957
-
958
- async function defaultRead$2() {
959
- return undefined;
960
- }
961
- async function defaultWrite$2() {
962
- return false;
963
- }
964
- function initSettings({
965
- read = defaultRead$2,
966
- write = defaultWrite$2
967
- } = {}) {
968
- return {
969
- read,
970
- write
971
- };
972
- }
973
-
974
- async function defaultRead$1() {
975
- return null;
976
- }
977
- async function defaultWrite$1() {
978
- return false;
979
- }
980
- function initAssets({
981
- read = defaultRead$1,
982
- write = defaultWrite$1,
983
- delete: unlink = defaultWrite$1,
984
- stat = defaultRead$1
985
- } = {}) {
986
- async function readAsset(path, encoding) {
987
- const ret = await read(path);
988
- return ret && bin2str(ret, encoding);
989
- }
990
- async function writeAsset(path, data, encoding) {
991
- if (data === undefined) {
992
- return unlink(path);
993
- }
994
- const value = str2bin(data, encoding);
995
- if (!value) {
996
- return unlink(path);
997
- }
998
- return write(path, value);
999
- }
1000
- function deleteAsset(path) {
1001
- return unlink(path);
1002
- }
1003
- async function statAsset(path) {
1004
- return stat(path);
1005
- }
1006
- return {
1007
- read: readAsset,
1008
- write: writeAsset,
1009
- delete: deleteAsset,
1010
- stat: statAsset
1011
- };
235
+ return new Promise((_, reject) => {
236
+ if (signal.aborted) {
237
+ return reject(signal.reason);
238
+ }
239
+ signal.addEventListener(
240
+ 'abort',
241
+ () => reject(signal.reason),
242
+ { once: true }
243
+ );
244
+ });
1012
245
  }
1013
246
 
1014
247
  /**
1015
- * 包装日志文本
1016
- * @param log 原始的日志
1017
- * @param opt 包装选项
248
+ *
249
+ * @param {Headers} headers
250
+ * @param {string} name
251
+ * @param {string} [value]
1018
252
  */
1019
- function pack(log, {
1020
- tags,
1021
- indent,
1022
- date
1023
- } = {}) {
1024
- let extendInfo = '';
1025
- if (tags) {
1026
- if (!Array.isArray(tags)) {
1027
- tags = [tags];
1028
- }
1029
- extendInfo = tags.map(tag => `[${tag}]`).join(' ');
1030
- }
1031
- if (date) {
1032
- extendInfo = `${new Date().toISOString()} ${extendInfo}`;
1033
- }
1034
- if (!log.includes('\n')) {
1035
- return `${extendInfo} ${log}`;
1036
- }
1037
- if (typeof indent === 'number') {
1038
- indent = Math.floor(indent);
1039
- if (isFinite(indent) && indent > 0) {
1040
- indent = Array(indent).fill(' ').join('');
1041
- }
1042
- }
1043
- if (!indent || typeof indent !== 'string') {
1044
- indent = ' ';
1045
- }
1046
- log = `${extendInfo}\n${log}`;
1047
- log = log.split('\n').join(`\n${indent}`);
1048
- return log;
1049
- }
1050
- function getErrorLog(log) {
1051
- if (typeof log === 'string') {
1052
- return log;
1053
- }
1054
- if (log instanceof Error) {
1055
- return log.stack || `${log.name}:${log.message}`;
1056
- }
1057
- return String(log);
1058
- }
1059
- const regex$2 = /(?:^|\/)(debug|error|warn|info)\/?$|^\/?(debug|error|warn|info)(?:\/|$)/;
1060
- async function defaultWrite(path, log) {
1061
- switch (path) {
1062
- case 'debug':
1063
- console.debug(log);
1064
- break;
1065
- case 'info':
1066
- console.info(log);
1067
- break;
1068
- case 'warn':
1069
- console.warn(log);
1070
- break;
1071
- case 'error':
1072
- console.error(log);
1073
- break;
1074
- }
1075
- console.group(`log @ ${path}`);
1076
- const v = regex$2.exec(path);
1077
- switch (v ? v[1] || v[2] : '') {
1078
- case 'debug':
1079
- console.debug(log);
1080
- break;
1081
- case 'info':
1082
- console.info(log);
1083
- break;
1084
- case 'warn':
1085
- console.warn(log);
1086
- break;
1087
- case 'error':
1088
- console.error(log);
1089
- break;
1090
- default:
1091
- console.log(log);
1092
- break;
1093
- }
1094
- console.groupEnd();
1095
- return true;
1096
- }
1097
- async function defaultRead() {
1098
- return '';
253
+ function setHeader(headers, name, value) {
254
+ if (value) {
255
+ headers.set(name, value);
256
+ } else {
257
+ headers.delete(name);
258
+ }
1099
259
  }
1100
- async function defaultClear() {}
1101
- function initLog({
1102
- read = defaultRead,
1103
- write = defaultWrite,
1104
- clear = defaultClear
1105
- } = {}) {
1106
- function writeLog(path, log, opt) {
1107
- return write(path, pack(log, opt));
1108
- }
1109
- return {
1110
- read,
1111
- write: writeLog,
1112
- clear,
1113
- async debug(log, opt) {
1114
- return writeLog('debug', log, opt);
1115
- },
1116
- async info(log, opt) {
1117
- return writeLog('info', log, opt);
1118
- },
1119
- async warn(log, opt) {
1120
- return writeLog('warn', getErrorLog(log), opt);
1121
- },
1122
- async error(log, opt) {
1123
- return writeLog('error', getErrorLog(log), opt);
1124
- }
1125
- };
1126
- }
1127
-
1128
- function createEnvironment(options) {
1129
- const asset = initAssets(options?.asset);
1130
- const setting = initSettings(options?.setting);
1131
- const log = initLog(options?.log);
1132
- return {
1133
- asset,
1134
- setting,
1135
- log,
1136
- error: options?.error || (e => {
1137
- log.error(e);
1138
- })
1139
- };
260
+ /**
261
+ *
262
+ * @param {Request} request
263
+ * @param {string | ((request: Request) => string)} [toMethod]
264
+ * @returns {import('./types').Method}
265
+ */
266
+ function getMethod(request, toMethod) {
267
+ let methodStr = '';
268
+ if (typeof toMethod === 'string') {
269
+ methodStr = toMethod;
270
+ } else if (typeof toMethod === 'function') {
271
+ methodStr = toMethod(request);
272
+ }
273
+ if (!methodStr || typeof methodStr !== 'string') {
274
+ methodStr = request.method || 'GET';
275
+ }
276
+ return /** @type {import('./types').Method} */(methodStr.toUpperCase());
1140
277
  }
1141
-
1142
- function run(req, getHandler, options) {
1143
- const environment = createEnvironment(options);
1144
- return main(req, getHandler, environment, options?.runner);
278
+ /**
279
+ *
280
+ * @param {Request} request
281
+ * @param {import('./types').FindHandler} getHandler
282
+ * @param {import('./types').Options} [options]
283
+ * @returns {Promise<Response | null>}
284
+ */
285
+ function main(
286
+ request, getHandler,
287
+ { runner, error: echoError, method: toMethod, environment } = {},
288
+ ) {
289
+ /**
290
+ *
291
+ * @param {Request} request
292
+ * @param {import('./types').Context} [parent]
293
+ * @returns {Promise<Response | null>}
294
+ */
295
+ function exec(request, parent) {
296
+ const method = getMethod(request, toMethod);
297
+ const url = new URL(request.url);
298
+ const { signal, headers } = request;
299
+ const aborted = signal2promise(signal);
300
+ /** @type {Map<import('./types').Service<any, any>, Function>} */
301
+ const services = new Map();
302
+ const cookies = getRequestCookies(headers.get('cookie') || '');
303
+ /** @type {import('./types').Cookie[]} */
304
+ const sentCookies = [];
305
+ const responseHeaders = new Headers();
306
+ const root = parent?.root;
307
+ let status = 200;
308
+ let destroyed = false;
309
+ /** @type {any} */
310
+ let error = null;
311
+
312
+ let resolve = () => {};
313
+ /** @type {(error: unknown) => void} */
314
+ let reject = (error) => {};
315
+ /** @type {Promise<void>} */
316
+ const donePromise = new Promise((a, b) => { resolve = a; reject = b; });
317
+ donePromise.catch(() => {});
318
+
319
+ /** @type {any} */
320
+ let params = {};
321
+ /** @type {import('./types').Context} */
322
+ const context = {
323
+ environment,
324
+ parent,
325
+ get error() { return error; },
326
+ get root() { return root || this; },
327
+ signal,
328
+ url,
329
+ fetch(input, { method = 'get', signal, body: data, headers: h } = {}) {
330
+ const fetchUrl = new URL(input, url);
331
+ const headers = new Headers(h || {});
332
+ if (!data || noBodyMethods.has(method.toUpperCase())) {
333
+ return exec(new Request(fetchUrl, { method, headers, signal }), context);
334
+ }
335
+ const body = toBody(data, headers);
336
+ return exec(new Request(fetchUrl, { method, headers, signal, body }), context);
337
+ },
338
+ done(onfulfilled, onrejected) {
339
+ if (destroyed) { return null; }
340
+ const result = donePromise.then(onfulfilled, onrejected);
341
+ result.catch(echoError);
342
+ return result;
343
+ },
344
+ service(service, ...p) {
345
+ if (service.rootOnly && root) {
346
+ return root.service(service, ...p);
347
+ }
348
+ let fn = services.get(service);
349
+ if (!fn) {
350
+ fn = service(context);
351
+ if (typeof fn !== 'function') { return; }
352
+ services.set(service, fn);
353
+ }
354
+ return fn(...p);
355
+ },
356
+
357
+ method,
358
+ get params() { return params; },
359
+ requestHeaders: headers,
360
+ requestType: headers.get('content-type') || '',
361
+ referer: headers.get('referer') || '',
362
+ userAgent: headers.get('user-agent') || '',
363
+ accept: (headers.get('accept') || '').split(/,\s*/).filter(Boolean),
364
+ acceptLanguage: (headers.get('accept-language') || '')
365
+ .split(/,\s*/)
366
+ .filter(Boolean),
367
+ cookies,
368
+ request,
369
+
370
+ get destroyed() { return destroyed; },
371
+ get status() { return status; },
372
+ set status(v) { status = v; },
373
+ responseHeaders,
374
+ get location() { return responseHeaders.get('location') || ''; },
375
+ set location(url) { setHeader(responseHeaders, 'location', url); },
376
+ get responseType() { return responseHeaders.get('content-type') || ''; },
377
+ set responseType(type) { setHeader(responseHeaders, 'content-type', type); },
378
+ getCookie(name) { return getCookie(sentCookies, name); },
379
+ setCookie(name, value, { expire, domain, path, secure, httpOnly } = {}) {
380
+ sentCookies.push({name, value, expire, domain, path, secure, httpOnly});
381
+ setCookiesHeader(responseHeaders, sentCookies);
382
+ },
383
+ /**
384
+ *
385
+ * @param {string | import('./types').CookieOption} [name]
386
+ * @param {import('./types').CookieOption | boolean} [opt]
387
+ * @returns {void}
388
+ */
389
+ clearCookie(name, opt) {
390
+ clearCookie(sentCookies, cookies, name, opt);
391
+ setCookiesHeader(responseHeaders, sentCookies);
392
+ },
393
+ };
394
+ function run() {
395
+ return Promise.race([
396
+ aborted,
397
+ Promise.resolve().then(() => getHandler(context, v => { params = v; })),
398
+ ]).then(handler => handler ? Promise.race([aborted, handler(context)]).then(result => {
399
+ if (result instanceof Response) {
400
+ return result;
401
+ }
402
+ const headers = new Headers(context.responseHeaders);
403
+ const { status } = context;
404
+ if (!result) { return new Response(null, { status, headers }); }
405
+ const body = toBody(result, headers, aborted);
406
+ return new Response(body, { status, headers });
407
+ }) : null).then(response => {
408
+ destroyed = true;
409
+ resolve();
410
+ return response;
411
+ }, e => {
412
+ destroyed = true;
413
+ error = e || true;
414
+ reject(error);
415
+ return Promise.reject(e);
416
+ });
417
+ }
418
+ return runner ? runner(context, run) : run();
419
+ }
420
+ return exec(request);
1145
421
  }
1146
422
 
423
+ /**
424
+ *
425
+ * @param {import('./main/types').FindHandler} getHandler
426
+ * @param {import('./main/types').Options} options
427
+ * @returns {(request: Request) => Promise<Response | null>}
428
+ */
1147
429
  function make(getHandler, options) {
1148
- const environment = createEnvironment(options);
1149
- return r => main(r, getHandler, environment, options?.runner);
430
+ return (r) => main(r, getHandler, options);
1150
431
  }
1151
432
 
433
+ /**
434
+ *
435
+ * @param {import('./main/types').Context} context
436
+ * @param {import('./main/types').Handler[]} handlers
437
+ * @returns {Promise<string | boolean | object | undefined>}
438
+ */
1152
439
  async function runHandles(context, handlers) {
1153
- for (const handle of handlers) {
1154
- if (context.headersSent) {
1155
- break;
1156
- }
1157
- const result = await handle(context);
1158
- if (context.finished) {
1159
- break;
1160
- }
1161
- if (typeof result === 'boolean') {
1162
- return result;
1163
- }
1164
- if (!result) {
1165
- continue;
1166
- }
1167
- return result;
1168
- }
440
+ for (const handle of handlers) {
441
+ const result = await handle(context);
442
+ if (typeof result === 'boolean') { return result; }
443
+ if (!result) { continue; }
444
+ return result;
445
+ }
1169
446
  }
447
+
448
+ /**
449
+ *
450
+ * @param {...(import('./main/types').Handler | import('./main/types').Handler[])} handlers
451
+ * @returns {import('./main/types').Handler}
452
+ */
1170
453
  function merge(...handlers) {
1171
- return ctx => runHandles(ctx, handlers.flat());
454
+ return ctx => runHandles(ctx, handlers.flat());
1172
455
  }
1173
456
 
457
+ /**
458
+ *
459
+ * @template T
460
+ * @template {any[]} P
461
+ * @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>}
466
+ */
467
+ /**
468
+ *
469
+ * @template T
470
+ * @template {any[]} P
471
+ * @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>}
475
+ */
476
+ /**
477
+ * @template T
478
+ * @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>}
483
+ */
1174
484
  function service(exec, destroy, options) {
1175
- const service = function (ctx, ...any) {
1176
- if (ctx.currentService !== service) {
1177
- return ctx.service(service, ...any);
1178
- }
1179
- if (!ctx.destroying) {
1180
- return exec(ctx, ...any);
1181
- }
1182
- if (typeof destroy === 'function') {
1183
- return destroy(ctx);
1184
- }
1185
- };
1186
- const {
1187
- rootOnly
1188
- } = typeof destroy === 'object' && destroy || typeof options === 'object' && options || {};
1189
- Object.assign(service, {
1190
- rootOnly: Boolean(rootOnly)
1191
- });
1192
- return service;
485
+ /** @type {import('./main/types').Service<T, P>} */
486
+ const service = function (ctx) {
487
+ if (typeof destroy === 'function') {
488
+ ctx.done(() => destroy(ctx), error => destroy(ctx, error));
489
+ }
490
+ /**
491
+ * @param {...any} any
492
+ */
493
+ return (...any) => exec(ctx, ...any);
494
+ };
495
+ const {
496
+ rootOnly,
497
+ } = typeof destroy === 'object' && destroy
498
+ || typeof options === 'object' && options
499
+ || {};
500
+ Object.assign(service, { rootOnly: Boolean(rootOnly) });
501
+ return service;
1193
502
  }
1194
503
 
504
+ /**
505
+ *
506
+ * @template T
507
+ * @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>}
511
+ */
512
+ /**
513
+ *
514
+ * @template T
515
+ * @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>}
520
+ */
521
+ /**
522
+ *
523
+ * @template T
524
+ * @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>}
530
+ */
531
+ /**
532
+ * @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>}
538
+ */
1195
539
  function stateService(init, destroy, exec, options) {
1196
- const service = function (ctx) {
1197
- if (ctx.currentService !== service) {
1198
- return ctx.service(service);
1199
- }
1200
- if (!ctx.destroying) {
1201
- let {
1202
- state
1203
- } = ctx;
1204
- if (!state) {
1205
- state = init(ctx) || {};
1206
- ctx.state = state;
1207
- }
1208
- if (typeof exec === 'function') {
1209
- exec(state, ctx);
1210
- }
1211
- return state;
1212
- }
1213
- if (typeof destroy === 'function') {
1214
- return destroy(ctx.state, ctx);
1215
- }
1216
- };
1217
- const {
1218
- rootOnly
1219
- } = typeof destroy === 'object' && destroy || typeof exec === 'object' && exec || typeof options === 'object' && options || {};
1220
- Object.assign(service, {
1221
- rootOnly: Boolean(rootOnly)
1222
- });
1223
- return service;
540
+ /** @type {import('./main/types').StateService<T>} */
541
+ const service = function (ctx) {
542
+ const state = init(ctx) || /** @type {T} */({});
543
+ if (typeof destroy === 'function') {
544
+ ctx.done(() => destroy(state, ctx), error => destroy(state, ctx, error));
545
+ }
546
+ if (typeof exec !== 'function') {
547
+ return () => state;
548
+ }
549
+ return () => {
550
+ exec(state, ctx);
551
+ return state;
552
+ };
553
+ };
554
+ const {
555
+ rootOnly,
556
+ } = typeof destroy === 'object' && destroy
557
+ || typeof exec === 'object' && exec
558
+ || typeof options === 'object' && options
559
+ || {};
560
+ Object.assign(service, { rootOnly: Boolean(rootOnly) });
561
+ return service;
1224
562
  }
1225
563
 
564
+ /**
565
+ *
566
+ * @template T
567
+ * @overload
568
+ * @param {import('./main/types').Service.Options?} [options]
569
+ * @returns {import('./main/types').StoreService<T>}
570
+ */
571
+ /**
572
+ *
573
+ * @template T
574
+ * @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>}
578
+ */
579
+ /**
580
+ *
581
+ * @template T
582
+ * @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>}
587
+ */
588
+ /**
589
+ * @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>}
594
+ */
1226
595
  function storeService(destroy, exec, options) {
1227
- const service = function (ctx, ...s) {
1228
- if (ctx.currentService !== service) {
1229
- return ctx.service(service, ...s);
1230
- }
1231
- if (!ctx.destroying) {
1232
- if (!s.length) {
1233
- return ctx.state;
1234
- }
1235
- const [state] = s;
1236
- ctx.state = state;
1237
- if (typeof exec === 'function') {
1238
- exec(state, ctx);
1239
- }
1240
- return state;
1241
- }
1242
- if (typeof destroy === 'function') {
1243
- return destroy(ctx.state, ctx);
1244
- }
1245
- };
1246
- const {
1247
- rootOnly
1248
- } = typeof destroy === 'object' && destroy || typeof exec === 'object' && exec || typeof options === 'object' && options || {};
1249
- Object.assign(service, {
1250
- rootOnly: Boolean(rootOnly)
1251
- });
1252
- return service;
596
+ /** @type {import('./main/types').StoreService<T>} */
597
+ const service = function (ctx) {
598
+ /** @type {T | undefined} */
599
+ let state;
600
+ if (typeof destroy === 'function') {
601
+ ctx.done(() => destroy(state, ctx), error => destroy(state, ctx, error));
602
+ }
603
+ if (typeof exec !== 'function') {
604
+ /**
605
+ * @param {[s?: T]} s
606
+ */
607
+ return (...s) => {
608
+ if (s.length) { [state] = s; }
609
+ return state;
610
+ };
611
+ }
612
+ /**
613
+ * @param {[s?: T]} s
614
+ */
615
+ return (...s) => {
616
+ if (s.length) {
617
+ [state] = s;
618
+ exec(state, ctx);
619
+ }
620
+ return state;
621
+ };
622
+ };
623
+ const {
624
+ rootOnly,
625
+ } = typeof destroy === 'object' && destroy
626
+ || typeof exec === 'object' && exec
627
+ || typeof options === 'object' && options
628
+ || {};
629
+ Object.assign(service, { rootOnly: Boolean(rootOnly) });
630
+ return service;
1253
631
  }
1254
632
 
633
+ /**
634
+ * @callback Guard
635
+ * @param {import('./main/types').Context} ctx
636
+ * @returns {PromiseLike<boolean | import('./main/types').Handler | void> | boolean | import('./main/types').Handler | void}
637
+ */
638
+
639
+ /**
640
+ * @typedef {[import('./main/types').Handler | Router, Record<string, any>, string[]]} FindItem
641
+ */
642
+ /**
643
+ * @callback Finder
644
+ * @this {Router}
645
+ * @property {import('./main/types').Method} method
646
+ * @property {string[]} path
647
+ * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
648
+ */
649
+
650
+ /**
651
+ *
652
+ * @param {Set<Guard>} guards
653
+ * @param {import('./main/types').Context} ctx
654
+ * @param {(v: any) => void} setParams
655
+ * @param {object} params
656
+ * @returns {Promise<boolean | import('./main/types').Handler>}
657
+ */
1255
658
  async function execGuard(guards, ctx, setParams, params) {
1256
- if (!guards.size) {
1257
- return true;
1258
- }
1259
- setParams(params);
1260
- for (const guard of guards) {
1261
- if (ctx.destroyed) {
1262
- return false;
1263
- }
1264
- const ret = await guard(Object.create(ctx, {
1265
- params: {
1266
- value: {
1267
- ...params
1268
- }
1269
- }
1270
- }));
1271
- if (ret === false) {
1272
- return false;
1273
- }
1274
- if (typeof ret === 'function') {
1275
- return ret;
1276
- }
1277
- }
1278
- return true;
659
+ if (!guards.size) { return true; }
660
+ setParams(params);
661
+ for (const guard of guards) {
662
+ if (ctx.destroyed) { return false; }
663
+ const ret = await guard(Object.create(ctx, {
664
+ params: { value: { ...params } },
665
+ }));
666
+ if (ret === false) { return false; }
667
+ if (typeof ret === 'function') { return ret; }
668
+ }
669
+ return true;
1279
670
  }
671
+
672
+ /**
673
+ *
674
+ * @param {Router | import('./main/types').Handler} route
675
+ * @param {string[]} path
676
+ * @param {import('./main/types').Context} ctx
677
+ * @param {(v: any) => void} setParams
678
+ * @param {object} params
679
+ * @returns {Promise<import('./main/types').Handler | null>}
680
+ */
1280
681
  async function find(route, path, ctx, setParams, params) {
1281
- if (!(route instanceof Router)) {
1282
- setParams(params);
1283
- return route;
1284
- }
1285
- if (route.disabled) {
1286
- return null;
1287
- }
1288
- const guardResult = await execGuard(route.guards, ctx, setParams, params);
1289
- if (!guardResult) {
1290
- return null;
1291
- }
1292
- if (typeof guardResult === 'function') {
1293
- return guardResult;
1294
- }
1295
- if (ctx.destroyed) {
1296
- return null;
1297
- }
1298
- for await (const [r, result, p] of route.find(ctx.method, path)) {
1299
- if (ctx.destroyed) {
1300
- return null;
1301
- }
1302
- const res = await find(r, p, ctx, setParams, {
1303
- ...params,
1304
- ...result
1305
- });
1306
- if (res) {
1307
- return res;
1308
- }
1309
- }
1310
- return null;
682
+ if (!(route instanceof Router)) {
683
+ setParams(params);
684
+ return route;
685
+ }
686
+ if (route.disabled) { return null; }
687
+ const guardResult = await execGuard(route.guards, ctx, setParams, params);
688
+ if (!guardResult) { return null; }
689
+ if (typeof guardResult === 'function') { return guardResult; }
690
+ if (ctx.destroyed) { return null; }
691
+ for await (const [r, result, p] of route.find(ctx.method, path)) {
692
+ if (ctx.destroyed) { return null; }
693
+ const res = await find(r, p, ctx, setParams, { ...params, ...result });
694
+ if (res) { return res; }
695
+ }
696
+ return null;
1311
697
  }
698
+ /**
699
+ *
700
+ * @param {string} t
701
+ * @returns
702
+ */
1312
703
  function uriDecode(t) {
1313
- try {
1314
- return decodeURIComponent(t);
1315
- } catch {
1316
- return t;
1317
- }
704
+ try {
705
+ return decodeURIComponent(t);
706
+ } catch {
707
+ return t;
708
+ }
1318
709
  }
710
+ /**
711
+ * @abstract
712
+ */
1319
713
  class Router {
1320
- disabled = false;
1321
- static make(routers) {
1322
- return async (ctx, setParams) => {
1323
- const list = routers.flat();
1324
- const path = ctx.pathname.split('/').filter(Boolean).map(uriDecode);
1325
- for (const route of list) {
1326
- const res = await find(route, path, ctx, setParams, {});
1327
- if (res) {
1328
- return res;
1329
- }
1330
- }
1331
- return null;
1332
- };
1333
- }
1334
- static create(find) {
1335
- return Object.create(Router.prototype, {
1336
- 'find': {
1337
- configurable: true,
1338
- value: find,
1339
- writable: true
1340
- }
1341
- });
1342
- }
1343
- guards = new Set();
714
+ disabled = false;
715
+ /**
716
+ * @abstract
717
+ * @param {import('./main/types').Method} method
718
+ * @param {string[]} path
719
+ * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
720
+ */
721
+ find(method, path) { return [] }
722
+ /**
723
+ *
724
+ * @param {Router[]} routers
725
+ * @returns {import('./main/types').FindHandler}
726
+ */
727
+ static make(routers) {
728
+ return async (ctx, setParams) => {
729
+ const list = routers.flat();
730
+ const path = ctx.url.pathname.split('/').filter(Boolean).map(uriDecode);
731
+ for (const route of list) {
732
+ const res = await find(route, path, ctx, setParams, {});
733
+ if (res) { return res; }
734
+ }
735
+ return null;
736
+ };
737
+ }
738
+
739
+ /**
740
+ *
741
+ * @param {Finder} find
742
+ * @returns {Router}
743
+ */
744
+ static create(find) {
745
+ return Object.create(Router.prototype, {
746
+ 'find': { configurable: true, value: find, writable: true },
747
+ });
748
+ }
749
+ /** @readonly @type {Set<Guard>} */
750
+ guards = new Set();
1344
751
  }
1345
752
 
1346
- const regex$1 = /^:([a-zA-Z][a-zA-Z0-9]*)(?:\((.+)\))?([ius]+)?([?+*]?)$/;
753
+ /**
754
+ * @typedef {object} Pattern
755
+ * @property {string} name
756
+ * @property {boolean} optional
757
+ * @property {boolean} many
758
+ * @property {RegExp} pattern
759
+ */
760
+
761
+ const regex = /^:([a-zA-Z][a-zA-Z0-9]*)(?:\((.+)\))?([ius]+)?([?+*]?)$/;
762
+ /**
763
+ *
764
+ * @param {string} p
765
+ * @returns {Pattern | string}
766
+ */
1347
767
  function parse(p) {
1348
- const res = regex$1.exec(p);
1349
- if (!res) {
1350
- return p;
1351
- }
1352
- const [, name, expression = '.*', flags, modifier] = res;
1353
- if (!expression) {
1354
- return {
1355
- name,
1356
- pattern: new RegExp('^.*$', flags),
1357
- optional: modifier === '?' || modifier === '*',
1358
- many: modifier === '+' || modifier === '*'
1359
- };
1360
- }
1361
- let i = 0;
1362
- let count = 0;
1363
- const pattern = ['^(?:'];
1364
- while (i < expression.length) {
1365
- const c = expression[i++];
1366
- pattern.push(c);
1367
- if (c === '\\') {
1368
- pattern.push(expression[i++]);
1369
- continue;
1370
- }
1371
- if (c === ')') {
1372
- if (count === 0) {
1373
- return p;
1374
- }
1375
- count--;
1376
- continue;
1377
- }
1378
- if (c === '[') {
1379
- while (i < expression.length) {
1380
- const c = expression[i++];
1381
- pattern.push(c);
1382
- if (c === ']') {
1383
- break;
1384
- }
1385
- if (c !== '\\') {
1386
- continue;
1387
- }
1388
- pattern.push(expression[i++]);
1389
- }
1390
- continue;
1391
- }
1392
- if (c !== '(') {
1393
- continue;
1394
- }
1395
- count++;
1396
- if (expression[i] !== '?') {
1397
- continue;
1398
- }
1399
- i += 2;
1400
- if (expression[i - 1] === ':') {
1401
- continue;
1402
- }
1403
- return p;
1404
- }
1405
- if (count) {
1406
- return p;
1407
- }
1408
- pattern.push(')$');
1409
- return {
1410
- name,
1411
- pattern: new RegExp(pattern.join(''), flags),
1412
- optional: modifier === '?' || modifier === '*',
1413
- many: modifier === '+' || modifier === '*'
1414
- };
768
+ const res = regex.exec(p);
769
+ if (!res) { return p; }
770
+ const [, name, expression = '.*', flags, modifier] = res;
771
+ if (!expression) {
772
+ return {
773
+ name, pattern: new RegExp('^.*$', flags),
774
+ optional: modifier === '?' || modifier === '*',
775
+ many: modifier === '+' || modifier === '*',
776
+ };
777
+ }
778
+ let i = 0;
779
+ let count = 0;
780
+ /** @type {string[]} */
781
+ const pattern = ['^(?:'];
782
+ while (i < expression.length) {
783
+ const c = expression[i++];
784
+ pattern.push(c);
785
+ if (c === '\\') {
786
+ pattern.push(expression[i++]);
787
+ continue;
788
+ }
789
+ if (c === ')') {
790
+ if (count === 0) { return p; }
791
+ count--;
792
+ continue;
793
+ }
794
+ if (c === '[') {
795
+ while (i < expression.length) {
796
+ const c = expression[i++];
797
+ pattern.push(c);
798
+ if (c === ']') { break; }
799
+ if (c !== '\\') { continue; }
800
+ pattern.push(expression[i++]);
801
+ }
802
+ continue;
803
+ }
804
+ if (c !== '(') { continue; }
805
+ count++;
806
+ if (expression[i] !== '?') { continue; }
807
+ i += 2;
808
+ if (expression[i - 1] === ':') { continue; }
809
+ return p;
810
+ }
811
+ if (count) { return p; }
812
+ pattern.push(')$');
813
+ return {
814
+ name, pattern: new RegExp(pattern.join(''), flags),
815
+ optional: modifier === '?' || modifier === '*',
816
+ many: modifier === '+' || modifier === '*',
817
+ };
818
+
1415
819
  }
820
+ /**
821
+ *
822
+ * @param {(Pattern | string)[]} match
823
+ * @param {string[]} path
824
+ * @param {boolean} end
825
+ * @returns {[Record<string, string | string[]>, string[]] | undefined}
826
+ */
1416
827
  function exec(match, path, end) {
1417
- const params = {};
1418
- for (let i = 0; i < match.length; i++) {
1419
- const m = match[i];
1420
- const p = path[i];
1421
- if (m === p) {
1422
- continue;
1423
- }
1424
- if (typeof m === 'string') {
1425
- return;
1426
- }
1427
- if (!p) {
1428
- return m.optional ? [params, []] : undefined;
1429
- }
1430
- if (!m.pattern.test(p)) {
1431
- return;
1432
- }
1433
- params[m.name] = p;
1434
- }
1435
- if (!end) {
1436
- return [params, path.slice(match.length)];
1437
- }
1438
- if (path.length <= match.length) {
1439
- return [params, []];
1440
- }
1441
- const last = match[match.length - 1];
1442
- if (typeof last === 'string') {
1443
- return;
1444
- }
1445
- if (!last.many && path.length > match.length) {
1446
- return;
1447
- }
1448
- for (let j = match.length; j < path.length; j++) {
1449
- if (!last.pattern.test(path[j])) {
1450
- return;
1451
- }
1452
- }
1453
- params[last.name] = path.slice(match.length - 1);
1454
- return [params, []];
828
+ /** @type {Record<string, string | string[]>} */
829
+ const params = {};
830
+ for (let i = 0; i < match.length; i++) {
831
+ const m = match[i];
832
+ const p = path[i];
833
+ if (m === p) { continue; }
834
+ if (typeof m === 'string') { return; }
835
+ if (!p) { return m.optional ? [params, []] : undefined; }
836
+ if (!m.pattern.test(p)) { return; }
837
+ params[m.name] = p;
838
+ }
839
+ if (!end) { return [params, path.slice(match.length)]; }
840
+ if (path.length <= match.length) { return [params, []]; }
841
+ const last = match[match.length - 1];
842
+ if (typeof last === 'string') { return; }
843
+ if (!last.many && path.length > match.length) { return; }
844
+ for (let j = match.length; j < path.length; j++) {
845
+ if (!last.pattern.test(path[j])) { return; }
846
+ }
847
+ params[last.name] = path.slice(match.length - 1);
848
+ return [params, []];
849
+
1455
850
  }
851
+ /**
852
+ *
853
+ * @param {string} path
854
+ * @param {boolean} end
855
+ * @returns {import('./index.mjs').Match | undefined}
856
+ */
1456
857
  function toMatch(path, end) {
1457
- const list = [];
1458
- for (const p of path.split('/')) {
1459
- if (!p || /^\.+$/.test(p)) {
1460
- continue;
1461
- }
1462
- list.push(parse(p));
1463
- }
1464
- if (!list.length) {
1465
- return;
1466
- }
1467
- return path => exec(list, path, end);
858
+ /** @type {(Pattern | string)[]} */
859
+ const list = [];
860
+ for (const p of path.split('/')) {
861
+ if (!p || /^\.+$/.test(p)) { continue; }
862
+ list.push(parse(p));
863
+ }
864
+ if (!list.length) { return; }
865
+ return path => exec(list, path, end);
1468
866
  }
1469
867
 
868
+ /**
869
+ *
870
+ * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
871
+ * @param {import('../main/types').Method[]} methods
872
+ * @param {string} path
873
+ * @param {import('../main/types').Handler} handler
874
+ * @returns {() => void}
875
+ */
1470
876
  function bind(routes, methods, path, handler) {
1471
- const route = {
1472
- match: toMatch(path || '', true),
1473
- methods: new Set(methods),
1474
- handler
1475
- };
1476
- routes.push(route);
1477
- let removed = false;
1478
- return () => {
1479
- if (removed) {
1480
- return;
1481
- }
1482
- removed = true;
1483
- const index = routes.indexOf(route);
1484
- if (index < 0) {
1485
- return;
1486
- }
1487
- routes.splice(index, 1);
1488
- };
1489
- }
877
+ /** @type {import('./index.mjs').Route} */
878
+ const route = {
879
+ match: toMatch(path || '', true),
880
+ methods: new Set(methods),
881
+ handler,
882
+ };
883
+ routes.push(route);
884
+ let removed = false;
885
+ return () => {
886
+ if (removed) { return; }
887
+ removed = true;
888
+ const index = routes.indexOf(route);
889
+ if (index < 0) { return; }
890
+ routes.splice(index, 1);
891
+ };
892
+ }
893
+ /** @type {(v: any) => v is import('../main/types').Handler} */
894
+ const findHandler = v => typeof v === 'function';
895
+ /**
896
+ *
897
+ * @param {(import('./index.mjs').Route | import('./index.mjs').RouterRoute)[]} routes
898
+ * @param {import('../main/types').Method[]} methods
899
+ * @param {any[]} p
900
+ * @returns {import('./index.mjs').Binder | (() => void)}
901
+ */
1490
902
  function verb(routes, methods, p) {
1491
- if (!p.length) {
1492
- return handler => bind(routes, methods, '', handler);
1493
- }
1494
- const [a, b] = p;
1495
- if (a && typeof a === 'object') {
1496
- const path = String.raw(a, ...p.slice(1));
1497
- return handler => bind(routes, methods, path, handler);
1498
- }
1499
- const path = typeof a === 'string' ? a : '';
1500
- const handler = [a, b].find(v => typeof v === 'function');
1501
- if (!handler) {
1502
- return handler => bind(routes, methods, path, handler);
1503
- }
1504
- return bind(routes, methods, path, handler);
903
+ if (!p.length) {
904
+ return handler => bind(routes, methods, '', handler);
905
+ }
906
+ const [a, b] = p;
907
+ if (a && typeof a === 'object') {
908
+ const path = String.raw(a, ...p.slice(1));
909
+ return handler => bind(routes, methods, path, handler);
910
+ }
911
+ const path = typeof a === 'string' ? a : '';
912
+ const handler = [a, b].find(findHandler);
913
+ if (!handler) {
914
+ return handler => bind(routes, methods, path, handler);
915
+ }
916
+ return bind(routes, methods, path, handler);
1505
917
  }
1506
918
 
1507
919
  const methods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']);
1508
- function isMethod(v) {
1509
- return methods.has(v);
1510
- }
920
+ /**
921
+ *
922
+ * @param {any} v
923
+ * @returns {v is import('../main/types').Method}
924
+ */
925
+ function isMethod(v) { return methods.has(v); }
926
+ /**
927
+ *
928
+ * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} [methods]
929
+ * @returns {import('../main/types').Method[]}
930
+ */
1511
931
  function getMethods(methods) {
1512
- if (!methods) {
1513
- return ['GET', 'POST', 'PUT', 'DELETE'];
1514
- }
1515
- if (typeof methods === 'string') {
1516
- return [methods.toUpperCase()].filter(isMethod);
1517
- }
1518
- return Array.from(methods).map(v => typeof v === 'string' && v.toUpperCase()).filter(isMethod);
1519
- }
1520
-
1521
- function bindRouter(routes, path, r) {
1522
- const router = r instanceof Router ? r : typeof r === 'function' ? Router.create(r) : new ApiRouter();
1523
- routes.push({
1524
- match: toMatch(path, false),
1525
- router
1526
- });
1527
- return router;
932
+ if (!methods) {
933
+ return ['GET', 'POST', 'PUT', 'DELETE'];
934
+ }
935
+ if (typeof methods === 'string') {
936
+ return [methods.toUpperCase()].filter(isMethod);
937
+ }
938
+ return Array.from(methods)
939
+ .map(v => typeof v === 'string' && v.toUpperCase())
940
+ .filter(isMethod);
1528
941
  }
1529
- class ApiRouter extends Router {
1530
- /** 路由列表 */
1531
- #routes = [];
1532
- /**
1533
- * 添加子路由
1534
- * @param router 要注册的子路由
1535
- */
1536
-
1537
- /**
1538
- * 添加子路由
1539
- * @param path 要注册的路径
1540
- * @param router 要注册的子路由
1541
- */
1542
-
1543
- /**
1544
- * 添加子路由
1545
- * @param path 要注册的路径
1546
- */
1547
-
1548
- /**
1549
- * 添加子路由
1550
- * @param find 要注册的子路由的 Finder
1551
- */
1552
-
1553
- /**
1554
- * 添加子路由
1555
- * @param path 要注册的路径
1556
- * @param find 要注册的子路由的 Finder
1557
- */
1558
-
1559
- route(...p) {
1560
- const [a] = p;
1561
- if (a && typeof a === 'object' && !(a instanceof Router)) {
1562
- const path = String.raw(a, ...p.slice(1));
1563
- return r => bindRouter(this.#routes, path, r);
1564
- }
1565
- const path = typeof a === 'string' ? a : '';
1566
- const r = typeof a === 'string' ? p[1] : a;
1567
- return bindRouter(this.#routes, path, r);
1568
- }
1569
- *find(method, path) {
1570
- for (const route of Array.from(this.#routes)) {
1571
- if (!route.router && !route.methods.has(method)) {
1572
- continue;
1573
- }
1574
- const {
1575
- match
1576
- } = route;
1577
- if (!match) {
1578
- if (route.router || !path.length) {
1579
- yield [route.router || route.handler, {}, path];
1580
- }
1581
- continue;
1582
- }
1583
- if (!path.length) {
1584
- continue;
1585
- }
1586
- const result = match(path);
1587
- if (!result) {
1588
- continue;
1589
- }
1590
- yield [route.router || route.handler, ...result];
1591
- }
1592
- }
1593
- /**
1594
- * 注册处理函数
1595
- * @param method 要注册的方法
1596
- * @param handler 要注册的处理函数
1597
- */
1598
942
 
1599
- /**
1600
- * 注册处理函数
1601
- * @param method 要注册的方法
1602
- * @param path 要注册的路径
1603
- * @param handler 要注册的处理函数
1604
- */
1605
-
1606
- /**
1607
- * 注册处理函数
1608
- * @param method 要注册的方法
1609
- * @param path 要注册的路径
1610
- */
1611
-
1612
- verb(methods, path, handler) {
1613
- const allMethods = getMethods(methods);
1614
- if (!allMethods.length) {
1615
- return () => {};
1616
- }
1617
- return verb(this.#routes, allMethods, [path, handler]);
1618
- }
1619
- /**
1620
- * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1621
- * @param handler 要注册的处理函数
1622
- */
1623
-
1624
- /**
1625
- * 注册处理函数
1626
- * @param path 要注册的路径
1627
- * @param handler 要注册的处理函数
1628
- */
1629
-
1630
- /**
1631
- * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1632
- * @param path 要注册的路径
1633
- */
1634
-
1635
- /**
1636
- * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1637
- * @param path 要注册的路径
1638
- */
1639
-
1640
- match(...p) {
1641
- return verb(this.#routes, ['GET', 'POST', 'PUT', 'DELETE'], p);
1642
- }
1643
- /**
1644
- * 注册 HTTP GET 处理函数
1645
- * @param handler 要注册的处理函数
1646
- */
1647
-
1648
- /**
1649
- * 注册 HTTP GET 处理函数
1650
- * @param path 要注册的路径
1651
- * @param handler 要注册的处理函数
1652
- */
1653
-
1654
- /**
1655
- * 注册 HTTP GET 处理函数
1656
- * @param path 要注册的路径
1657
- */
1658
-
1659
- /**
1660
- * 注册 HTTP GET 处理函数
1661
- * @param path 要注册的路径
1662
- */
1663
-
1664
- get(...p) {
1665
- return verb(this.#routes, ['GET'], p);
1666
- }
1667
- /**
1668
- * 注册 HTTP POST 处理函数
1669
- * @param handler 要注册的处理函数
1670
- */
1671
-
1672
- /**
1673
- * 注册 HTTP POST 处理函数
1674
- * @param path 要注册的路径
1675
- * @param handler 要注册的处理函数
1676
- */
1677
-
1678
- /**
1679
- * 注册 HTTP POST 处理函数
1680
- * @param path 要注册的路径
1681
- */
1682
-
1683
- /**
1684
- * 注册 HTTP POST 处理函数
1685
- * @param path 要注册的路径
1686
- */
1687
-
1688
- post(...p) {
1689
- return verb(this.#routes, ['POST'], p);
1690
- }
1691
- /**
1692
- * 注册 HTTP PUT 处理函数
1693
- * @param handler 要注册的处理函数
1694
- */
1695
-
1696
- /**
1697
- * 注册 HTTP PUT 处理函数
1698
- * @param path 要注册的路径
1699
- * @param handler 要注册的处理函数
1700
- */
1701
-
1702
- /**
1703
- * 注册 HTTP PUT 处理函数
1704
- * @param path 要注册的路径
1705
- */
1706
-
1707
- /**
1708
- * 注册 HTTP PUT 处理函数
1709
- * @param path 要注册的路径
1710
- */
1711
-
1712
- put(...p) {
1713
- return verb(this.#routes, ['PUT'], p);
1714
- }
1715
- /**
1716
- * 注册 HTTP DELETE 处理函数
1717
- * @param handler 要注册的处理函数
1718
- */
1719
-
1720
- /**
1721
- * 注册 HTTP DELETE 处理函数
1722
- * @param path 要注册的路径
1723
- * @param handler 要注册的处理函数
1724
- */
1725
-
1726
- /**
1727
- * 注册 HTTP DELETE 处理函数
1728
- * @param path 要注册的路径
1729
- */
1730
-
1731
- /**
1732
- * 注册 HTTP DELETE 处理函数
1733
- * @param path 要注册的路径
1734
- */
1735
-
1736
- delete(...p) {
1737
- return verb(this.#routes, ['DELETE'], p);
1738
- }
1739
- /**
1740
- * 注册 HTTP HEAD 处理函数
1741
- * @param handler 要注册的处理函数
1742
- */
1743
-
1744
- /**
1745
- * 注册 HTTP HEAD 处理函数
1746
- * @param path 要注册的路径
1747
- * @param handler 要注册的处理函数
1748
- */
1749
-
1750
- /**
1751
- * 注册 HTTP HEAD 处理函数
1752
- * @param path 要注册的路径
1753
- */
1754
-
1755
- /**
1756
- * 注册 HTTP HEAD 处理函数
1757
- * @param path 要注册的路径
1758
- */
943
+ /**
944
+ * @callback Match
945
+ * @param {string[]} paths
946
+ * @returns {[Record<string, string | string[]>, string[]] | undefined}
947
+ */
1759
948
 
1760
- head(...p) {
1761
- return verb(this.#routes, ['HEAD'], p);
1762
- }
1763
- /**
1764
- * 注册 HTTP OPTIONS 处理函数
1765
- * @param handler 要注册的处理函数
1766
- */
949
+ /**
950
+ * @callback Binder
951
+ * @param {import('../main/types').Handler} handler
952
+ * @returns {() => void}
953
+ */
1767
954
 
1768
- /**
1769
- * 注册 HTTP OPTIONS 处理函数
1770
- * @param path 要注册的路径
1771
- * @param handler 要注册的处理函数
1772
- */
955
+ /**
956
+ * @typedef {object} Route
957
+ * @property {Match} [match] 路径匹配
958
+ * @property {null} [router]
959
+ * @property {string} [plugin] 所属插件
960
+ * @property {import('../main/types').Handler} handler 处理函数
961
+ * @property {Set<import('../main/types').Method>} methods 方法列表
962
+ */
1773
963
 
1774
- /**
1775
- * 注册 HTTP OPTIONS 处理函数
1776
- * @param path 要注册的路径
1777
- */
964
+ /**
965
+ * @typedef {object} RouterRoute
966
+ * @property {Match} [match] 路径匹配
967
+ * @property {Router} router
968
+ */
1778
969
 
1779
- /**
1780
- * 注册 HTTP OPTIONS 处理函数
1781
- * @param path 要注册的路径
1782
- */
1783
970
 
1784
- options(...p) {
1785
- return verb(this.#routes, ['OPTIONS'], p);
1786
- }
971
+ /**
972
+ * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
973
+ * @callback RouteBinder
974
+ * @param {T} [router] 要注册的子路由或子路由的 Finder
975
+ * @returns {T extends import('../Router.mjs').Finder ? Router : T}
976
+ */
977
+ /**
978
+ *
979
+ * @param {(Route | RouterRoute)[]} routes
980
+ * @param {string} path
981
+ * @param {Router | import('../Router.mjs').Finder} [r]
982
+ * @returns {Router}
983
+ */
984
+ function bindRouter(routes, path, r) {
985
+ const router = r instanceof Router ? r
986
+ : typeof r === 'function' ? Router.create(r)
987
+ : new ApiRouter();
988
+ routes.push({ match: toMatch(path, false), router });
989
+ return router;
990
+ }
991
+ class ApiRouter extends Router {
992
+ /** @readonly @type {(Route | RouterRoute)[]} 路由列表 */
993
+ #routes = [];
994
+ /**
995
+ * 添加子路由
996
+ * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
997
+ * @overload
998
+ * @param {T} [router] 要注册的子路由或子路由的 Finder
999
+ * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1000
+ */
1001
+ /**
1002
+ * 添加子路由
1003
+ * @template {Router | import('../Router.mjs').Finder} [T=ApiRouter]
1004
+ * @overload
1005
+ * @param {string} path 要注册的路径
1006
+ * @param {T} [router] 要注册的子路由或子路由的 Finder
1007
+ * @returns {T extends import('../Router.mjs').Finder ? Router : T}
1008
+ */
1009
+ /**
1010
+ * 添加子路由
1011
+ * @overload
1012
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1013
+ * @returns {RouteBinder}
1014
+ */
1015
+ /**
1016
+ * 添加子路由
1017
+ * @param {...any} p 要注册的路径
1018
+ * @returns {Router | RouteBinder}
1019
+ */
1020
+ route(...p) {
1021
+ const [a] = p;
1022
+ if (a && typeof a === 'object' && !(a instanceof Router)) {
1023
+ const path = String.raw(a, ...p.slice(1));
1024
+ /**
1025
+ * @param { import('../Router.mjs').Finder | Router} [r];
1026
+ * @returns {any}
1027
+ */
1028
+ return r => bindRouter(this.#routes, path, r);
1029
+ }
1030
+ const path = typeof a === 'string' ? a : '';
1031
+ const r = typeof a === 'string' ? p[1] : a;
1032
+ return bindRouter(this.#routes, path, r);
1033
+ }
1034
+ /**
1035
+ *
1036
+ * @param {import('../main/types').Method} method
1037
+ * @param {string[]} path
1038
+ * @returns {Iterable<import('../Router.mjs').FindItem>}
1039
+ */
1040
+ *find(method, path) {
1041
+ for (const route of Array.from(this.#routes)) {
1042
+ if (!route.router && !route.methods.has(method)) { continue; }
1043
+ const {match} = route;
1044
+ if (!match) {
1045
+ if (route.router || !path.length) {
1046
+ yield [route.router || route.handler, {}, path];
1047
+ }
1048
+ continue;
1049
+ }
1050
+ if (!path.length) { continue; }
1051
+ const result = match(path);
1052
+ if (!result) { continue; }
1053
+ yield [route.router || route.handler, ...result];
1054
+ }
1055
+ }
1056
+ /**
1057
+ * 注册处理函数
1058
+ * @overload
1059
+ * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1060
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1061
+ * @returns {() => void}
1062
+ */
1063
+ /**
1064
+ * 注册处理函数
1065
+ * @overload
1066
+ * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1067
+ * @param {string} path 要注册的路径
1068
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1069
+ * @returns {() => void}
1070
+ */
1071
+ /**
1072
+ * 注册处理函数
1073
+ * @overload
1074
+ * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} method 要注册的方法
1075
+ * @param {string} path 要注册的路径
1076
+ * @returns {Binder}
1077
+ */
1078
+ /**
1079
+ * @param {import('../main/types').Method | Iterable<import('../main/types').Method> | ArrayLike<import('../main/types').Method>} methods
1080
+ * @param {string| import('../main/types').Handler} [path]
1081
+ * @param {import('../main/types').Handler} [handler]
1082
+ * @returns {Binder | (() => void)}
1083
+ */
1084
+ verb(methods, path, handler) {
1085
+ const allMethods = getMethods(methods);
1086
+ if (!allMethods.length) { return () => {}; }
1087
+ return verb(this.#routes, allMethods, [path, handler]);
1088
+ }
1089
+ /**
1090
+ * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1091
+ * @overload
1092
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1093
+ * @returns {() => void}
1094
+ */
1095
+ /**
1096
+ * 注册处理函数
1097
+ * @overload
1098
+ * @param {string} path 要注册的路径
1099
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1100
+ * @returns {() => void}
1101
+ */
1102
+ /**
1103
+ * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1104
+ * @overload
1105
+ * @param {string} path 要注册的路径
1106
+ * @returns {Binder}
1107
+ */
1108
+ /**
1109
+ * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1110
+ * @overload
1111
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1112
+ * @returns {Binder}
1113
+ */
1114
+ /**
1115
+ * @param {...any} p 要注册的路径
1116
+ * @returns {Binder | (() => void)}
1117
+ */
1118
+ match(...p) {
1119
+ return verb(this.#routes, ['GET', 'POST', 'PUT', 'DELETE'], p);
1120
+ }
1121
+ /**
1122
+ * 注册 HTTP GET 处理函数
1123
+ * @overload
1124
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1125
+ * @returns {() => void}
1126
+ */
1127
+ /**
1128
+ * 注册 HTTP GET 处理函数
1129
+ * @overload
1130
+ * @param {string} path 要注册的路径
1131
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1132
+ * @returns {() => void}
1133
+ */
1134
+ /**
1135
+ * 注册 HTTP GET 处理函数
1136
+ * @overload
1137
+ * @param {string} path 要注册的路径
1138
+ * @returns {Binder}
1139
+ */
1140
+ /**
1141
+ * 注册 HTTP GET 处理函数
1142
+ * @overload
1143
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1144
+ * @returns {Binder}
1145
+ */
1146
+ /**
1147
+ * @param {...any} p 要注册的路径
1148
+ * @returns {Binder | (() => void)}
1149
+ */
1150
+ get(...p) { return verb(this.#routes, ['GET'], p); }
1151
+ /**
1152
+ * 注册 HTTP POST 处理函数
1153
+ * @overload
1154
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1155
+ * @returns {() => void}
1156
+ */
1157
+ /**
1158
+ * 注册 HTTP POST 处理函数
1159
+ * @overload
1160
+ * @param {string} path 要注册的路径
1161
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1162
+ * @returns {() => void}
1163
+ */
1164
+ /**
1165
+ * 注册 HTTP POST 处理函数
1166
+ * @overload
1167
+ * @param {string} path 要注册的路径
1168
+ * @returns {Binder}
1169
+ */
1170
+ /**
1171
+ * 注册 HTTP POST 处理函数
1172
+ * @overload
1173
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1174
+ * @returns {Binder}
1175
+ */
1176
+ /**
1177
+ * @param {...any} p 要注册的路径
1178
+ * @returns {Binder | (() => void)}
1179
+ */
1180
+ post(...p) { return verb(this.#routes, ['POST'], p); }
1181
+ /**
1182
+ * 注册 HTTP PUT 处理函数
1183
+ * @overload
1184
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1185
+ * @returns {() => void}
1186
+ */
1187
+ /**
1188
+ * 注册 HTTP PUT 处理函数
1189
+ * @overload
1190
+ * @param {string} path 要注册的路径
1191
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1192
+ * @returns {() => void}
1193
+ */
1194
+ /**
1195
+ * 注册 HTTP PUT 处理函数
1196
+ * @overload
1197
+ * @param {string} path 要注册的路径
1198
+ * @returns {Binder}
1199
+ */
1200
+ /**
1201
+ * 注册 HTTP PUT 处理函数
1202
+ * @overload
1203
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1204
+ * @returns {Binder}
1205
+ */
1206
+ /**
1207
+ * @param {...any} p 要注册的路径
1208
+ * @returns {Binder | (() => void)}
1209
+ */
1210
+ put(...p) { return verb(this.#routes, ['PUT'], p); }
1211
+ /**
1212
+ * 注册 HTTP DELETE 处理函数
1213
+ * @overload
1214
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1215
+ * @returns {() => void}
1216
+ */
1217
+ /**
1218
+ * 注册 HTTP DELETE 处理函数
1219
+ * @overload
1220
+ * @param {string} path 要注册的路径
1221
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1222
+ * @returns {() => void}
1223
+ */
1224
+ /**
1225
+ * 注册 HTTP DELETE 处理函数
1226
+ * @overload
1227
+ * @param {string} path 要注册的路径
1228
+ * @returns {Binder}
1229
+ */
1230
+ /**
1231
+ * 注册 HTTP DELETE 处理函数
1232
+ * @overload
1233
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1234
+ * @returns {Binder}
1235
+ */
1236
+ /**
1237
+ * @param {...any} p 要注册的路径
1238
+ * @returns {Binder | (() => void)}
1239
+ */
1240
+ delete(...p) { return verb(this.#routes, ['DELETE'], p); }
1241
+ /**
1242
+ * 注册 HTTP HEAD 处理函数
1243
+ * @overload
1244
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1245
+ * @returns {() => void}
1246
+ */
1247
+ /**
1248
+ * 注册 HTTP HEAD 处理函数
1249
+ * @overload
1250
+ * @param {string} path 要注册的路径
1251
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1252
+ * @returns {() => void}
1253
+ */
1254
+ /**
1255
+ * 注册 HTTP HEAD 处理函数
1256
+ * @overload
1257
+ * @param {string} path 要注册的路径
1258
+ * @returns {Binder}
1259
+ */
1260
+ /**
1261
+ * 注册 HTTP HEAD 处理函数
1262
+ * @overload
1263
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1264
+ * @returns {Binder}
1265
+ */
1266
+ /**
1267
+ * @param {...any} p 要注册的路径
1268
+ * @returns {Binder | (() => void)}
1269
+ */
1270
+ head(...p) { return verb(this.#routes, ['HEAD'], p); }
1271
+ /**
1272
+ * 注册 HTTP OPTIONS 处理函数
1273
+ * @overload
1274
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1275
+ * @returns {() => void}
1276
+ */
1277
+ /**
1278
+ * 注册 HTTP OPTIONS 处理函数
1279
+ * @overload
1280
+ * @param {string} path 要注册的路径
1281
+ * @param {import('../main/types').Handler} handler 要注册的处理函数
1282
+ * @returns {() => void}
1283
+ */
1284
+ /**
1285
+ * 注册 HTTP OPTIONS 处理函数
1286
+ * @overload
1287
+ * @param {string} path 要注册的路径
1288
+ * @returns {Binder}
1289
+ */
1290
+ /**
1291
+ * 注册 HTTP OPTIONS 处理函数
1292
+ * @overload
1293
+ * @param {...Parameters<typeof String.raw>} path 要注册的路径
1294
+ * @returns {Binder}
1295
+ */
1296
+ /**
1297
+ * @param {...any} p 要注册的路径
1298
+ * @returns {Binder | (() => void)}
1299
+ */
1300
+ options(...p) { return verb(this.#routes, ['OPTIONS'], p); }
1787
1301
  }
1788
1302
 
1789
- const idRegexText = '[a-zA-Z][a-zA-Z0-9_-]*';
1790
- const kRegexText = `${idRegexText}(?:.${idRegexText})*`;
1791
- const regexText = `^/(${idRegexText})/((?:@${kRegexText}/)?${kRegexText})/(.+)$`;
1792
- const regex = new RegExp(regexText);
1793
- class Plugin {
1794
- static make(plugins, {
1795
- router,
1796
- setting,
1797
- asset,
1798
- log,
1799
- runner
1800
- } = {}) {
1801
- const routers = [];
1802
- for (const plugin of Object.values(plugins)) {
1803
- routers.push(plugin.router);
1804
- }
1805
- if (router instanceof Router) {
1806
- routers.push(router);
1807
- }
1808
- return make(Router.make(routers), {
1809
- setting,
1810
- asset: Plugin.bindAsset(asset, plugins),
1811
- log,
1812
- runner
1813
- });
1814
- }
1815
- static bindAsset(api, plugins, pluginPath = 'plugins') {
1816
- if (!plugins) {
1817
- return api;
1818
- }
1819
- const read = api?.read;
1820
- if (typeof read !== 'function') {
1821
- return api;
1822
- }
1823
- return {
1824
- ...api,
1825
- read: async path => {
1826
- const ret = await read(path);
1827
- if (ret !== null) {
1828
- return ret;
1829
- }
1830
- if (!plugins) {
1831
- return null;
1832
- }
1833
- const r = regex.exec(path);
1834
- if (!r) {
1835
- return null;
1836
- }
1837
- const [, base, name, subpath] = r;
1838
- if (base !== pluginPath) {
1839
- return null;
1840
- }
1841
- if (!(name in plugins)) {
1842
- return null;
1843
- }
1844
- const plugin = plugins[name];
1845
- if (!plugin) {
1846
- return null;
1847
- }
1848
- return plugin.readAsset(subpath);
1849
- }
1850
- };
1851
- }
1852
-
1853
- /** 包名 */
1854
-
1855
- /** 版本 */
1856
-
1857
- /** 作者 */
1858
-
1859
- /** 开源协议 */
1860
-
1861
- constructor(name, /** 版本 */
1862
- version, {
1863
- author,
1864
- license
1865
- } = {}) {
1866
- this.name = name;
1867
- this.version = typeof version === 'string' ? version : '';
1868
- this.author = typeof author === 'string' ? author : '';
1869
- this.license = typeof license === 'string' ? license : '';
1870
- }
1871
- initRouter() {
1872
- const {
1873
- router
1874
- } = this;
1875
- if (this.__initRouterPromise) {
1876
- return this.__initRouterPromise;
1877
- }
1878
- return this.__initRouterPromise = Promise.resolve(this._initRouter(router)).then(() => router);
1879
- }
1880
- get router() {
1881
- const router = this._createRouter();
1882
- Reflect.defineProperty(this, 'router', {
1883
- value: router,
1884
- configurable: true
1885
- });
1886
- this.initRouter();
1887
- return router;
1888
- }
1889
- get disabled() {
1890
- return this.router.disabled;
1891
- }
1892
- set disabled(t) {
1893
- this.router.disabled = t;
1894
- }
1303
+ /**
1304
+ *
1305
+ * @param {(request: Request) => Promise<Response | null>} run
1306
+ * @param {((request: Request) => Response | Promise<Response>)?} [notFound]
1307
+ * @returns {(input: RequestInfo, init?: RequestInit) => Promise<Response>}
1308
+ */
1309
+ function createFetch(run, notFound) {
1310
+ /**
1311
+ * @param {RequestInfo} input
1312
+ * @param {RequestInit} [init]
1313
+ * @returns {Promise<Response>}
1314
+ */
1315
+ return async function fetch(input, init) {
1316
+ const request = new Request(input, init);
1317
+ const {signal} = request;
1318
+ signal.throwIfAborted();
1319
+ const r = await run(request);
1320
+ if (r) { return r; }
1321
+ if (typeof notFound === 'function') { return notFound(request); }
1322
+ return new Response(null, { status: 404 });
1323
+ };
1895
1324
  }
1896
1325
 
1897
1326
  exports.ApiRouter = ApiRouter;
1898
- exports.Plugin = Plugin;
1899
1327
  exports.Router = Router;
1900
- exports.createEnvironment = createEnvironment;
1328
+ exports.createFetch = createFetch;
1901
1329
  exports.main = main;
1902
1330
  exports.make = make;
1903
1331
  exports.merge = merge;
1904
- exports.run = run;
1905
1332
  exports.service = service;
1906
1333
  exports.stateService = stateService;
1907
1334
  exports.storeService = storeService;