@kevisual/router 0.0.71 → 0.0.72
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/dist/app.d.ts +5 -0
- package/dist/app.js +19504 -0
- package/dist/opencode.d.ts +2 -2
- package/dist/opencode.js +13380 -14323
- package/dist/router-browser.d.ts +19 -20
- package/dist/router-browser.js +14046 -15099
- package/dist/router-define.d.ts +394 -3
- package/dist/router-define.js +126 -124
- package/dist/router-simple.js +665 -759
- package/dist/router.d.ts +19 -20
- package/dist/router.js +17822 -21151
- package/dist/ws.d.ts +2 -2
- package/dist/ws.js +2980 -150
- package/package.json +19 -54
- package/src/app.ts +2 -2
- package/src/browser.ts +1 -1
- package/src/index.ts +1 -1
- package/src/route.ts +10 -10
- package/src/router-define.ts +1 -1
- package/src/utils/random.ts +8 -0
package/dist/router-simple.js
CHANGED
|
@@ -1,788 +1,694 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const NOOP_VALUE = (value) => value;
|
|
19
|
-
const ID_START = /^[$_\p{ID_Start}]$/u;
|
|
20
|
-
const ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u;
|
|
21
|
-
const SIMPLE_TOKENS = {
|
|
22
|
-
// Groups.
|
|
23
|
-
"{": "{",
|
|
24
|
-
"}": "}",
|
|
25
|
-
// Reserved.
|
|
26
|
-
"(": "(",
|
|
27
|
-
")": ")",
|
|
28
|
-
"[": "[",
|
|
29
|
-
"]": "]",
|
|
30
|
-
"+": "+",
|
|
31
|
-
"?": "?",
|
|
32
|
-
"!": "!",
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Escape text for stringify to path.
|
|
36
|
-
*/
|
|
37
|
-
function escapeText(str) {
|
|
38
|
-
return str.replace(/[{}()\[\]+?!:*\\]/g, "\\$&");
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Escape a regular expression string.
|
|
42
|
-
*/
|
|
43
|
-
function escape(str) {
|
|
44
|
-
return str.replace(/[.+*?^${}()[\]|/\\]/g, "\\$&");
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Tokenized path instance.
|
|
48
|
-
*/
|
|
49
|
-
class TokenData {
|
|
50
|
-
constructor(tokens, originalPath) {
|
|
51
|
-
this.tokens = tokens;
|
|
52
|
-
this.originalPath = originalPath;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
dist.TokenData = TokenData;
|
|
56
|
-
/**
|
|
57
|
-
* ParseError is thrown when there is an error processing the path.
|
|
58
|
-
*/
|
|
59
|
-
class PathError extends TypeError {
|
|
60
|
-
constructor(message, originalPath) {
|
|
61
|
-
let text = message;
|
|
62
|
-
if (originalPath)
|
|
63
|
-
text += `: ${originalPath}`;
|
|
64
|
-
text += `; visit https://git.new/pathToRegexpError for info`;
|
|
65
|
-
super(text);
|
|
66
|
-
this.originalPath = originalPath;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
dist.PathError = PathError;
|
|
70
|
-
/**
|
|
71
|
-
* Parse a string for the raw tokens.
|
|
72
|
-
*/
|
|
73
|
-
function parse(str, options = {}) {
|
|
74
|
-
const { encodePath = NOOP_VALUE } = options;
|
|
75
|
-
const chars = [...str];
|
|
76
|
-
const tokens = [];
|
|
77
|
-
let index = 0;
|
|
78
|
-
let pos = 0;
|
|
79
|
-
function name() {
|
|
80
|
-
let value = "";
|
|
81
|
-
if (ID_START.test(chars[index])) {
|
|
82
|
-
do {
|
|
83
|
-
value += chars[index++];
|
|
84
|
-
} while (ID_CONTINUE.test(chars[index]));
|
|
85
|
-
}
|
|
86
|
-
else if (chars[index] === '"') {
|
|
87
|
-
let quoteStart = index;
|
|
88
|
-
while (index++ < chars.length) {
|
|
89
|
-
if (chars[index] === '"') {
|
|
90
|
-
index++;
|
|
91
|
-
quoteStart = 0;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
// Increment over escape characters.
|
|
95
|
-
if (chars[index] === "\\")
|
|
96
|
-
index++;
|
|
97
|
-
value += chars[index];
|
|
98
|
-
}
|
|
99
|
-
if (quoteStart) {
|
|
100
|
-
throw new PathError(`Unterminated quote at index ${quoteStart}`, str);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (!value) {
|
|
104
|
-
throw new PathError(`Missing parameter name at index ${index}`, str);
|
|
105
|
-
}
|
|
106
|
-
return value;
|
|
107
|
-
}
|
|
108
|
-
while (index < chars.length) {
|
|
109
|
-
const value = chars[index];
|
|
110
|
-
const type = SIMPLE_TOKENS[value];
|
|
111
|
-
if (type) {
|
|
112
|
-
tokens.push({ type, index: index++, value });
|
|
113
|
-
}
|
|
114
|
-
else if (value === "\\") {
|
|
115
|
-
tokens.push({ type: "escape", index: index++, value: chars[index++] });
|
|
116
|
-
}
|
|
117
|
-
else if (value === ":") {
|
|
118
|
-
tokens.push({ type: "param", index: index++, value: name() });
|
|
119
|
-
}
|
|
120
|
-
else if (value === "*") {
|
|
121
|
-
tokens.push({ type: "wildcard", index: index++, value: name() });
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
tokens.push({ type: "char", index: index++, value });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
tokens.push({ type: "end", index, value: "" });
|
|
128
|
-
function consumeUntil(endType) {
|
|
129
|
-
const output = [];
|
|
130
|
-
while (true) {
|
|
131
|
-
const token = tokens[pos++];
|
|
132
|
-
if (token.type === endType)
|
|
133
|
-
break;
|
|
134
|
-
if (token.type === "char" || token.type === "escape") {
|
|
135
|
-
let path = token.value;
|
|
136
|
-
let cur = tokens[pos];
|
|
137
|
-
while (cur.type === "char" || cur.type === "escape") {
|
|
138
|
-
path += cur.value;
|
|
139
|
-
cur = tokens[++pos];
|
|
140
|
-
}
|
|
141
|
-
output.push({
|
|
142
|
-
type: "text",
|
|
143
|
-
value: encodePath(path),
|
|
144
|
-
});
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (token.type === "param" || token.type === "wildcard") {
|
|
148
|
-
output.push({
|
|
149
|
-
type: token.type,
|
|
150
|
-
name: token.value,
|
|
151
|
-
});
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (token.type === "{") {
|
|
155
|
-
output.push({
|
|
156
|
-
type: "group",
|
|
157
|
-
tokens: consumeUntil("}"),
|
|
158
|
-
});
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
throw new PathError(`Unexpected ${token.type} at index ${token.index}, expected ${endType}`, str);
|
|
162
|
-
}
|
|
163
|
-
return output;
|
|
164
|
-
}
|
|
165
|
-
return new TokenData(consumeUntil("end"), str);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Compile a string to a template function for the path.
|
|
169
|
-
*/
|
|
170
|
-
function compile(path, options = {}) {
|
|
171
|
-
const { encode = encodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
|
|
172
|
-
const data = typeof path === "object" ? path : parse(path, options);
|
|
173
|
-
const fn = tokensToFunction(data.tokens, delimiter, encode);
|
|
174
|
-
return function path(params = {}) {
|
|
175
|
-
const [path, ...missing] = fn(params);
|
|
176
|
-
if (missing.length) {
|
|
177
|
-
throw new TypeError(`Missing parameters: ${missing.join(", ")}`);
|
|
178
|
-
}
|
|
179
|
-
return path;
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function tokensToFunction(tokens, delimiter, encode) {
|
|
183
|
-
const encoders = tokens.map((token) => tokenToFunction(token, delimiter, encode));
|
|
184
|
-
return (data) => {
|
|
185
|
-
const result = [""];
|
|
186
|
-
for (const encoder of encoders) {
|
|
187
|
-
const [value, ...extras] = encoder(data);
|
|
188
|
-
result[0] += value;
|
|
189
|
-
result.push(...extras);
|
|
190
|
-
}
|
|
191
|
-
return result;
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Convert a single token into a path building function.
|
|
196
|
-
*/
|
|
197
|
-
function tokenToFunction(token, delimiter, encode) {
|
|
198
|
-
if (token.type === "text")
|
|
199
|
-
return () => [token.value];
|
|
200
|
-
if (token.type === "group") {
|
|
201
|
-
const fn = tokensToFunction(token.tokens, delimiter, encode);
|
|
202
|
-
return (data) => {
|
|
203
|
-
const [value, ...missing] = fn(data);
|
|
204
|
-
if (!missing.length)
|
|
205
|
-
return [value];
|
|
206
|
-
return [""];
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const encodeValue = encode || NOOP_VALUE;
|
|
210
|
-
if (token.type === "wildcard" && encode !== false) {
|
|
211
|
-
return (data) => {
|
|
212
|
-
const value = data[token.name];
|
|
213
|
-
if (value == null)
|
|
214
|
-
return ["", token.name];
|
|
215
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
216
|
-
throw new TypeError(`Expected "${token.name}" to be a non-empty array`);
|
|
217
|
-
}
|
|
218
|
-
return [
|
|
219
|
-
value
|
|
220
|
-
.map((value, index) => {
|
|
221
|
-
if (typeof value !== "string") {
|
|
222
|
-
throw new TypeError(`Expected "${token.name}/${index}" to be a string`);
|
|
223
|
-
}
|
|
224
|
-
return encodeValue(value);
|
|
225
|
-
})
|
|
226
|
-
.join(delimiter),
|
|
227
|
-
];
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
return (data) => {
|
|
231
|
-
const value = data[token.name];
|
|
232
|
-
if (value == null)
|
|
233
|
-
return ["", token.name];
|
|
234
|
-
if (typeof value !== "string") {
|
|
235
|
-
throw new TypeError(`Expected "${token.name}" to be a string`);
|
|
236
|
-
}
|
|
237
|
-
return [encodeValue(value)];
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Transform a path into a match function.
|
|
242
|
-
*/
|
|
243
|
-
function match(path, options = {}) {
|
|
244
|
-
const { decode = decodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
|
|
245
|
-
const { regexp, keys } = pathToRegexp(path, options);
|
|
246
|
-
const decoders = keys.map((key) => {
|
|
247
|
-
if (decode === false)
|
|
248
|
-
return NOOP_VALUE;
|
|
249
|
-
if (key.type === "param")
|
|
250
|
-
return decode;
|
|
251
|
-
return (value) => value.split(delimiter).map(decode);
|
|
252
|
-
});
|
|
253
|
-
return function match(input) {
|
|
254
|
-
const m = regexp.exec(input);
|
|
255
|
-
if (!m)
|
|
256
|
-
return false;
|
|
257
|
-
const path = m[0];
|
|
258
|
-
const params = Object.create(null);
|
|
259
|
-
for (let i = 1; i < m.length; i++) {
|
|
260
|
-
if (m[i] === undefined)
|
|
261
|
-
continue;
|
|
262
|
-
const key = keys[i - 1];
|
|
263
|
-
const decoder = decoders[i - 1];
|
|
264
|
-
params[key.name] = decoder(m[i]);
|
|
265
|
-
}
|
|
266
|
-
return { path, params };
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
function pathToRegexp(path, options = {}) {
|
|
270
|
-
const { delimiter = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true, } = options;
|
|
271
|
-
const keys = [];
|
|
272
|
-
const flags = sensitive ? "" : "i";
|
|
273
|
-
const sources = [];
|
|
274
|
-
for (const input of pathsToArray(path, [])) {
|
|
275
|
-
const data = typeof input === "object" ? input : parse(input, options);
|
|
276
|
-
for (const tokens of flatten(data.tokens, 0, [])) {
|
|
277
|
-
sources.push(toRegExpSource(tokens, delimiter, keys, data.originalPath));
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
let pattern = `^(?:${sources.join("|")})`;
|
|
281
|
-
if (trailing)
|
|
282
|
-
pattern += `(?:${escape(delimiter)}$)?`;
|
|
283
|
-
pattern += end ? "$" : `(?=${escape(delimiter)}|$)`;
|
|
284
|
-
const regexp = new RegExp(pattern, flags);
|
|
285
|
-
return { regexp, keys };
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Convert a path or array of paths into a flat array.
|
|
289
|
-
*/
|
|
290
|
-
function pathsToArray(paths, init) {
|
|
291
|
-
if (Array.isArray(paths)) {
|
|
292
|
-
for (const p of paths)
|
|
293
|
-
pathsToArray(p, init);
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
init.push(paths);
|
|
297
|
-
}
|
|
298
|
-
return init;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Generate a flat list of sequence tokens from the given tokens.
|
|
302
|
-
*/
|
|
303
|
-
function* flatten(tokens, index, init) {
|
|
304
|
-
if (index === tokens.length) {
|
|
305
|
-
return yield init;
|
|
306
|
-
}
|
|
307
|
-
const token = tokens[index];
|
|
308
|
-
if (token.type === "group") {
|
|
309
|
-
for (const seq of flatten(token.tokens, 0, init.slice())) {
|
|
310
|
-
yield* flatten(tokens, index + 1, seq);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
init.push(token);
|
|
315
|
-
}
|
|
316
|
-
yield* flatten(tokens, index + 1, init);
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Transform a flat sequence of tokens into a regular expression.
|
|
320
|
-
*/
|
|
321
|
-
function toRegExpSource(tokens, delimiter, keys, originalPath) {
|
|
322
|
-
let result = "";
|
|
323
|
-
let backtrack = "";
|
|
324
|
-
let isSafeSegmentParam = true;
|
|
325
|
-
for (const token of tokens) {
|
|
326
|
-
if (token.type === "text") {
|
|
327
|
-
result += escape(token.value);
|
|
328
|
-
backtrack += token.value;
|
|
329
|
-
isSafeSegmentParam || (isSafeSegmentParam = token.value.includes(delimiter));
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
if (token.type === "param" || token.type === "wildcard") {
|
|
333
|
-
if (!isSafeSegmentParam && !backtrack) {
|
|
334
|
-
throw new PathError(`Missing text before "${token.name}" ${token.type}`, originalPath);
|
|
335
|
-
}
|
|
336
|
-
if (token.type === "param") {
|
|
337
|
-
result += `(${negate(delimiter, isSafeSegmentParam ? "" : backtrack)}+)`;
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
result += `([\\s\\S]+)`;
|
|
341
|
-
}
|
|
342
|
-
keys.push(token);
|
|
343
|
-
backtrack = "";
|
|
344
|
-
isSafeSegmentParam = false;
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Block backtracking on previous text and ignore delimiter string.
|
|
352
|
-
*/
|
|
353
|
-
function negate(delimiter, backtrack) {
|
|
354
|
-
if (backtrack.length < 2) {
|
|
355
|
-
if (delimiter.length < 2)
|
|
356
|
-
return `[^${escape(delimiter + backtrack)}]`;
|
|
357
|
-
return `(?:(?!${escape(delimiter)})[^${escape(backtrack)}])`;
|
|
358
|
-
}
|
|
359
|
-
if (delimiter.length < 2) {
|
|
360
|
-
return `(?:(?!${escape(backtrack)})[^${escape(delimiter)}])`;
|
|
361
|
-
}
|
|
362
|
-
return `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\\s\\S])`;
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Stringify an array of tokens into a path string.
|
|
366
|
-
*/
|
|
367
|
-
function stringifyTokens(tokens) {
|
|
368
|
-
let value = "";
|
|
369
|
-
let i = 0;
|
|
370
|
-
function name(value) {
|
|
371
|
-
const isSafe = isNameSafe(value) && isNextNameSafe(tokens[i]);
|
|
372
|
-
return isSafe ? value : JSON.stringify(value);
|
|
373
|
-
}
|
|
374
|
-
while (i < tokens.length) {
|
|
375
|
-
const token = tokens[i++];
|
|
376
|
-
if (token.type === "text") {
|
|
377
|
-
value += escapeText(token.value);
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
if (token.type === "group") {
|
|
381
|
-
value += `{${stringifyTokens(token.tokens)}}`;
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
if (token.type === "param") {
|
|
385
|
-
value += `:${name(token.name)}`;
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
if (token.type === "wildcard") {
|
|
389
|
-
value += `*${name(token.name)}`;
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
throw new TypeError(`Unknown token type: ${token.type}`);
|
|
393
|
-
}
|
|
394
|
-
return value;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Stringify token data into a path string.
|
|
398
|
-
*/
|
|
399
|
-
function stringify(data) {
|
|
400
|
-
return stringifyTokens(data.tokens);
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Validate the parameter name contains valid ID characters.
|
|
404
|
-
*/
|
|
405
|
-
function isNameSafe(name) {
|
|
406
|
-
const [first, ...rest] = name;
|
|
407
|
-
return ID_START.test(first) && rest.every((char) => ID_CONTINUE.test(char));
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Validate the next token does not interfere with the current param name.
|
|
411
|
-
*/
|
|
412
|
-
function isNextNameSafe(token) {
|
|
413
|
-
if (token && token.type === "text")
|
|
414
|
-
return !ID_CONTINUE.test(token.value[0]);
|
|
415
|
-
return true;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
return dist;
|
|
419
|
-
}
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
7
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
8
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
9
|
+
for (let key of __getOwnPropNames(mod))
|
|
10
|
+
if (!__hasOwnProp.call(to, key))
|
|
11
|
+
__defProp(to, key, {
|
|
12
|
+
get: () => mod[key],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
420
18
|
|
|
421
|
-
|
|
19
|
+
// node_modules/.pnpm/path-to-regexp@8.3.0/node_modules/path-to-regexp/dist/index.js
|
|
20
|
+
var require_dist = __commonJS((exports) => {
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.PathError = exports.TokenData = undefined;
|
|
23
|
+
exports.parse = parse;
|
|
24
|
+
exports.compile = compile;
|
|
25
|
+
exports.match = match;
|
|
26
|
+
exports.pathToRegexp = pathToRegexp;
|
|
27
|
+
exports.stringify = stringify;
|
|
28
|
+
var DEFAULT_DELIMITER = "/";
|
|
29
|
+
var NOOP_VALUE = (value) => value;
|
|
30
|
+
var ID_START = /^[$_\p{ID_Start}]$/u;
|
|
31
|
+
var ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u;
|
|
32
|
+
var SIMPLE_TOKENS = {
|
|
33
|
+
"{": "{",
|
|
34
|
+
"}": "}",
|
|
35
|
+
"(": "(",
|
|
36
|
+
")": ")",
|
|
37
|
+
"[": "[",
|
|
38
|
+
"]": "]",
|
|
39
|
+
"+": "+",
|
|
40
|
+
"?": "?",
|
|
41
|
+
"!": "!"
|
|
42
|
+
};
|
|
43
|
+
function escapeText(str) {
|
|
44
|
+
return str.replace(/[{}()\[\]+?!:*\\]/g, "\\$&");
|
|
45
|
+
}
|
|
46
|
+
function escape(str) {
|
|
47
|
+
return str.replace(/[.+*?^${}()[\]|/\\]/g, "\\$&");
|
|
48
|
+
}
|
|
422
49
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
50
|
+
class TokenData {
|
|
51
|
+
constructor(tokens, originalPath) {
|
|
52
|
+
this.tokens = tokens;
|
|
53
|
+
this.originalPath = originalPath;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.TokenData = TokenData;
|
|
428
57
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
58
|
+
class PathError extends TypeError {
|
|
59
|
+
constructor(message, originalPath) {
|
|
60
|
+
let text = message;
|
|
61
|
+
if (originalPath)
|
|
62
|
+
text += `: ${originalPath}`;
|
|
63
|
+
text += `; visit https://git.new/pathToRegexpError for info`;
|
|
64
|
+
super(text);
|
|
65
|
+
this.originalPath = originalPath;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.PathError = PathError;
|
|
69
|
+
function parse(str, options = {}) {
|
|
70
|
+
const { encodePath = NOOP_VALUE } = options;
|
|
71
|
+
const chars = [...str];
|
|
72
|
+
const tokens = [];
|
|
73
|
+
let index = 0;
|
|
74
|
+
let pos = 0;
|
|
75
|
+
function name() {
|
|
76
|
+
let value = "";
|
|
77
|
+
if (ID_START.test(chars[index])) {
|
|
78
|
+
do {
|
|
79
|
+
value += chars[index++];
|
|
80
|
+
} while (ID_CONTINUE.test(chars[index]));
|
|
81
|
+
} else if (chars[index] === '"') {
|
|
82
|
+
let quoteStart = index;
|
|
83
|
+
while (index++ < chars.length) {
|
|
84
|
+
if (chars[index] === '"') {
|
|
85
|
+
index++;
|
|
86
|
+
quoteStart = 0;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
if (chars[index] === "\\")
|
|
90
|
+
index++;
|
|
91
|
+
value += chars[index];
|
|
439
92
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const formData = new URLSearchParams(body);
|
|
443
|
-
const result = {};
|
|
444
|
-
formData.forEach((value, key) => {
|
|
445
|
-
// 尝试将值解析为 JSON,如果失败则保留原始字符串
|
|
446
|
-
try {
|
|
447
|
-
result[key] = JSON.parse(value);
|
|
448
|
-
}
|
|
449
|
-
catch {
|
|
450
|
-
result[key] = value;
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
return resolve(result);
|
|
93
|
+
if (quoteStart) {
|
|
94
|
+
throw new PathError(`Unterminated quote at index ${quoteStart}`, str);
|
|
454
95
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
96
|
+
}
|
|
97
|
+
if (!value) {
|
|
98
|
+
throw new PathError(`Missing parameter name at index ${index}`, str);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
while (index < chars.length) {
|
|
103
|
+
const value = chars[index];
|
|
104
|
+
const type = SIMPLE_TOKENS[value];
|
|
105
|
+
if (type) {
|
|
106
|
+
tokens.push({ type, index: index++, value });
|
|
107
|
+
} else if (value === "\\") {
|
|
108
|
+
tokens.push({ type: "escape", index: index++, value: chars[index++] });
|
|
109
|
+
} else if (value === ":") {
|
|
110
|
+
tokens.push({ type: "param", index: index++, value: name() });
|
|
111
|
+
} else if (value === "*") {
|
|
112
|
+
tokens.push({ type: "wildcard", index: index++, value: name() });
|
|
113
|
+
} else {
|
|
114
|
+
tokens.push({ type: "char", index: index++, value });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
tokens.push({ type: "end", index, value: "" });
|
|
118
|
+
function consumeUntil(endType) {
|
|
119
|
+
const output = [];
|
|
120
|
+
while (true) {
|
|
121
|
+
const token = tokens[pos++];
|
|
122
|
+
if (token.type === endType)
|
|
123
|
+
break;
|
|
124
|
+
if (token.type === "char" || token.type === "escape") {
|
|
125
|
+
let path = token.value;
|
|
126
|
+
let cur = tokens[pos];
|
|
127
|
+
while (cur.type === "char" || cur.type === "escape") {
|
|
128
|
+
path += cur.value;
|
|
129
|
+
cur = tokens[++pos];
|
|
130
|
+
}
|
|
131
|
+
output.push({
|
|
132
|
+
type: "text",
|
|
133
|
+
value: encodePath(path)
|
|
134
|
+
});
|
|
135
|
+
continue;
|
|
458
136
|
}
|
|
459
|
-
|
|
460
|
-
|
|
137
|
+
if (token.type === "param" || token.type === "wildcard") {
|
|
138
|
+
output.push({
|
|
139
|
+
type: token.type,
|
|
140
|
+
name: token.value
|
|
141
|
+
});
|
|
142
|
+
continue;
|
|
461
143
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
144
|
+
if (token.type === "{") {
|
|
145
|
+
output.push({
|
|
146
|
+
type: "group",
|
|
147
|
+
tokens: consumeUntil("}")
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
468
150
|
}
|
|
469
|
-
|
|
151
|
+
throw new PathError(`Unexpected ${token.type} at index ${token.index}, expected ${endType}`, str);
|
|
152
|
+
}
|
|
153
|
+
return output;
|
|
470
154
|
}
|
|
471
|
-
return new
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
155
|
+
return new TokenData(consumeUntil("end"), str);
|
|
156
|
+
}
|
|
157
|
+
function compile(path, options = {}) {
|
|
158
|
+
const { encode = encodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
|
|
159
|
+
const data = typeof path === "object" ? path : parse(path, options);
|
|
160
|
+
const fn = tokensToFunction(data.tokens, delimiter, encode);
|
|
161
|
+
return function path2(params = {}) {
|
|
162
|
+
const [path3, ...missing] = fn(params);
|
|
163
|
+
if (missing.length) {
|
|
164
|
+
throw new TypeError(`Missing parameters: ${missing.join(", ")}`);
|
|
165
|
+
}
|
|
166
|
+
return path3;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function tokensToFunction(tokens, delimiter, encode) {
|
|
170
|
+
const encoders = tokens.map((token) => tokenToFunction(token, delimiter, encode));
|
|
171
|
+
return (data) => {
|
|
172
|
+
const result = [""];
|
|
173
|
+
for (const encoder of encoders) {
|
|
174
|
+
const [value, ...extras] = encoder(data);
|
|
175
|
+
result[0] += value;
|
|
176
|
+
result.push(...extras);
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function tokenToFunction(token, delimiter, encode) {
|
|
182
|
+
if (token.type === "text")
|
|
183
|
+
return () => [token.value];
|
|
184
|
+
if (token.type === "group") {
|
|
185
|
+
const fn = tokensToFunction(token.tokens, delimiter, encode);
|
|
186
|
+
return (data) => {
|
|
187
|
+
const [value, ...missing] = fn(data);
|
|
188
|
+
if (!missing.length)
|
|
189
|
+
return [value];
|
|
190
|
+
return [""];
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const encodeValue = encode || NOOP_VALUE;
|
|
194
|
+
if (token.type === "wildcard" && encode !== false) {
|
|
195
|
+
return (data) => {
|
|
196
|
+
const value = data[token.name];
|
|
197
|
+
if (value == null)
|
|
198
|
+
return ["", token.name];
|
|
199
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
200
|
+
throw new TypeError(`Expected "${token.name}" to be a non-empty array`);
|
|
201
|
+
}
|
|
202
|
+
return [
|
|
203
|
+
value.map((value2, index) => {
|
|
204
|
+
if (typeof value2 !== "string") {
|
|
205
|
+
throw new TypeError(`Expected "${token.name}/${index}" to be a string`);
|
|
483
206
|
}
|
|
484
|
-
|
|
207
|
+
return encodeValue(value2);
|
|
208
|
+
}).join(delimiter)
|
|
209
|
+
];
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return (data) => {
|
|
213
|
+
const value = data[token.name];
|
|
214
|
+
if (value == null)
|
|
215
|
+
return ["", token.name];
|
|
216
|
+
if (typeof value !== "string") {
|
|
217
|
+
throw new TypeError(`Expected "${token.name}" to be a string`);
|
|
218
|
+
}
|
|
219
|
+
return [encodeValue(value)];
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function match(path, options = {}) {
|
|
223
|
+
const { decode = decodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
|
|
224
|
+
const { regexp, keys } = pathToRegexp(path, options);
|
|
225
|
+
const decoders = keys.map((key) => {
|
|
226
|
+
if (decode === false)
|
|
227
|
+
return NOOP_VALUE;
|
|
228
|
+
if (key.type === "param")
|
|
229
|
+
return decode;
|
|
230
|
+
return (value) => value.split(delimiter).map(decode);
|
|
485
231
|
});
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
232
|
+
return function match2(input) {
|
|
233
|
+
const m = regexp.exec(input);
|
|
234
|
+
if (!m)
|
|
235
|
+
return false;
|
|
236
|
+
const path2 = m[0];
|
|
237
|
+
const params = Object.create(null);
|
|
238
|
+
for (let i = 1;i < m.length; i++) {
|
|
239
|
+
if (m[i] === undefined)
|
|
240
|
+
continue;
|
|
241
|
+
const key = keys[i - 1];
|
|
242
|
+
const decoder = decoders[i - 1];
|
|
243
|
+
params[key.name] = decoder(m[i]);
|
|
244
|
+
}
|
|
245
|
+
return { path: path2, params };
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function pathToRegexp(path, options = {}) {
|
|
249
|
+
const { delimiter = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true } = options;
|
|
250
|
+
const keys = [];
|
|
251
|
+
const flags = sensitive ? "" : "i";
|
|
252
|
+
const sources = [];
|
|
253
|
+
for (const input of pathsToArray(path, [])) {
|
|
254
|
+
const data = typeof input === "object" ? input : parse(input, options);
|
|
255
|
+
for (const tokens of flatten(data.tokens, 0, [])) {
|
|
256
|
+
sources.push(toRegExpSource(tokens, delimiter, keys, data.originalPath));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
let pattern = `^(?:${sources.join("|")})`;
|
|
260
|
+
if (trailing)
|
|
261
|
+
pattern += `(?:${escape(delimiter)}$)?`;
|
|
262
|
+
pattern += end ? "$" : `(?=${escape(delimiter)}|$)`;
|
|
263
|
+
const regexp = new RegExp(pattern, flags);
|
|
264
|
+
return { regexp, keys };
|
|
265
|
+
}
|
|
266
|
+
function pathsToArray(paths, init) {
|
|
267
|
+
if (Array.isArray(paths)) {
|
|
268
|
+
for (const p of paths)
|
|
269
|
+
pathsToArray(p, init);
|
|
270
|
+
} else {
|
|
271
|
+
init.push(paths);
|
|
272
|
+
}
|
|
273
|
+
return init;
|
|
274
|
+
}
|
|
275
|
+
function* flatten(tokens, index, init) {
|
|
276
|
+
if (index === tokens.length) {
|
|
277
|
+
return yield init;
|
|
278
|
+
}
|
|
279
|
+
const token = tokens[index];
|
|
280
|
+
if (token.type === "group") {
|
|
281
|
+
for (const seq of flatten(token.tokens, 0, init.slice())) {
|
|
282
|
+
yield* flatten(tokens, index + 1, seq);
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
init.push(token);
|
|
286
|
+
}
|
|
287
|
+
yield* flatten(tokens, index + 1, init);
|
|
288
|
+
}
|
|
289
|
+
function toRegExpSource(tokens, delimiter, keys, originalPath) {
|
|
290
|
+
let result = "";
|
|
291
|
+
let backtrack = "";
|
|
292
|
+
let isSafeSegmentParam = true;
|
|
293
|
+
for (const token of tokens) {
|
|
294
|
+
if (token.type === "text") {
|
|
295
|
+
result += escape(token.value);
|
|
296
|
+
backtrack += token.value;
|
|
297
|
+
isSafeSegmentParam || (isSafeSegmentParam = token.value.includes(delimiter));
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (token.type === "param" || token.type === "wildcard") {
|
|
301
|
+
if (!isSafeSegmentParam && !backtrack) {
|
|
302
|
+
throw new PathError(`Missing text before "${token.name}" ${token.type}`, originalPath);
|
|
303
|
+
}
|
|
304
|
+
if (token.type === "param") {
|
|
305
|
+
result += `(${negate(delimiter, isSafeSegmentParam ? "" : backtrack)}+)`;
|
|
306
|
+
} else {
|
|
307
|
+
result += `([\\s\\S]+)`;
|
|
308
|
+
}
|
|
309
|
+
keys.push(token);
|
|
310
|
+
backtrack = "";
|
|
311
|
+
isSafeSegmentParam = false;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
function negate(delimiter, backtrack) {
|
|
318
|
+
if (backtrack.length < 2) {
|
|
319
|
+
if (delimiter.length < 2)
|
|
320
|
+
return `[^${escape(delimiter + backtrack)}]`;
|
|
321
|
+
return `(?:(?!${escape(delimiter)})[^${escape(backtrack)}])`;
|
|
322
|
+
}
|
|
323
|
+
if (delimiter.length < 2) {
|
|
324
|
+
return `(?:(?!${escape(backtrack)})[^${escape(delimiter)}])`;
|
|
325
|
+
}
|
|
326
|
+
return `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\\s\\S])`;
|
|
327
|
+
}
|
|
328
|
+
function stringifyTokens(tokens) {
|
|
329
|
+
let value = "";
|
|
330
|
+
let i = 0;
|
|
331
|
+
function name(value2) {
|
|
332
|
+
const isSafe = isNameSafe(value2) && isNextNameSafe(tokens[i]);
|
|
333
|
+
return isSafe ? value2 : JSON.stringify(value2);
|
|
334
|
+
}
|
|
335
|
+
while (i < tokens.length) {
|
|
336
|
+
const token = tokens[i++];
|
|
337
|
+
if (token.type === "text") {
|
|
338
|
+
value += escapeText(token.value);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (token.type === "group") {
|
|
342
|
+
value += `{${stringifyTokens(token.tokens)}}`;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (token.type === "param") {
|
|
346
|
+
value += `:${name(token.name)}`;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (token.type === "wildcard") {
|
|
350
|
+
value += `*${name(token.name)}`;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
throw new TypeError(`Unknown token type: ${token.type}`);
|
|
354
|
+
}
|
|
355
|
+
return value;
|
|
356
|
+
}
|
|
357
|
+
function stringify(data) {
|
|
358
|
+
return stringifyTokens(data.tokens);
|
|
359
|
+
}
|
|
360
|
+
function isNameSafe(name) {
|
|
361
|
+
const [first, ...rest] = name;
|
|
362
|
+
return ID_START.test(first) && rest.every((char) => ID_CONTINUE.test(char));
|
|
363
|
+
}
|
|
364
|
+
function isNextNameSafe(token) {
|
|
365
|
+
if (token && token.type === "text")
|
|
366
|
+
return !ID_CONTINUE.test(token.value[0]);
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// src/router-simple.ts
|
|
372
|
+
var import_path_to_regexp = __toESM(require_dist(), 1);
|
|
373
|
+
|
|
374
|
+
// src/server/parse-body.ts
|
|
375
|
+
import url from "node:url";
|
|
376
|
+
|
|
377
|
+
// src/utils/is-engine.ts
|
|
378
|
+
var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
379
|
+
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined" && typeof document.createElement === "function";
|
|
380
|
+
var isDeno = typeof Deno !== "undefined" && typeof Deno.version === "object" && typeof Deno.version.deno === "string";
|
|
381
|
+
var isBun = typeof Bun !== "undefined" && typeof Bun.version === "string";
|
|
382
|
+
|
|
383
|
+
// src/server/parse-body.ts
|
|
384
|
+
var parseBody = async (req) => {
|
|
385
|
+
const resolveBody = (body) => {
|
|
386
|
+
const contentType = req.headers["content-type"] || "";
|
|
387
|
+
const resolve = (data) => {
|
|
388
|
+
return data;
|
|
389
|
+
};
|
|
390
|
+
if (contentType.includes("application/json")) {
|
|
391
|
+
return resolve(JSON.parse(body));
|
|
392
|
+
}
|
|
393
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
394
|
+
const formData = new URLSearchParams(body);
|
|
395
|
+
const result = {};
|
|
396
|
+
formData.forEach((value, key) => {
|
|
397
|
+
try {
|
|
398
|
+
result[key] = JSON.parse(value);
|
|
399
|
+
} catch {
|
|
400
|
+
result[key] = value;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return resolve(result);
|
|
501
404
|
}
|
|
502
405
|
try {
|
|
503
|
-
|
|
406
|
+
return resolve(JSON.parse(body));
|
|
407
|
+
} catch {
|
|
408
|
+
return resolve({});
|
|
504
409
|
}
|
|
505
|
-
|
|
506
|
-
|
|
410
|
+
};
|
|
411
|
+
if (isBun) {
|
|
412
|
+
const body = req.body;
|
|
413
|
+
if (body) {
|
|
414
|
+
return resolveBody(body);
|
|
507
415
|
}
|
|
416
|
+
return {};
|
|
417
|
+
}
|
|
418
|
+
return new Promise((resolve, reject) => {
|
|
419
|
+
const arr = [];
|
|
420
|
+
req.on("data", (chunk) => {
|
|
421
|
+
arr.push(chunk);
|
|
422
|
+
});
|
|
423
|
+
req.on("end", () => {
|
|
424
|
+
try {
|
|
425
|
+
const body = Buffer.concat(arr).toString();
|
|
426
|
+
resolve(resolveBody(body));
|
|
427
|
+
} catch (e) {
|
|
428
|
+
resolve({});
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
};
|
|
433
|
+
var parseSearch = (req) => {
|
|
434
|
+
const parsedUrl = url.parse(req.url, true);
|
|
435
|
+
return parsedUrl.query;
|
|
436
|
+
};
|
|
437
|
+
var parseSearchValue = (value, opts) => {
|
|
438
|
+
if (!value)
|
|
439
|
+
return {};
|
|
440
|
+
const decode = opts?.decode ?? false;
|
|
441
|
+
if (decode) {
|
|
442
|
+
value = decodeURIComponent(value);
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
return JSON.parse(value);
|
|
446
|
+
} catch (e) {
|
|
447
|
+
return {};
|
|
448
|
+
}
|
|
508
449
|
};
|
|
509
450
|
|
|
510
|
-
|
|
511
|
-
* SimpleRouter
|
|
512
|
-
*/
|
|
451
|
+
// src/router-simple.ts
|
|
513
452
|
class SimpleRouter {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
try {
|
|
552
|
-
return JSON.parse(v);
|
|
553
|
-
}
|
|
554
|
-
catch (e) {
|
|
555
|
-
return {};
|
|
556
|
-
}
|
|
453
|
+
routes = [];
|
|
454
|
+
exclude = [];
|
|
455
|
+
constructor(opts) {
|
|
456
|
+
this.exclude = opts?.exclude || ["/api/router"];
|
|
457
|
+
}
|
|
458
|
+
getBody(req) {
|
|
459
|
+
return parseBody(req);
|
|
460
|
+
}
|
|
461
|
+
getSearch(req) {
|
|
462
|
+
return parseSearch(req);
|
|
463
|
+
}
|
|
464
|
+
parseSearchValue = parseSearchValue;
|
|
465
|
+
use(method, route, ...fns) {
|
|
466
|
+
const handlers = Array.isArray(fns) ? fns.flat() : [];
|
|
467
|
+
const pattern = import_path_to_regexp.pathToRegexp(route);
|
|
468
|
+
this.routes.push({ method: method.toLowerCase(), regexp: pattern.regexp, keys: pattern.keys, handlers });
|
|
469
|
+
return this;
|
|
470
|
+
}
|
|
471
|
+
get(route, ...fns) {
|
|
472
|
+
return this.use("get", route, ...fns);
|
|
473
|
+
}
|
|
474
|
+
post(route, ...fns) {
|
|
475
|
+
return this.use("post", route, ...fns);
|
|
476
|
+
}
|
|
477
|
+
sse(route, ...fns) {
|
|
478
|
+
return this.use("sse", route, ...fns);
|
|
479
|
+
}
|
|
480
|
+
all(route, ...fns) {
|
|
481
|
+
this.use("post", route, ...fns);
|
|
482
|
+
this.use("get", route, ...fns);
|
|
483
|
+
this.use("sse", route, ...fns);
|
|
484
|
+
return this;
|
|
485
|
+
}
|
|
486
|
+
getJson(v) {
|
|
487
|
+
if (typeof v === "object") {
|
|
488
|
+
return v;
|
|
557
489
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if (headers['accept'] && headers['accept'].includes('text/event-stream')) {
|
|
563
|
-
return true;
|
|
564
|
-
}
|
|
565
|
-
if (headers['content-type'] && headers['content-type'].includes('text/event-stream')) {
|
|
566
|
-
return true;
|
|
567
|
-
}
|
|
568
|
-
return false;
|
|
490
|
+
try {
|
|
491
|
+
return JSON.parse(v);
|
|
492
|
+
} catch (e) {
|
|
493
|
+
return {};
|
|
569
494
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
return 'is_exclude';
|
|
581
|
-
}
|
|
582
|
-
const isSse = this.isSse(req);
|
|
583
|
-
if (isSse)
|
|
584
|
-
method = 'sse';
|
|
585
|
-
const route = this.routes.find((route) => {
|
|
586
|
-
const matchResult = route.regexp.exec(pathname);
|
|
587
|
-
if (matchResult && route.method === method) {
|
|
588
|
-
const params = {};
|
|
589
|
-
route.keys.forEach((key, i) => {
|
|
590
|
-
params[key.name] = matchResult[i + 1];
|
|
591
|
-
});
|
|
592
|
-
req.params = params;
|
|
593
|
-
return true;
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
if (route) {
|
|
597
|
-
const { handlers } = route;
|
|
598
|
-
return handlers.reduce((promiseChain, handler) => promiseChain.then(() => Promise.resolve(handler(req, res))), Promise.resolve());
|
|
599
|
-
}
|
|
600
|
-
return 'not_found';
|
|
495
|
+
}
|
|
496
|
+
isSse(req) {
|
|
497
|
+
const { headers } = req;
|
|
498
|
+
if (!headers)
|
|
499
|
+
return false;
|
|
500
|
+
if (headers["accept"] && headers["accept"].includes("text/event-stream")) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
if (headers["content-type"] && headers["content-type"].includes("text/event-stream")) {
|
|
504
|
+
return true;
|
|
601
505
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const chain = new HttpChain({ req, res, simpleRouter: this });
|
|
610
|
-
return chain;
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
parse(req, res) {
|
|
509
|
+
const { pathname } = new URL(req.url, "http://localhost");
|
|
510
|
+
let method = req.method.toLowerCase();
|
|
511
|
+
if (this.exclude.includes(pathname)) {
|
|
512
|
+
return "is_exclude";
|
|
611
513
|
}
|
|
612
|
-
|
|
613
|
-
|
|
514
|
+
const isSse = this.isSse(req);
|
|
515
|
+
if (isSse)
|
|
516
|
+
method = "sse";
|
|
517
|
+
const route = this.routes.find((route2) => {
|
|
518
|
+
const matchResult = route2.regexp.exec(pathname);
|
|
519
|
+
if (matchResult && route2.method === method) {
|
|
520
|
+
const params = {};
|
|
521
|
+
route2.keys.forEach((key, i) => {
|
|
522
|
+
params[key.name] = matchResult[i + 1];
|
|
523
|
+
});
|
|
524
|
+
req.params = params;
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
if (route) {
|
|
529
|
+
const { handlers } = route;
|
|
530
|
+
return handlers.reduce((promiseChain, handler) => promiseChain.then(() => Promise.resolve(handler(req, res))), Promise.resolve());
|
|
614
531
|
}
|
|
532
|
+
return "not_found";
|
|
533
|
+
}
|
|
534
|
+
chain(req, res) {
|
|
535
|
+
const chain = new HttpChain({ req, res, simpleRouter: this });
|
|
536
|
+
return chain;
|
|
537
|
+
}
|
|
538
|
+
static Chain(opts) {
|
|
539
|
+
return new HttpChain(opts);
|
|
540
|
+
}
|
|
615
541
|
}
|
|
616
|
-
|
|
617
|
-
* HttpChain 类, 用于链式调用,router.get内部使用
|
|
618
|
-
*/
|
|
542
|
+
|
|
619
543
|
class HttpChain {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
res
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
return this;
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* 兼容 express 的一点功能
|
|
690
|
-
* @param data
|
|
691
|
-
* @returns
|
|
692
|
-
*/
|
|
693
|
-
end(data) {
|
|
694
|
-
if (!this.res)
|
|
695
|
-
return this;
|
|
696
|
-
if (typeof data === 'object') {
|
|
697
|
-
this.res.end(JSON.stringify(data));
|
|
698
|
-
}
|
|
699
|
-
else if (typeof data === 'string') {
|
|
700
|
-
this.res.end(data);
|
|
701
|
-
}
|
|
702
|
-
else {
|
|
703
|
-
this.res.end('nothing');
|
|
704
|
-
}
|
|
705
|
-
return this;
|
|
706
|
-
}
|
|
707
|
-
listen(opts, callback) {
|
|
708
|
-
this.server.listen(opts, callback);
|
|
709
|
-
return this;
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* 外部 parse 方法
|
|
713
|
-
* @returns
|
|
714
|
-
*/
|
|
715
|
-
parse(opts) {
|
|
716
|
-
const { listenOptions, listenCallBack } = opts || {};
|
|
717
|
-
if (!this.server || !this.simpleRouter) {
|
|
718
|
-
throw new Error('Server and SimpleRouter must be set before calling parse');
|
|
719
|
-
}
|
|
720
|
-
const that = this;
|
|
721
|
-
const listener = (req, res) => {
|
|
722
|
-
try {
|
|
723
|
-
that.simpleRouter.parse(req, res);
|
|
724
|
-
}
|
|
725
|
-
catch (error) {
|
|
726
|
-
console.error('Error parsing request:', error);
|
|
727
|
-
if (!res.headersSent) {
|
|
728
|
-
res.writeHead(500);
|
|
729
|
-
res.end(JSON.stringify({ code: 500, message: 'Internal Server Error' }));
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
if (listenOptions) {
|
|
734
|
-
this.server.listen(listenOptions, listenCallBack);
|
|
735
|
-
}
|
|
736
|
-
this.server.on('request', listener);
|
|
737
|
-
return () => {
|
|
738
|
-
that.server.removeListener('request', listener);
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
getString(value) {
|
|
742
|
-
if (typeof value === 'string') {
|
|
743
|
-
return value;
|
|
744
|
-
}
|
|
745
|
-
return JSON.stringify(value);
|
|
746
|
-
}
|
|
747
|
-
sse(value) {
|
|
748
|
-
const res = this.res;
|
|
749
|
-
const req = this.req;
|
|
750
|
-
if (!res || !req)
|
|
751
|
-
return;
|
|
752
|
-
const data = this.getString(value);
|
|
753
|
-
if (this.isSseSet) {
|
|
754
|
-
res.write(`data: ${data}\n\n`);
|
|
755
|
-
return this;
|
|
756
|
-
}
|
|
757
|
-
const headersMap = new Map([
|
|
758
|
-
['Content-Type', 'text/event-stream'],
|
|
759
|
-
['Cache-Control', 'no-cache'],
|
|
760
|
-
['Connection', 'keep-alive'],
|
|
761
|
-
]);
|
|
762
|
-
this.isSseSet = true;
|
|
763
|
-
let intervalId;
|
|
764
|
-
if (!this.hasSetHeader) {
|
|
765
|
-
this.hasSetHeader = true;
|
|
766
|
-
res.setHeaders(headersMap);
|
|
767
|
-
// 每隔 2 秒发送一个空行,保持连接
|
|
768
|
-
setInterval(() => {
|
|
769
|
-
res.write('\n'); // 发送一个空行,保持连接
|
|
770
|
-
}, 3000);
|
|
771
|
-
// 客户端断开连接时清理
|
|
772
|
-
req.on('close', () => {
|
|
773
|
-
clearInterval(intervalId);
|
|
774
|
-
res.end();
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
this.res.write(`data: ${data}\n\n`);
|
|
778
|
-
return this;
|
|
544
|
+
req;
|
|
545
|
+
res;
|
|
546
|
+
simpleRouter;
|
|
547
|
+
server;
|
|
548
|
+
hasSetHeader = false;
|
|
549
|
+
isSseSet = false;
|
|
550
|
+
constructor(opts) {
|
|
551
|
+
if (opts?.res) {
|
|
552
|
+
this.res = opts.res;
|
|
553
|
+
}
|
|
554
|
+
if (opts?.req) {
|
|
555
|
+
this.req = opts.req;
|
|
556
|
+
}
|
|
557
|
+
this.simpleRouter = opts?.simpleRouter;
|
|
558
|
+
}
|
|
559
|
+
setReq(req) {
|
|
560
|
+
this.req = req;
|
|
561
|
+
return this;
|
|
562
|
+
}
|
|
563
|
+
setRes(res) {
|
|
564
|
+
this.res = res;
|
|
565
|
+
return this;
|
|
566
|
+
}
|
|
567
|
+
setRouter(router) {
|
|
568
|
+
this.simpleRouter = router;
|
|
569
|
+
return this;
|
|
570
|
+
}
|
|
571
|
+
setServer(server) {
|
|
572
|
+
this.server = server;
|
|
573
|
+
return this;
|
|
574
|
+
}
|
|
575
|
+
status(status) {
|
|
576
|
+
if (!this.res)
|
|
577
|
+
return this;
|
|
578
|
+
if (this.hasSetHeader) {
|
|
579
|
+
return this;
|
|
580
|
+
}
|
|
581
|
+
this.hasSetHeader = true;
|
|
582
|
+
this.res.writeHead(status);
|
|
583
|
+
return this;
|
|
584
|
+
}
|
|
585
|
+
writeHead(status) {
|
|
586
|
+
if (!this.res)
|
|
587
|
+
return this;
|
|
588
|
+
if (this.hasSetHeader) {
|
|
589
|
+
return this;
|
|
590
|
+
}
|
|
591
|
+
this.hasSetHeader = true;
|
|
592
|
+
this.res.writeHead(status);
|
|
593
|
+
return this;
|
|
594
|
+
}
|
|
595
|
+
json(data) {
|
|
596
|
+
if (!this.res)
|
|
597
|
+
return this;
|
|
598
|
+
this.res.end(JSON.stringify(data));
|
|
599
|
+
return this;
|
|
600
|
+
}
|
|
601
|
+
end(data) {
|
|
602
|
+
if (!this.res)
|
|
603
|
+
return this;
|
|
604
|
+
if (typeof data === "object") {
|
|
605
|
+
this.res.end(JSON.stringify(data));
|
|
606
|
+
} else if (typeof data === "string") {
|
|
607
|
+
this.res.end(data);
|
|
608
|
+
} else {
|
|
609
|
+
this.res.end("nothing");
|
|
779
610
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
611
|
+
return this;
|
|
612
|
+
}
|
|
613
|
+
listen(opts, callback) {
|
|
614
|
+
this.server.listen(opts, callback);
|
|
615
|
+
return this;
|
|
616
|
+
}
|
|
617
|
+
parse(opts) {
|
|
618
|
+
const { listenOptions, listenCallBack } = opts || {};
|
|
619
|
+
if (!this.server || !this.simpleRouter) {
|
|
620
|
+
throw new Error("Server and SimpleRouter must be set before calling parse");
|
|
621
|
+
}
|
|
622
|
+
const that = this;
|
|
623
|
+
const listener = (req, res) => {
|
|
624
|
+
try {
|
|
625
|
+
that.simpleRouter.parse(req, res);
|
|
626
|
+
} catch (error) {
|
|
627
|
+
console.error("Error parsing request:", error);
|
|
628
|
+
if (!res.headersSent) {
|
|
629
|
+
res.writeHead(500);
|
|
630
|
+
res.end(JSON.stringify({ code: 500, message: "Internal Server Error" }));
|
|
783
631
|
}
|
|
784
|
-
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
if (listenOptions) {
|
|
635
|
+
this.server.listen(listenOptions, listenCallBack);
|
|
785
636
|
}
|
|
786
|
-
|
|
637
|
+
this.server.on("request", listener);
|
|
638
|
+
return () => {
|
|
639
|
+
that.server.removeListener("request", listener);
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
getString(value) {
|
|
643
|
+
if (typeof value === "string") {
|
|
644
|
+
return value;
|
|
645
|
+
}
|
|
646
|
+
return JSON.stringify(value);
|
|
647
|
+
}
|
|
648
|
+
sse(value) {
|
|
649
|
+
const res = this.res;
|
|
650
|
+
const req = this.req;
|
|
651
|
+
if (!res || !req)
|
|
652
|
+
return;
|
|
653
|
+
const data = this.getString(value);
|
|
654
|
+
if (this.isSseSet) {
|
|
655
|
+
res.write(`data: ${data}
|
|
787
656
|
|
|
788
|
-
|
|
657
|
+
`);
|
|
658
|
+
return this;
|
|
659
|
+
}
|
|
660
|
+
const headersMap = new Map([
|
|
661
|
+
["Content-Type", "text/event-stream"],
|
|
662
|
+
["Cache-Control", "no-cache"],
|
|
663
|
+
["Connection", "keep-alive"]
|
|
664
|
+
]);
|
|
665
|
+
this.isSseSet = true;
|
|
666
|
+
let intervalId;
|
|
667
|
+
if (!this.hasSetHeader) {
|
|
668
|
+
this.hasSetHeader = true;
|
|
669
|
+
res.setHeaders(headersMap);
|
|
670
|
+
setInterval(() => {
|
|
671
|
+
res.write(`
|
|
672
|
+
`);
|
|
673
|
+
}, 3000);
|
|
674
|
+
req.on("close", () => {
|
|
675
|
+
clearInterval(intervalId);
|
|
676
|
+
res.end();
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
this.res.write(`data: ${data}
|
|
680
|
+
|
|
681
|
+
`);
|
|
682
|
+
return this;
|
|
683
|
+
}
|
|
684
|
+
close() {
|
|
685
|
+
if (this.req?.destroy) {
|
|
686
|
+
this.req.destroy();
|
|
687
|
+
}
|
|
688
|
+
return this;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
export {
|
|
692
|
+
SimpleRouter,
|
|
693
|
+
HttpChain
|
|
694
|
+
};
|