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 +1245 -1818
- package/index.d.ts +350 -460
- package/index.js +1245 -1818
- package/index.min.js +3 -3
- package/index.min.mjs +3 -3
- package/index.mjs +1246 -1817
- package/node/index.cjs +118 -1045
- package/node/index.d.cts +49 -232
- package/package.json +2 -15
- package/services.cjs +64 -92
- package/services.d.ts +7 -7
- package/services.js +64 -92
- package/services.min.js +3 -3
- package/services.min.mjs +3 -3
- package/services.mjs +64 -92
- package/browser.cjs +0 -447
- package/browser.d.ts +0 -38
- package/browser.js +0 -453
- package/browser.min.js +0 -6
- package/browser.min.mjs +0 -6
- package/browser.mjs +0 -439
- package/cli/index.cjs +0 -200
- package/cli/index.d.cts +0 -81
- package/cli.cjs +0 -14
- package/starter.cjs +0 -15
package/index.cjs
CHANGED
|
@@ -1,1907 +1,1334 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* k99 v0.6.0-
|
|
3
|
-
* (c) 2019-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
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
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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
|
|
1017
|
-
* @param
|
|
248
|
+
*
|
|
249
|
+
* @param {Headers} headers
|
|
250
|
+
* @param {string} name
|
|
251
|
+
* @param {string} [value]
|
|
1018
252
|
*/
|
|
1019
|
-
function
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
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
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
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
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
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
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
704
|
+
try {
|
|
705
|
+
return decodeURIComponent(t);
|
|
706
|
+
} catch {
|
|
707
|
+
return t;
|
|
708
|
+
}
|
|
1318
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* @abstract
|
|
712
|
+
*/
|
|
1319
713
|
class Router {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
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
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
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
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
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
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
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
|
-
|
|
1509
|
-
|
|
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
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
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
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
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
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
* @param handler 要注册的处理函数
|
|
1766
|
-
*/
|
|
949
|
+
/**
|
|
950
|
+
* @callback Binder
|
|
951
|
+
* @param {import('../main/types').Handler} handler
|
|
952
|
+
* @returns {() => void}
|
|
953
|
+
*/
|
|
1767
954
|
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
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
|
-
|
|
1776
|
-
|
|
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
|
-
|
|
1785
|
-
|
|
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
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
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.
|
|
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;
|