@kevisual/router 0.0.4-alpha-7 → 0.0.5-alpha-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/dist/router-browser.d.ts +2 -0
- package/dist/router-browser.js +6 -1
- package/dist/router-sign.d.ts +15 -0
- package/dist/router-sign.js +28654 -0
- package/dist/router-simple.d.ts +25 -0
- package/dist/router-simple.js +463 -0
- package/dist/router.d.ts +12 -0
- package/dist/router.js +40 -4
- package/package.json +12 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Key } from 'path-to-regexp';
|
|
2
|
+
import { ServerResponse, IncomingMessage } from 'http';
|
|
3
|
+
|
|
4
|
+
type Req = IncomingMessage & {
|
|
5
|
+
params?: Record<string, string>;
|
|
6
|
+
};
|
|
7
|
+
interface Route {
|
|
8
|
+
method: string;
|
|
9
|
+
regexp: RegExp;
|
|
10
|
+
keys: Key[];
|
|
11
|
+
handlers: Array<(req: Req, res: ServerResponse) => Promise<void> | void>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* SimpleRouter
|
|
15
|
+
*/
|
|
16
|
+
declare class SimpleRouter {
|
|
17
|
+
routes: Route[];
|
|
18
|
+
constructor();
|
|
19
|
+
use(method: string, route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
|
|
20
|
+
get(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
|
|
21
|
+
post(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>): this;
|
|
22
|
+
parse(req: Req, res: ServerResponse): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { SimpleRouter };
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
var dist = {};
|
|
2
|
+
|
|
3
|
+
var hasRequiredDist;
|
|
4
|
+
|
|
5
|
+
function requireDist () {
|
|
6
|
+
if (hasRequiredDist) return dist;
|
|
7
|
+
hasRequiredDist = 1;
|
|
8
|
+
Object.defineProperty(dist, "__esModule", { value: true });
|
|
9
|
+
dist.TokenData = void 0;
|
|
10
|
+
dist.parse = parse;
|
|
11
|
+
dist.compile = compile;
|
|
12
|
+
dist.match = match;
|
|
13
|
+
dist.pathToRegexp = pathToRegexp;
|
|
14
|
+
dist.stringify = stringify;
|
|
15
|
+
const DEFAULT_DELIMITER = "/";
|
|
16
|
+
const NOOP_VALUE = (value) => value;
|
|
17
|
+
const ID_START = /^[$_\p{ID_Start}]$/u;
|
|
18
|
+
const ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u;
|
|
19
|
+
const DEBUG_URL = "https://git.new/pathToRegexpError";
|
|
20
|
+
const SIMPLE_TOKENS = {
|
|
21
|
+
// Groups.
|
|
22
|
+
"{": "{",
|
|
23
|
+
"}": "}",
|
|
24
|
+
// Reserved.
|
|
25
|
+
"(": "(",
|
|
26
|
+
")": ")",
|
|
27
|
+
"[": "[",
|
|
28
|
+
"]": "]",
|
|
29
|
+
"+": "+",
|
|
30
|
+
"?": "?",
|
|
31
|
+
"!": "!",
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Escape text for stringify to path.
|
|
35
|
+
*/
|
|
36
|
+
function escapeText(str) {
|
|
37
|
+
return str.replace(/[{}()\[\]+?!:*]/g, "\\$&");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Escape a regular expression string.
|
|
41
|
+
*/
|
|
42
|
+
function escape(str) {
|
|
43
|
+
return str.replace(/[.+*?^${}()[\]|/\\]/g, "\\$&");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Tokenize input string.
|
|
47
|
+
*/
|
|
48
|
+
function* lexer(str) {
|
|
49
|
+
const chars = [...str];
|
|
50
|
+
let i = 0;
|
|
51
|
+
function name() {
|
|
52
|
+
let value = "";
|
|
53
|
+
if (ID_START.test(chars[++i])) {
|
|
54
|
+
value += chars[i];
|
|
55
|
+
while (ID_CONTINUE.test(chars[++i])) {
|
|
56
|
+
value += chars[i];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (chars[i] === '"') {
|
|
60
|
+
let pos = i;
|
|
61
|
+
while (i < chars.length) {
|
|
62
|
+
if (chars[++i] === '"') {
|
|
63
|
+
i++;
|
|
64
|
+
pos = 0;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (chars[i] === "\\") {
|
|
68
|
+
value += chars[++i];
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
value += chars[i];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (pos) {
|
|
75
|
+
throw new TypeError(`Unterminated quote at ${pos}: ${DEBUG_URL}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!value) {
|
|
79
|
+
throw new TypeError(`Missing parameter name at ${i}: ${DEBUG_URL}`);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
while (i < chars.length) {
|
|
84
|
+
const value = chars[i];
|
|
85
|
+
const type = SIMPLE_TOKENS[value];
|
|
86
|
+
if (type) {
|
|
87
|
+
yield { type, index: i++, value };
|
|
88
|
+
}
|
|
89
|
+
else if (value === "\\") {
|
|
90
|
+
yield { type: "ESCAPED", index: i++, value: chars[i++] };
|
|
91
|
+
}
|
|
92
|
+
else if (value === ":") {
|
|
93
|
+
const value = name();
|
|
94
|
+
yield { type: "PARAM", index: i, value };
|
|
95
|
+
}
|
|
96
|
+
else if (value === "*") {
|
|
97
|
+
const value = name();
|
|
98
|
+
yield { type: "WILDCARD", index: i, value };
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
yield { type: "CHAR", index: i, value: chars[i++] };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return { type: "END", index: i, value: "" };
|
|
105
|
+
}
|
|
106
|
+
class Iter {
|
|
107
|
+
constructor(tokens) {
|
|
108
|
+
this.tokens = tokens;
|
|
109
|
+
}
|
|
110
|
+
peek() {
|
|
111
|
+
if (!this._peek) {
|
|
112
|
+
const next = this.tokens.next();
|
|
113
|
+
this._peek = next.value;
|
|
114
|
+
}
|
|
115
|
+
return this._peek;
|
|
116
|
+
}
|
|
117
|
+
tryConsume(type) {
|
|
118
|
+
const token = this.peek();
|
|
119
|
+
if (token.type !== type)
|
|
120
|
+
return;
|
|
121
|
+
this._peek = undefined; // Reset after consumed.
|
|
122
|
+
return token.value;
|
|
123
|
+
}
|
|
124
|
+
consume(type) {
|
|
125
|
+
const value = this.tryConsume(type);
|
|
126
|
+
if (value !== undefined)
|
|
127
|
+
return value;
|
|
128
|
+
const { type: nextType, index } = this.peek();
|
|
129
|
+
throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}: ${DEBUG_URL}`);
|
|
130
|
+
}
|
|
131
|
+
text() {
|
|
132
|
+
let result = "";
|
|
133
|
+
let value;
|
|
134
|
+
while ((value = this.tryConsume("CHAR") || this.tryConsume("ESCAPED"))) {
|
|
135
|
+
result += value;
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Tokenized path instance.
|
|
142
|
+
*/
|
|
143
|
+
class TokenData {
|
|
144
|
+
constructor(tokens) {
|
|
145
|
+
this.tokens = tokens;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
dist.TokenData = TokenData;
|
|
149
|
+
/**
|
|
150
|
+
* Parse a string for the raw tokens.
|
|
151
|
+
*/
|
|
152
|
+
function parse(str, options = {}) {
|
|
153
|
+
const { encodePath = NOOP_VALUE } = options;
|
|
154
|
+
const it = new Iter(lexer(str));
|
|
155
|
+
function consume(endType) {
|
|
156
|
+
const tokens = [];
|
|
157
|
+
while (true) {
|
|
158
|
+
const path = it.text();
|
|
159
|
+
if (path)
|
|
160
|
+
tokens.push({ type: "text", value: encodePath(path) });
|
|
161
|
+
const param = it.tryConsume("PARAM");
|
|
162
|
+
if (param) {
|
|
163
|
+
tokens.push({
|
|
164
|
+
type: "param",
|
|
165
|
+
name: param,
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const wildcard = it.tryConsume("WILDCARD");
|
|
170
|
+
if (wildcard) {
|
|
171
|
+
tokens.push({
|
|
172
|
+
type: "wildcard",
|
|
173
|
+
name: wildcard,
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const open = it.tryConsume("{");
|
|
178
|
+
if (open) {
|
|
179
|
+
tokens.push({
|
|
180
|
+
type: "group",
|
|
181
|
+
tokens: consume("}"),
|
|
182
|
+
});
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
it.consume(endType);
|
|
186
|
+
return tokens;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const tokens = consume("END");
|
|
190
|
+
return new TokenData(tokens);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Compile a string to a template function for the path.
|
|
194
|
+
*/
|
|
195
|
+
function compile(path, options = {}) {
|
|
196
|
+
const { encode = encodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
|
|
197
|
+
const data = path instanceof TokenData ? path : parse(path, options);
|
|
198
|
+
const fn = tokensToFunction(data.tokens, delimiter, encode);
|
|
199
|
+
return function path(data = {}) {
|
|
200
|
+
const [path, ...missing] = fn(data);
|
|
201
|
+
if (missing.length) {
|
|
202
|
+
throw new TypeError(`Missing parameters: ${missing.join(", ")}`);
|
|
203
|
+
}
|
|
204
|
+
return path;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function tokensToFunction(tokens, delimiter, encode) {
|
|
208
|
+
const encoders = tokens.map((token) => tokenToFunction(token, delimiter, encode));
|
|
209
|
+
return (data) => {
|
|
210
|
+
const result = [""];
|
|
211
|
+
for (const encoder of encoders) {
|
|
212
|
+
const [value, ...extras] = encoder(data);
|
|
213
|
+
result[0] += value;
|
|
214
|
+
result.push(...extras);
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Convert a single token into a path building function.
|
|
221
|
+
*/
|
|
222
|
+
function tokenToFunction(token, delimiter, encode) {
|
|
223
|
+
if (token.type === "text")
|
|
224
|
+
return () => [token.value];
|
|
225
|
+
if (token.type === "group") {
|
|
226
|
+
const fn = tokensToFunction(token.tokens, delimiter, encode);
|
|
227
|
+
return (data) => {
|
|
228
|
+
const [value, ...missing] = fn(data);
|
|
229
|
+
if (!missing.length)
|
|
230
|
+
return [value];
|
|
231
|
+
return [""];
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const encodeValue = encode || NOOP_VALUE;
|
|
235
|
+
if (token.type === "wildcard" && encode !== false) {
|
|
236
|
+
return (data) => {
|
|
237
|
+
const value = data[token.name];
|
|
238
|
+
if (value == null)
|
|
239
|
+
return ["", token.name];
|
|
240
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
241
|
+
throw new TypeError(`Expected "${token.name}" to be a non-empty array`);
|
|
242
|
+
}
|
|
243
|
+
return [
|
|
244
|
+
value
|
|
245
|
+
.map((value, index) => {
|
|
246
|
+
if (typeof value !== "string") {
|
|
247
|
+
throw new TypeError(`Expected "${token.name}/${index}" to be a string`);
|
|
248
|
+
}
|
|
249
|
+
return encodeValue(value);
|
|
250
|
+
})
|
|
251
|
+
.join(delimiter),
|
|
252
|
+
];
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return (data) => {
|
|
256
|
+
const value = data[token.name];
|
|
257
|
+
if (value == null)
|
|
258
|
+
return ["", token.name];
|
|
259
|
+
if (typeof value !== "string") {
|
|
260
|
+
throw new TypeError(`Expected "${token.name}" to be a string`);
|
|
261
|
+
}
|
|
262
|
+
return [encodeValue(value)];
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Transform a path into a match function.
|
|
267
|
+
*/
|
|
268
|
+
function match(path, options = {}) {
|
|
269
|
+
const { decode = decodeURIComponent, delimiter = DEFAULT_DELIMITER } = options;
|
|
270
|
+
const { regexp, keys } = pathToRegexp(path, options);
|
|
271
|
+
const decoders = keys.map((key) => {
|
|
272
|
+
if (decode === false)
|
|
273
|
+
return NOOP_VALUE;
|
|
274
|
+
if (key.type === "param")
|
|
275
|
+
return decode;
|
|
276
|
+
return (value) => value.split(delimiter).map(decode);
|
|
277
|
+
});
|
|
278
|
+
return function match(input) {
|
|
279
|
+
const m = regexp.exec(input);
|
|
280
|
+
if (!m)
|
|
281
|
+
return false;
|
|
282
|
+
const path = m[0];
|
|
283
|
+
const params = Object.create(null);
|
|
284
|
+
for (let i = 1; i < m.length; i++) {
|
|
285
|
+
if (m[i] === undefined)
|
|
286
|
+
continue;
|
|
287
|
+
const key = keys[i - 1];
|
|
288
|
+
const decoder = decoders[i - 1];
|
|
289
|
+
params[key.name] = decoder(m[i]);
|
|
290
|
+
}
|
|
291
|
+
return { path, params };
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function pathToRegexp(path, options = {}) {
|
|
295
|
+
const { delimiter = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true, } = options;
|
|
296
|
+
const keys = [];
|
|
297
|
+
const sources = [];
|
|
298
|
+
const flags = sensitive ? "" : "i";
|
|
299
|
+
const paths = Array.isArray(path) ? path : [path];
|
|
300
|
+
const items = paths.map((path) => path instanceof TokenData ? path : parse(path, options));
|
|
301
|
+
for (const { tokens } of items) {
|
|
302
|
+
for (const seq of flatten(tokens, 0, [])) {
|
|
303
|
+
const regexp = sequenceToRegExp(seq, delimiter, keys);
|
|
304
|
+
sources.push(regexp);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
let pattern = `^(?:${sources.join("|")})`;
|
|
308
|
+
if (trailing)
|
|
309
|
+
pattern += `(?:${escape(delimiter)}$)?`;
|
|
310
|
+
pattern += end ? "$" : `(?=${escape(delimiter)}|$)`;
|
|
311
|
+
const regexp = new RegExp(pattern, flags);
|
|
312
|
+
return { regexp, keys };
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Generate a flat list of sequence tokens from the given tokens.
|
|
316
|
+
*/
|
|
317
|
+
function* flatten(tokens, index, init) {
|
|
318
|
+
if (index === tokens.length) {
|
|
319
|
+
return yield init;
|
|
320
|
+
}
|
|
321
|
+
const token = tokens[index];
|
|
322
|
+
if (token.type === "group") {
|
|
323
|
+
const fork = init.slice();
|
|
324
|
+
for (const seq of flatten(token.tokens, 0, fork)) {
|
|
325
|
+
yield* flatten(tokens, index + 1, seq);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
init.push(token);
|
|
330
|
+
}
|
|
331
|
+
yield* flatten(tokens, index + 1, init);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Transform a flat sequence of tokens into a regular expression.
|
|
335
|
+
*/
|
|
336
|
+
function sequenceToRegExp(tokens, delimiter, keys) {
|
|
337
|
+
let result = "";
|
|
338
|
+
let backtrack = "";
|
|
339
|
+
let isSafeSegmentParam = true;
|
|
340
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
341
|
+
const token = tokens[i];
|
|
342
|
+
if (token.type === "text") {
|
|
343
|
+
result += escape(token.value);
|
|
344
|
+
backtrack += token.value;
|
|
345
|
+
isSafeSegmentParam || (isSafeSegmentParam = token.value.includes(delimiter));
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (token.type === "param" || token.type === "wildcard") {
|
|
349
|
+
if (!isSafeSegmentParam && !backtrack) {
|
|
350
|
+
throw new TypeError(`Missing text after "${token.name}": ${DEBUG_URL}`);
|
|
351
|
+
}
|
|
352
|
+
if (token.type === "param") {
|
|
353
|
+
result += `(${negate(delimiter, isSafeSegmentParam ? "" : backtrack)}+)`;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
result += `([\\s\\S]+)`;
|
|
357
|
+
}
|
|
358
|
+
keys.push(token);
|
|
359
|
+
backtrack = "";
|
|
360
|
+
isSafeSegmentParam = false;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
function negate(delimiter, backtrack) {
|
|
367
|
+
if (backtrack.length < 2) {
|
|
368
|
+
if (delimiter.length < 2)
|
|
369
|
+
return `[^${escape(delimiter + backtrack)}]`;
|
|
370
|
+
return `(?:(?!${escape(delimiter)})[^${escape(backtrack)}])`;
|
|
371
|
+
}
|
|
372
|
+
if (delimiter.length < 2) {
|
|
373
|
+
return `(?:(?!${escape(backtrack)})[^${escape(delimiter)}])`;
|
|
374
|
+
}
|
|
375
|
+
return `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\\s\\S])`;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Stringify token data into a path string.
|
|
379
|
+
*/
|
|
380
|
+
function stringify(data) {
|
|
381
|
+
return data.tokens
|
|
382
|
+
.map(function stringifyToken(token, index, tokens) {
|
|
383
|
+
if (token.type === "text")
|
|
384
|
+
return escapeText(token.value);
|
|
385
|
+
if (token.type === "group") {
|
|
386
|
+
return `{${token.tokens.map(stringifyToken).join("")}}`;
|
|
387
|
+
}
|
|
388
|
+
const isSafe = isNameSafe(token.name) && isNextNameSafe(tokens[index + 1]);
|
|
389
|
+
const key = isSafe ? token.name : JSON.stringify(token.name);
|
|
390
|
+
if (token.type === "param")
|
|
391
|
+
return `:${key}`;
|
|
392
|
+
if (token.type === "wildcard")
|
|
393
|
+
return `*${key}`;
|
|
394
|
+
throw new TypeError(`Unexpected token: ${token}`);
|
|
395
|
+
})
|
|
396
|
+
.join("");
|
|
397
|
+
}
|
|
398
|
+
function isNameSafe(name) {
|
|
399
|
+
const [first, ...rest] = name;
|
|
400
|
+
if (!ID_START.test(first))
|
|
401
|
+
return false;
|
|
402
|
+
return rest.every((char) => ID_CONTINUE.test(char));
|
|
403
|
+
}
|
|
404
|
+
function isNextNameSafe(token) {
|
|
405
|
+
if ((token === null || token === void 0 ? void 0 : token.type) !== "text")
|
|
406
|
+
return true;
|
|
407
|
+
return !ID_CONTINUE.test(token.value[0]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return dist;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
var distExports = requireDist();
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* SimpleRouter
|
|
417
|
+
*/
|
|
418
|
+
class SimpleRouter {
|
|
419
|
+
routes = [];
|
|
420
|
+
constructor() {
|
|
421
|
+
// console.log('AppSimple initialized');
|
|
422
|
+
}
|
|
423
|
+
use(method, route, ...fns) {
|
|
424
|
+
const handlers = Array.isArray(fns) ? fns.flat() : [];
|
|
425
|
+
const pattern = distExports.pathToRegexp(route);
|
|
426
|
+
this.routes.push({ method: method.toLowerCase(), regexp: pattern.regexp, keys: pattern.keys, handlers });
|
|
427
|
+
return this;
|
|
428
|
+
}
|
|
429
|
+
get(route, ...fns) {
|
|
430
|
+
return this.use('get', route, ...fns);
|
|
431
|
+
}
|
|
432
|
+
post(route, ...fns) {
|
|
433
|
+
return this.use('post', route, ...fns);
|
|
434
|
+
}
|
|
435
|
+
parse(req, res) {
|
|
436
|
+
const { pathname } = new URL(req.url, 'http://localhost');
|
|
437
|
+
const method = req.method.toLowerCase();
|
|
438
|
+
const route = this.routes.find((route) => {
|
|
439
|
+
const matchResult = route.regexp.exec(pathname);
|
|
440
|
+
if (matchResult && route.method === method) {
|
|
441
|
+
const params = {};
|
|
442
|
+
route.keys.forEach((key, i) => {
|
|
443
|
+
params[key.name] = matchResult[i + 1];
|
|
444
|
+
});
|
|
445
|
+
req.params = params;
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
if (route) {
|
|
450
|
+
const { handlers } = route;
|
|
451
|
+
return handlers.reduce((promiseChain, handler) => promiseChain.then(() => Promise.resolve(handler(req, res))), Promise.resolve());
|
|
452
|
+
}
|
|
453
|
+
if (!res?.headersSent && res && res?.end) {
|
|
454
|
+
res.statusCode = 404;
|
|
455
|
+
res.end('Not Found');
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
console.error('Not Found');
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export { SimpleRouter };
|
package/dist/router.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Schema } from 'zod';
|
|
2
2
|
export { Schema } from 'zod';
|
|
3
3
|
import http, { IncomingMessage, ServerResponse } from 'http';
|
|
4
|
+
import https from 'https';
|
|
4
5
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
5
6
|
|
|
6
7
|
type BaseRule = {
|
|
@@ -190,6 +191,7 @@ declare class Route {
|
|
|
190
191
|
declare class QueryRouter {
|
|
191
192
|
routes: Route[];
|
|
192
193
|
maxNextRoute: number;
|
|
194
|
+
context?: RouteContext;
|
|
193
195
|
constructor();
|
|
194
196
|
add(route: Route): void;
|
|
195
197
|
/**
|
|
@@ -233,6 +235,7 @@ declare class QueryRouter {
|
|
|
233
235
|
}, ctx?: RouteContext & {
|
|
234
236
|
[key: string]: any;
|
|
235
237
|
}): Promise<any>;
|
|
238
|
+
setContext(ctx: RouteContext): Promise<void>;
|
|
236
239
|
getList(): RouteInfo[];
|
|
237
240
|
getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn<T>, ctx?: RouteContext): (msg: {
|
|
238
241
|
path: string;
|
|
@@ -345,6 +348,9 @@ type ServerOpts = {
|
|
|
345
348
|
[key: string]: any;
|
|
346
349
|
}) => any;
|
|
347
350
|
cors?: Cors;
|
|
351
|
+
isHTTPS?: boolean;
|
|
352
|
+
httpsKey?: string;
|
|
353
|
+
httpsCert?: string;
|
|
348
354
|
};
|
|
349
355
|
declare class Server {
|
|
350
356
|
path: string;
|
|
@@ -353,6 +359,8 @@ declare class Server {
|
|
|
353
359
|
private _callback;
|
|
354
360
|
private cors;
|
|
355
361
|
private hasOn;
|
|
362
|
+
private isHTTPS;
|
|
363
|
+
private options;
|
|
356
364
|
constructor(opts?: ServerOpts);
|
|
357
365
|
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): void;
|
|
358
366
|
listen(port: number, hostname?: string, listeningListener?: () => void): void;
|
|
@@ -362,6 +370,7 @@ declare class Server {
|
|
|
362
370
|
listen(path: string, listeningListener?: () => void): void;
|
|
363
371
|
listen(handle: any, backlog?: number, listeningListener?: () => void): void;
|
|
364
372
|
listen(handle: any, listeningListener?: () => void): void;
|
|
373
|
+
createServer(): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse> | https.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
365
374
|
setHandle(handle?: any): void;
|
|
366
375
|
/**
|
|
367
376
|
* get callback
|
|
@@ -462,6 +471,9 @@ type AppOptions<T = {}> = {
|
|
|
462
471
|
path?: string;
|
|
463
472
|
cors?: Cors;
|
|
464
473
|
handle?: any;
|
|
474
|
+
isHTTPS?: boolean;
|
|
475
|
+
httpsKey?: string;
|
|
476
|
+
httpsCert?: string;
|
|
465
477
|
};
|
|
466
478
|
io?: boolean;
|
|
467
479
|
ioOpts?: {
|
package/dist/router.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { webcrypto } from 'node:crypto';
|
|
2
2
|
import http from 'http';
|
|
3
|
+
import https from 'https';
|
|
3
4
|
import url from 'url';
|
|
4
5
|
import { WebSocketServer } from 'ws';
|
|
5
6
|
|
|
@@ -5659,6 +5660,7 @@ class Route {
|
|
|
5659
5660
|
class QueryRouter {
|
|
5660
5661
|
routes;
|
|
5661
5662
|
maxNextRoute = 40;
|
|
5663
|
+
context = {}; // default context for call
|
|
5662
5664
|
constructor() {
|
|
5663
5665
|
this.routes = [];
|
|
5664
5666
|
}
|
|
@@ -5885,7 +5887,10 @@ class QueryRouter {
|
|
|
5885
5887
|
return await this.runRoute(path, key, ctx);
|
|
5886
5888
|
}
|
|
5887
5889
|
async call(message, ctx) {
|
|
5888
|
-
return await this.parse(message, ctx);
|
|
5890
|
+
return await this.parse(message, { ...this.context, ...ctx });
|
|
5891
|
+
}
|
|
5892
|
+
async setContext(ctx) {
|
|
5893
|
+
this.context = ctx;
|
|
5889
5894
|
}
|
|
5890
5895
|
getList() {
|
|
5891
5896
|
return this.routes.map((r) => {
|
|
@@ -5930,6 +5935,7 @@ class QueryRouterServer extends QueryRouter {
|
|
|
5930
5935
|
constructor(opts) {
|
|
5931
5936
|
super();
|
|
5932
5937
|
this.handle = this.getHandle(this, opts?.handleFn, opts?.context);
|
|
5938
|
+
this.setContext(opts?.context);
|
|
5933
5939
|
}
|
|
5934
5940
|
setHandle(wrapperFn, ctx) {
|
|
5935
5941
|
this.handle = this.getHandle(this, wrapperFn, ctx);
|
|
@@ -6129,17 +6135,46 @@ class Server {
|
|
|
6129
6135
|
_callback;
|
|
6130
6136
|
cors;
|
|
6131
6137
|
hasOn = false;
|
|
6138
|
+
isHTTPS = false;
|
|
6139
|
+
options = {
|
|
6140
|
+
key: '',
|
|
6141
|
+
cert: '',
|
|
6142
|
+
};
|
|
6132
6143
|
constructor(opts) {
|
|
6133
6144
|
this.path = opts?.path || '/api/router';
|
|
6134
6145
|
this.handle = opts?.handle;
|
|
6135
6146
|
this.cors = opts?.cors;
|
|
6147
|
+
this.isHTTPS = opts?.isHTTPS || false;
|
|
6148
|
+
this.options = {
|
|
6149
|
+
key: opts?.httpsKey || '',
|
|
6150
|
+
cert: opts?.httpsCert || '',
|
|
6151
|
+
};
|
|
6136
6152
|
}
|
|
6137
6153
|
listen(...args) {
|
|
6138
|
-
this._server =
|
|
6154
|
+
this._server = this.createServer();
|
|
6139
6155
|
const callback = this.createCallback();
|
|
6140
6156
|
this._server.on('request', callback);
|
|
6141
6157
|
this._server.listen(...args);
|
|
6142
6158
|
}
|
|
6159
|
+
createServer() {
|
|
6160
|
+
let server;
|
|
6161
|
+
if (this.isHTTPS) {
|
|
6162
|
+
if (this.options.key && this.options.cert) {
|
|
6163
|
+
server = https.createServer({
|
|
6164
|
+
key: this.options.key,
|
|
6165
|
+
cert: this.options.cert,
|
|
6166
|
+
});
|
|
6167
|
+
console.log('https server');
|
|
6168
|
+
return server;
|
|
6169
|
+
}
|
|
6170
|
+
else {
|
|
6171
|
+
console.error('https key and cert is required');
|
|
6172
|
+
console.log('downgrade to http');
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
server = http.createServer();
|
|
6176
|
+
return server;
|
|
6177
|
+
}
|
|
6143
6178
|
setHandle(handle) {
|
|
6144
6179
|
this.handle = handle;
|
|
6145
6180
|
}
|
|
@@ -6221,7 +6256,7 @@ class Server {
|
|
|
6221
6256
|
* @param listener
|
|
6222
6257
|
*/
|
|
6223
6258
|
on(listener) {
|
|
6224
|
-
this._server = this._server ||
|
|
6259
|
+
this._server = this._server || this.createServer();
|
|
6225
6260
|
this._server.removeAllListeners('request');
|
|
6226
6261
|
this.hasOn = true;
|
|
6227
6262
|
if (Array.isArray(listener)) {
|
|
@@ -6402,6 +6437,7 @@ class App {
|
|
|
6402
6437
|
const router = opts?.router || new QueryRouter();
|
|
6403
6438
|
const server = opts?.server || new Server(opts?.serverOptions || {});
|
|
6404
6439
|
server.setHandle(router.getHandle(router, opts?.routerHandle, opts?.routerContext));
|
|
6440
|
+
router.setContext(opts?.routerContext);
|
|
6405
6441
|
this.router = router;
|
|
6406
6442
|
this.server = server;
|
|
6407
6443
|
if (opts?.io) {
|
|
@@ -6443,7 +6479,7 @@ class App {
|
|
|
6443
6479
|
}
|
|
6444
6480
|
async call(message, ctx) {
|
|
6445
6481
|
const router = this.router;
|
|
6446
|
-
return await router.
|
|
6482
|
+
return await router.call(message, ctx);
|
|
6447
6483
|
}
|
|
6448
6484
|
exportRoutes() {
|
|
6449
6485
|
return this.router.exportRoutes();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@kevisual/router",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5-alpha-0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"url": "git+https://github.com/abearxiong/kevisual-router.git"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
+
"path-to-regexp": "^8.2.0",
|
|
45
|
+
"selfsigned": "^2.4.1",
|
|
44
46
|
"ws": "^8.18.0"
|
|
45
47
|
},
|
|
46
48
|
"publishConfig": {
|
|
@@ -54,6 +56,14 @@
|
|
|
54
56
|
"./browser": {
|
|
55
57
|
"import": "./dist/router-browser.js",
|
|
56
58
|
"require": "./dist/router-browser.js"
|
|
59
|
+
},
|
|
60
|
+
"./sign": {
|
|
61
|
+
"import": "./dist/router-sign.js",
|
|
62
|
+
"require": "./dist/router-sign.js"
|
|
63
|
+
},
|
|
64
|
+
"./simple": {
|
|
65
|
+
"import": "./dist/router-simple.js",
|
|
66
|
+
"require": "./dist/router-simple.js"
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
|
-
}
|
|
69
|
+
}
|