@esportsplus/routing 0.0.8 → 0.0.10
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.
Potentially problematic release.
This version of @esportsplus/routing might be problematic. Click here for more details.
- package/build/hash.d.ts +70 -0
- package/build/hash.js +75 -0
- package/build/index.d.ts +6 -92
- package/build/index.js +6 -8
- package/build/middleware/dispatch.d.ts +5 -0
- package/build/middleware/dispatch.js +7 -0
- package/build/middleware/factory.d.ts +3 -3
- package/build/middleware/factory.js +13 -13
- package/build/middleware/index.d.ts +24 -47
- package/build/middleware/index.js +5 -4
- package/build/middleware/match.d.ts +10 -0
- package/build/middleware/match.js +23 -0
- package/build/router/index.d.ts +36 -0
- package/build/router/index.js +182 -0
- package/build/router/node.d.ts +17 -0
- package/build/router/node.js +93 -0
- package/build/router/path.d.ts +14 -0
- package/build/router/path.js +21 -0
- package/build/slugify.d.ts +2 -0
- package/build/slugify.js +3 -0
- package/build/symbols.d.ts +4 -0
- package/build/symbols.js +4 -0
- package/build/types.d.ts +12 -26
- package/build/types.js +2 -1
- package/package.json +2 -3
- package/readme.md +1 -0
- package/src/hash.ts +106 -0
- package/src/index.ts +4 -6
- package/src/middleware/dispatch.ts +12 -0
- package/src/middleware/factory.ts +10 -9
- package/src/middleware/index.ts +4 -3
- package/src/middleware/match.ts +41 -0
- package/src/router/index.ts +237 -0
- package/src/router/node.ts +141 -0
- package/src/router/path.ts +29 -0
- package/src/slugify.ts +4 -0
- package/src/symbols.ts +8 -0
- package/src/types.ts +9 -26
- package/tsconfig.json +5 -19
- package/build/group.d.ts +0 -3
- package/build/group.js +0 -8
- package/build/listener.d.ts +0 -4
- package/build/listener.js +0 -13
- package/build/middleware/common/dispatch.d.ts +0 -3
- package/build/middleware/common/dispatch.js +0 -11
- package/build/middleware/common/index.d.ts +0 -29
- package/build/middleware/common/index.js +0 -3
- package/build/middleware/common/match.d.ts +0 -3
- package/build/middleware/common/match.js +0 -12
- package/build/middleware/factory-backup.d.ts +0 -3
- package/build/middleware/factory-backup.js +0 -11
- package/build/redirect.d.ts +0 -2
- package/build/redirect.js +0 -10
- package/build/routes.d.ts +0 -20
- package/build/routes.js +0 -45
- package/build/url.d.ts +0 -30
- package/build/url.js +0 -22
- package/src/group.ts +0 -11
- package/src/listener.ts +0 -24
- package/src/middleware/common/dispatch.ts +0 -17
- package/src/middleware/common/index.ts +0 -5
- package/src/middleware/common/match.ts +0 -24
- package/src/redirect.ts +0 -16
- package/src/routes.ts +0 -68
- package/src/url.ts +0 -30
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { PLACEHOLDER, STATIC, WILDCARD } from "../symbols";
|
|
2
|
+
class Node {
|
|
3
|
+
children = null;
|
|
4
|
+
parent = null;
|
|
5
|
+
path = null;
|
|
6
|
+
property = null;
|
|
7
|
+
route = null;
|
|
8
|
+
type = null;
|
|
9
|
+
constructor(parent = null) {
|
|
10
|
+
this.parent = parent;
|
|
11
|
+
}
|
|
12
|
+
add(path, route) {
|
|
13
|
+
let node = this, segments = path.split('/'), type = STATIC, unnamed = 0;
|
|
14
|
+
for (let i = 0, n = segments.length; i < n; i++) {
|
|
15
|
+
let child = node.children?.get(segments[i]);
|
|
16
|
+
if (!child) {
|
|
17
|
+
let segment = segments[i], symbol = segment[0];
|
|
18
|
+
if (!node.children) {
|
|
19
|
+
node.children = new Map();
|
|
20
|
+
}
|
|
21
|
+
node.children.set(segment, (child = new Node(node)));
|
|
22
|
+
if (symbol === ':') {
|
|
23
|
+
child.property = segment.slice(1) || `${unnamed++}`;
|
|
24
|
+
node.children.set(PLACEHOLDER, child);
|
|
25
|
+
type = null;
|
|
26
|
+
}
|
|
27
|
+
else if (symbol === '*') {
|
|
28
|
+
child.property = segment.slice(2) || `${unnamed++}`;
|
|
29
|
+
node.children.set(WILDCARD, child);
|
|
30
|
+
type = null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
node = child;
|
|
34
|
+
}
|
|
35
|
+
node.path = path;
|
|
36
|
+
node.route = route;
|
|
37
|
+
node.type = type;
|
|
38
|
+
return node;
|
|
39
|
+
}
|
|
40
|
+
find(path) {
|
|
41
|
+
let node = this, parameters = {}, segments = path.split('/'), wildcard = null;
|
|
42
|
+
for (let i = 0, n = segments.length; i < n; i++) {
|
|
43
|
+
let segment = segments[i], wc = node.children?.get(WILDCARD);
|
|
44
|
+
if (wc) {
|
|
45
|
+
wildcard = {
|
|
46
|
+
node: wc,
|
|
47
|
+
value: segments.slice(i).join('/')
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
let next = node.children?.get(segment);
|
|
51
|
+
if (next) {
|
|
52
|
+
node = next;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
node = node.children?.get(PLACEHOLDER);
|
|
56
|
+
if (!node) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
parameters[node.property] = segment;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if ((!node || !node.route) && wildcard) {
|
|
63
|
+
node = wildcard.node;
|
|
64
|
+
parameters[node.property] = wildcard.value;
|
|
65
|
+
}
|
|
66
|
+
if (!node) {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
parameters,
|
|
71
|
+
route: node.route
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
remove(path) {
|
|
75
|
+
let node = this, segments = path.split('/');
|
|
76
|
+
for (let i = 0, n = segments.length; i < n; i++) {
|
|
77
|
+
node = node.children?.get(segments[i]);
|
|
78
|
+
if (!node) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!node?.children?.size) {
|
|
83
|
+
let parent = node.parent;
|
|
84
|
+
if (parent && parent.children) {
|
|
85
|
+
parent.children.delete(segments[segments.length - 1]);
|
|
86
|
+
parent.children.delete(WILDCARD);
|
|
87
|
+
parent.children.delete(PLACEHOLDER);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return node;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export { Node };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const normalize: (path: string) => string;
|
|
2
|
+
declare const radixkey: (path: string, { method, subdomain }?: {
|
|
3
|
+
method?: string | null | undefined;
|
|
4
|
+
subdomain?: string | null | undefined;
|
|
5
|
+
}) => string;
|
|
6
|
+
declare const _default: {
|
|
7
|
+
normalize: (path: string) => string;
|
|
8
|
+
radixkey: (path: string, { method, subdomain }?: {
|
|
9
|
+
method?: string | null | undefined;
|
|
10
|
+
subdomain?: string | null | undefined;
|
|
11
|
+
}) => string;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
14
|
+
export { normalize, radixkey };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const normalize = (path) => {
|
|
2
|
+
if (path[0] !== '/') {
|
|
3
|
+
path = `/${path}`;
|
|
4
|
+
}
|
|
5
|
+
if (path.endsWith('/')) {
|
|
6
|
+
path = path.slice(0, -1);
|
|
7
|
+
}
|
|
8
|
+
return path || '/';
|
|
9
|
+
};
|
|
10
|
+
const radixkey = (path, { method, subdomain } = {}) => {
|
|
11
|
+
let prefix = '';
|
|
12
|
+
if (subdomain) {
|
|
13
|
+
prefix = subdomain + ' ';
|
|
14
|
+
}
|
|
15
|
+
if (method) {
|
|
16
|
+
prefix += method + ' ';
|
|
17
|
+
}
|
|
18
|
+
return prefix.toUpperCase() + normalize(path);
|
|
19
|
+
};
|
|
20
|
+
export default { normalize, radixkey };
|
|
21
|
+
export { normalize, radixkey };
|
package/build/slugify.js
ADDED
package/build/symbols.js
ADDED
package/build/types.d.ts
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
type Routes = {
|
|
14
|
-
add: typeof routes.add;
|
|
15
|
-
group: typeof routes.group;
|
|
16
|
-
routes: typeof routes.routes;
|
|
17
|
-
subdomains: typeof routes.subdomains;
|
|
18
|
-
};
|
|
19
|
-
type Route = {
|
|
20
|
-
middleware: Middleware[];
|
|
21
|
-
name: string;
|
|
22
|
-
path?: string;
|
|
23
|
-
responder: Responder;
|
|
24
|
-
subdomain?: string;
|
|
25
|
-
};
|
|
26
|
-
export { Group, Middleware, Next, Request, Responder, Route, Routes };
|
|
1
|
+
import { Route, Router } from './router';
|
|
2
|
+
type Middleware = <T>(request: T, next: Next) => unknown;
|
|
3
|
+
type Next = <T>(request: T) => unknown;
|
|
4
|
+
type Options = {
|
|
5
|
+
middleware?: Middleware[];
|
|
6
|
+
name?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
responder: Responder;
|
|
9
|
+
subdomain?: string;
|
|
10
|
+
};
|
|
11
|
+
type Responder = <T>(request: T) => Promise<unknown> | unknown;
|
|
12
|
+
export { Middleware, Next, Options, Responder, Route, Router };
|
package/build/types.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { Route, Router } from './router';
|
|
2
|
+
export { Route, Router };
|
package/package.json
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"description": "Routing",
|
|
4
4
|
"devDependencies": {
|
|
5
|
-
"
|
|
6
|
-
"typescript": "^4.9.4"
|
|
5
|
+
"@esportsplus/rspack": "^0.0.15"
|
|
7
6
|
},
|
|
8
7
|
"main": "./build/index.js",
|
|
9
8
|
"name": "@esportsplus/routing",
|
|
@@ -15,5 +14,5 @@
|
|
|
15
14
|
"prepublishOnly": "npm run build"
|
|
16
15
|
},
|
|
17
16
|
"types": "./build/index.d.ts",
|
|
18
|
-
"version": "0.0.
|
|
17
|
+
"version": "0.0.10"
|
|
19
18
|
}
|
package/readme.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
https://github.com/unjs/radix3
|
package/src/hash.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Router } from './types';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
let cache: {
|
|
5
|
+
factory: typeof request;
|
|
6
|
+
state: Record<PropertyKey, unknown>;
|
|
7
|
+
}[] = [],
|
|
8
|
+
registered = false;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
function update() {
|
|
12
|
+
for (let i = 0, n = cache.length; i < n; i++) {
|
|
13
|
+
let { factory, state } = cache[i],
|
|
14
|
+
values = factory();
|
|
15
|
+
|
|
16
|
+
for (let key in values) {
|
|
17
|
+
state[key] = values[key as keyof typeof values];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const back = () => window.history.back();
|
|
24
|
+
|
|
25
|
+
const forward = () => window.history.forward();
|
|
26
|
+
|
|
27
|
+
const listener = {
|
|
28
|
+
register: (factory: typeof cache[0]['factory'], state: typeof cache[0]['state']) => {
|
|
29
|
+
cache.push({ factory, state });
|
|
30
|
+
|
|
31
|
+
if (!registered) {
|
|
32
|
+
registered = true;
|
|
33
|
+
update();
|
|
34
|
+
|
|
35
|
+
window.addEventListener('popstate', update);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
listener.remove(state);
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
remove: (state: typeof cache[0]['state']) => {
|
|
43
|
+
for (let i = 0, n = cache.length; i < n; i++) {
|
|
44
|
+
if (cache[i].state !== state) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
cache[i] = cache[n - 1];
|
|
49
|
+
cache.pop();
|
|
50
|
+
|
|
51
|
+
if (cache.length === 0) {
|
|
52
|
+
window.removeEventListener('popstate', update);
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const factory = {
|
|
60
|
+
redirect: (router: Router) => {
|
|
61
|
+
return (path: string, { state, values }: { state?: Record<PropertyKey, unknown>; values?: unknown[] }) => {
|
|
62
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
63
|
+
return window.location.replace(path);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let uri = router.uri(path, values || []);
|
|
67
|
+
|
|
68
|
+
if (uri[0] === '/') {
|
|
69
|
+
uri = '#' + uri;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
window.history.pushState(state || {}, '', uri);
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
uri: (router: Router) => {
|
|
76
|
+
return (path: string, values: unknown[] = []) => {
|
|
77
|
+
let uri = router.uri(path, values || []);
|
|
78
|
+
|
|
79
|
+
if (uri[0] === '/') {
|
|
80
|
+
uri = '#' + uri;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return uri;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const request = (url: string = window?.location?.href || '') => {
|
|
89
|
+
let { hash, hostname, href, origin, port, protocol } = new URL( url ),
|
|
90
|
+
path = hash?.replace('#/', '/')?.split('?') || ['/', ''];
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
href,
|
|
94
|
+
hostname,
|
|
95
|
+
method: 'GET',
|
|
96
|
+
origin,
|
|
97
|
+
path: path[0],
|
|
98
|
+
port,
|
|
99
|
+
protocol,
|
|
100
|
+
query: Object.fromEntries( (new URLSearchParams(path[1])).entries() )
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
export default { back, factory, forward, listener, request };
|
|
106
|
+
export { back, factory, forward, listener, request };
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import hash from './hash';
|
|
1
2
|
import middleware from './middleware';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import routes from './routes';
|
|
5
|
-
import url from './url';
|
|
3
|
+
import router from './router';
|
|
4
|
+
import slugify from './slugify';
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
export
|
|
9
|
-
export { listener, middleware, redirect, routes, url };
|
|
7
|
+
export { hash, middleware, router, slugify };
|
|
10
8
|
export * from './types';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from '~/types';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export default (request: { data: ReturnType<Router['match']> }) => {
|
|
5
|
+
let { route } = request.data;
|
|
6
|
+
|
|
7
|
+
if (!route) {
|
|
8
|
+
throw new Error(`Routing: route dispatching failed, route is undefined!`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return route.dispatcher(request);
|
|
12
|
+
};
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { Middleware, Next
|
|
1
|
+
import { Middleware, Next } from '~/types';
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
function error() {
|
|
5
|
-
throw new Error('
|
|
5
|
+
throw new Error('Routing: request middleware did not return a responder');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// TODO: Use '@esportsplus/middleware'
|
|
10
|
+
export default (...middleware: Middleware[]) => {
|
|
11
|
+
let stack: Next[] = [];
|
|
11
12
|
|
|
12
|
-
for (let i = 0, n =
|
|
13
|
-
|
|
13
|
+
for (let i = 0, n = middleware.length; i < n; i++) {
|
|
14
|
+
stack[i] = <T>(request: T) => middleware[i](request, stack[i + 1] || error);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
if (!
|
|
17
|
-
throw new Error('
|
|
17
|
+
if (!stack.length) {
|
|
18
|
+
throw new Error('Routing: request middleware has not been defined');
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
return
|
|
21
|
+
return <T>(request: T) => stack[0](request);
|
|
21
22
|
};
|
package/src/middleware/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import dispatch from './dispatch';
|
|
2
2
|
import factory from './factory';
|
|
3
|
+
import match from './match';
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
export default {
|
|
6
|
-
export {
|
|
6
|
+
export default { dispatch, factory, match };
|
|
7
|
+
export { dispatch, factory, match };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Next, Router } from '~/types';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type Request = {
|
|
5
|
+
data: ReturnType<Router['match']>;
|
|
6
|
+
hostname: string;
|
|
7
|
+
method: string;
|
|
8
|
+
path: string;
|
|
9
|
+
subdomain?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export default (router: Router, spa = false) => {
|
|
14
|
+
let subdomain: string | null = null;
|
|
15
|
+
|
|
16
|
+
return (request: Request, next: Next) => {
|
|
17
|
+
if ((typeof request.subdomain !== 'string' && !spa) || subdomain === null) {
|
|
18
|
+
if (router.subdomains) {
|
|
19
|
+
for (let i = 0, n = router.subdomains.length; i < n; i++) {
|
|
20
|
+
if (!request.hostname.startsWith(router.subdomains[i])) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
subdomain = router.subdomains[i];
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (subdomain === null) {
|
|
30
|
+
subdomain = '';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let { parameters, route } = router.match(request.method, request.path, request.subdomain || subdomain);
|
|
35
|
+
|
|
36
|
+
request.data.parameters = parameters;
|
|
37
|
+
request.data.route = route;
|
|
38
|
+
|
|
39
|
+
return next(request);
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { STATIC } from "~/symbols";
|
|
2
|
+
import { Middleware, Responder, Options } from '~/types';
|
|
3
|
+
import { Node } from './node';
|
|
4
|
+
import { normalize, radixkey } from './path';
|
|
5
|
+
import factory from '~/middleware/factory';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
let { isArray } = Array;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
function set(route: Route, key: keyof Route, value?: any) {
|
|
12
|
+
if (!value) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!route[key]) {
|
|
17
|
+
(route[key] as any) = value;
|
|
18
|
+
}
|
|
19
|
+
else if (typeof value === 'string') {
|
|
20
|
+
if (typeof route[key] === 'string') {
|
|
21
|
+
(route[key] as string) += value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (isArray(value)) {
|
|
25
|
+
if (isArray(route[key])) {
|
|
26
|
+
(route[key] as any[]).push( ...value );
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Route {
|
|
33
|
+
dispatch: ReturnType<typeof factory> | null = null;
|
|
34
|
+
name: string | null = null;
|
|
35
|
+
path: string | null = null;
|
|
36
|
+
responder: Responder;
|
|
37
|
+
stack: Middleware[] | null = null;
|
|
38
|
+
subdomain: string | null = null;
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
constructor(responder: Responder) {
|
|
42
|
+
this.responder = responder;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
get dispatcher() {
|
|
47
|
+
if (this.dispatch === null) {
|
|
48
|
+
if (!this.stack?.length) {
|
|
49
|
+
this.dispatch = <T>(request: T) => this.responder(request);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.dispatch = factory(...this.stack, (request => this.responder(request)));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this.dispatch;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class Router {
|
|
61
|
+
groups: Omit<Options, 'responder'>[] = [];
|
|
62
|
+
root: Node;
|
|
63
|
+
routes: Record<string, Route> = {};
|
|
64
|
+
static: Record<string, Route> = {};
|
|
65
|
+
subdomains: string[] | null = null;
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
this.root = new Node();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
private add(radixkey: string, route: Route) {
|
|
74
|
+
let node = this.root.add(radixkey, route);
|
|
75
|
+
|
|
76
|
+
if (node.type === STATIC) {
|
|
77
|
+
if (this.static[radixkey]) {
|
|
78
|
+
throw new Error(`Routing: static path '${radixkey}' is already in use`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.static[radixkey] = route;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private route({ middleware, name, path, responder, subdomain }: Options) {
|
|
86
|
+
let route = new Route(responder);
|
|
87
|
+
|
|
88
|
+
for (let i = 0, n = this.groups.length; i < n; i++) {
|
|
89
|
+
let { middleware, name, path, subdomain } = this.groups[i];
|
|
90
|
+
|
|
91
|
+
set(route, 'name', name);
|
|
92
|
+
set(route, 'path', path);
|
|
93
|
+
set(route, 'stack', middleware);
|
|
94
|
+
set(route, 'subdomain', subdomain);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
set(route, 'name', name);
|
|
98
|
+
set(route, 'path', path);
|
|
99
|
+
set(route, 'stack', middleware);
|
|
100
|
+
set(route, 'subdomain', subdomain);
|
|
101
|
+
|
|
102
|
+
if (route.path) {
|
|
103
|
+
route.path = normalize(route.path);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (route.subdomain === 'www') {
|
|
107
|
+
route.subdomain = '';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return route;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
delete(options: Options) {
|
|
115
|
+
this.on(['DELETE'], options);
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get(options: Options) {
|
|
120
|
+
this.on(['GET'], options);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
group(options: Router['groups'][0]) {
|
|
125
|
+
return {
|
|
126
|
+
routes: (fn: (router: Router) => void) => {
|
|
127
|
+
this.groups.push(options);
|
|
128
|
+
fn(this);
|
|
129
|
+
this.groups.pop();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
match(method: string, path: string, subdomain?: string | null): ReturnType<Node['find']> {
|
|
135
|
+
let key = radixkey(path, { method, subdomain });
|
|
136
|
+
|
|
137
|
+
if (this.static[key]) {
|
|
138
|
+
return {
|
|
139
|
+
route: this.static[key]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return this.root.find(key);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
on(methods: string[], options: Options) {
|
|
147
|
+
let route = this.route(options);
|
|
148
|
+
|
|
149
|
+
if (route.name) {
|
|
150
|
+
if (this.routes[route.name]) {
|
|
151
|
+
throw new Error(`Routing: '${route.name}' is already in use`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.routes[route.name] = route;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (route.path) {
|
|
158
|
+
for (let i = 0, n = methods.length; i < n; i++) {
|
|
159
|
+
let key = radixkey(route.path, {
|
|
160
|
+
method: methods[i],
|
|
161
|
+
subdomain: route.subdomain
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (key.indexOf('?:') !== -1) {
|
|
165
|
+
let segments = key.split('?:'),
|
|
166
|
+
url = '';
|
|
167
|
+
|
|
168
|
+
for (let i = 0, n = segments.length; i < n; i++) {
|
|
169
|
+
this.add((url += (i > 0 ? '/:' : '/') + segments[i]), route);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.add(key, route);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (route.subdomain) {
|
|
179
|
+
if (!this.subdomains) {
|
|
180
|
+
this.subdomains = [route.subdomain];
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
this.subdomains.push(route.subdomain);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
post(options: Options) {
|
|
189
|
+
this.on(['POST'], options);
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
put(options: Options) {
|
|
194
|
+
this.on(['PUT'], options);
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
uri(name: string, values: unknown[] = []) {
|
|
199
|
+
let path = this.routes?.[name]?.path;
|
|
200
|
+
|
|
201
|
+
if (!path) {
|
|
202
|
+
throw new Error(`Routing: route name '${name}' does not exist or it does not provide a path`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let resolved = [] as typeof values,
|
|
206
|
+
segments = path.split('/');
|
|
207
|
+
|
|
208
|
+
for (let i = 0, n = segments.length; i < n; i++) {
|
|
209
|
+
let segment = segments[i],
|
|
210
|
+
symbol = segment[0];
|
|
211
|
+
|
|
212
|
+
if (symbol === ':') {
|
|
213
|
+
resolved.push(values[i]);
|
|
214
|
+
}
|
|
215
|
+
else if (symbol === '?') {
|
|
216
|
+
if (values[i] === undefined) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
resolved.push(values[i]);
|
|
221
|
+
}
|
|
222
|
+
else if (symbol === '*') {
|
|
223
|
+
resolved.push( ...values.slice(i) );
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
resolved.push(segment);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return resolved.join('/');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
export default (...args: ConstructorParameters<typeof Router>) => new Router(...args);
|
|
237
|
+
export { Router, Route };
|