@esportsplus/routing 0.1.32 → 0.2.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/build/browser.js +2 -2
- package/build/constants.d.ts +2 -2
- package/build/constants.js +2 -2
- package/build/index.d.ts +0 -1
- package/build/index.js +0 -1
- package/build/router/index.d.ts +5 -3
- package/build/router/index.js +41 -41
- package/build/router/node.d.ts +4 -3
- package/build/router/node.js +40 -53
- package/package.json +5 -3
- package/src/browser.ts +3 -2
- package/src/constants.ts +2 -2
- package/src/index.ts +0 -1
- package/src/router/index.ts +52 -47
- package/src/router/node.ts +51 -70
- package/src/types.ts +6 -1
- package/build/slugify.d.ts +0 -2
- package/build/slugify.js +0 -3
- package/src/slugify.ts +0 -4
package/build/browser.js
CHANGED
|
@@ -29,9 +29,9 @@ function href() {
|
|
|
29
29
|
}
|
|
30
30
|
function match(request, router, subdomain) {
|
|
31
31
|
if (router.subdomains !== null) {
|
|
32
|
-
let subdomains = router.subdomains;
|
|
32
|
+
let hostname = request.hostname, subdomains = router.subdomains;
|
|
33
33
|
for (let i = 0, n = subdomains.length; i < n; i++) {
|
|
34
|
-
if (!
|
|
34
|
+
if (!hostname.startsWith(subdomains[i])) {
|
|
35
35
|
continue;
|
|
36
36
|
}
|
|
37
37
|
subdomain = subdomains[i];
|
package/build/constants.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ declare const ON_DELETE: string[];
|
|
|
2
2
|
declare const ON_GET: string[];
|
|
3
3
|
declare const ON_POST: string[];
|
|
4
4
|
declare const ON_PUT: string[];
|
|
5
|
-
declare const
|
|
5
|
+
declare const PARAMETER = 0;
|
|
6
6
|
declare const STATIC = 1;
|
|
7
7
|
declare const WILDCARD = 2;
|
|
8
|
-
export { ON_DELETE, ON_GET, ON_POST, ON_PUT,
|
|
8
|
+
export { ON_DELETE, ON_GET, ON_POST, ON_PUT, PARAMETER, STATIC, WILDCARD };
|
package/build/constants.js
CHANGED
|
@@ -2,7 +2,7 @@ const ON_DELETE = ['DELETE'];
|
|
|
2
2
|
const ON_GET = ['GET'];
|
|
3
3
|
const ON_POST = ['POST'];
|
|
4
4
|
const ON_PUT = ['PUT'];
|
|
5
|
-
const
|
|
5
|
+
const PARAMETER = 0;
|
|
6
6
|
const STATIC = 1;
|
|
7
7
|
const WILDCARD = 2;
|
|
8
|
-
export { ON_DELETE, ON_GET, ON_POST, ON_PUT,
|
|
8
|
+
export { ON_DELETE, ON_GET, ON_POST, ON_PUT, PARAMETER, STATIC, WILDCARD };
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
package/build/router/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Name, Options, Route, RouteOptions } from '../types.js';
|
|
2
2
|
import { Node } from './node.js';
|
|
3
|
+
declare function key(method: string, subdomain?: string | null): string;
|
|
3
4
|
declare class Router<T> {
|
|
5
|
+
bucket: Record<ReturnType<typeof key>, {
|
|
6
|
+
root: Node<T>;
|
|
7
|
+
static: Record<string, Route<T>>;
|
|
8
|
+
}>;
|
|
4
9
|
groups: Options<T>[];
|
|
5
|
-
root: Node<T>;
|
|
6
10
|
routes: Record<Name, Route<T>>;
|
|
7
|
-
static: Record<Name, Route<T>>;
|
|
8
11
|
subdomains: string[] | null;
|
|
9
|
-
constructor();
|
|
10
12
|
private add;
|
|
11
13
|
private route;
|
|
12
14
|
delete(options: RouteOptions<T>): this;
|
package/build/router/index.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import { ON_DELETE, ON_GET, ON_POST, ON_PUT, STATIC } from '../constants.js';
|
|
2
2
|
import { Node } from './node.js';
|
|
3
3
|
import pipeline from '@esportsplus/pipeline';
|
|
4
|
+
function key(method, subdomain) {
|
|
5
|
+
return (method + (subdomain ? subdomain + ' ' : '')).toUpperCase();
|
|
6
|
+
}
|
|
4
7
|
function normalize(path) {
|
|
5
8
|
if (path) {
|
|
6
9
|
if (path[0] !== '/') {
|
|
7
10
|
path = '/' + path;
|
|
8
11
|
}
|
|
9
|
-
if (path.
|
|
12
|
+
if (path.length > 1 && path[path.length - 1] === '/') {
|
|
10
13
|
path = path.slice(0, -1);
|
|
11
14
|
}
|
|
12
15
|
}
|
|
13
16
|
return path || '/';
|
|
14
17
|
}
|
|
15
|
-
function radixkey(method, path, subdomain) {
|
|
16
|
-
return ((subdomain ? subdomain + ' ' : '') + method).toUpperCase() + ' ' + normalize(path);
|
|
17
|
-
}
|
|
18
18
|
function set(route, options) {
|
|
19
|
+
let pipeline = route.pipeline;
|
|
19
20
|
for (let key in options) {
|
|
20
21
|
let value = options[key];
|
|
21
22
|
if (key === 'middleware') {
|
|
22
23
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
23
|
-
|
|
24
|
+
pipeline.add(value[i]);
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
else if (key === 'responder') {
|
|
27
|
-
|
|
28
|
+
pipeline.add(value);
|
|
28
29
|
}
|
|
29
30
|
else {
|
|
30
31
|
route[key] = (route[key] || '') + value;
|
|
@@ -32,20 +33,20 @@ function set(route, options) {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
class Router {
|
|
36
|
+
bucket = {};
|
|
35
37
|
groups = [];
|
|
36
|
-
root;
|
|
37
38
|
routes = {};
|
|
38
|
-
static = {};
|
|
39
39
|
subdomains = null;
|
|
40
|
-
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
add(method, path, route) {
|
|
41
|
+
let bucket = this.bucket[key(method, route.subdomain)] ??= {
|
|
42
|
+
root: new Node(),
|
|
43
|
+
static: {}
|
|
44
|
+
};
|
|
45
|
+
if (path.indexOf(':') === -1 || bucket.root.add(path, route).type === STATIC) {
|
|
46
|
+
if (path in bucket.static) {
|
|
47
|
+
throw new Error(`Routing: static path '${path}' is already in use`);
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
+
bucket.static[path] = route;
|
|
49
50
|
}
|
|
50
51
|
return this;
|
|
51
52
|
}
|
|
@@ -84,44 +85,43 @@ class Router {
|
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
match(method, path, subdomain) {
|
|
87
|
-
let
|
|
88
|
-
if (
|
|
89
|
-
return {
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
let bucket = this.bucket[key(method, subdomain)];
|
|
89
|
+
if (!bucket) {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
path = normalize(path);
|
|
93
|
+
if (path in bucket.static) {
|
|
94
|
+
return { route: bucket.static[path] };
|
|
92
95
|
}
|
|
93
|
-
return
|
|
96
|
+
return bucket.root.find(path);
|
|
94
97
|
}
|
|
95
98
|
on(methods, options) {
|
|
96
99
|
let route = this.route(options);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
let name = route.name, path = route.path, subdomain = route.subdomain;
|
|
101
|
+
if (name) {
|
|
102
|
+
if (this.routes[name]) {
|
|
103
|
+
throw new Error(`Routing: '${name}' is already in use`);
|
|
100
104
|
}
|
|
101
|
-
this.routes[
|
|
105
|
+
this.routes[name] = route;
|
|
102
106
|
}
|
|
103
|
-
if (
|
|
107
|
+
if (path) {
|
|
104
108
|
for (let i = 0, n = methods.length; i < n; i++) {
|
|
105
|
-
let
|
|
106
|
-
if (
|
|
107
|
-
let segments =
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
let method = methods[i];
|
|
110
|
+
if (path.indexOf('?:') !== -1) {
|
|
111
|
+
let segments = path.split('?:'), url = segments[0];
|
|
112
|
+
this.add(method, url, route);
|
|
113
|
+
for (let i = 1; i < segments.length; i++) {
|
|
114
|
+
url += '/:' + segments[i];
|
|
115
|
+
this.add(method, url, route);
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
else {
|
|
113
|
-
this.add(
|
|
119
|
+
this.add(method, path, route);
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
if (!this.subdomains) {
|
|
120
|
-
this.subdomains = [subdomain];
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
this.subdomains.push(subdomain);
|
|
124
|
-
}
|
|
123
|
+
if (subdomain) {
|
|
124
|
+
(this.subdomains ??= []).push(subdomain.toLowerCase());
|
|
125
125
|
}
|
|
126
126
|
return this;
|
|
127
127
|
}
|
package/build/router/node.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { Route } from './index.js';
|
|
2
2
|
declare class Node<T> {
|
|
3
|
-
children: Map<string | number, Node<T>> | null;
|
|
4
3
|
parent: Node<T> | null;
|
|
5
4
|
path: string | null;
|
|
6
|
-
property: string | null;
|
|
7
5
|
route: Route<T> | null;
|
|
6
|
+
static: Map<string | number, Node<T>> | null;
|
|
8
7
|
type: number | null;
|
|
8
|
+
name: string | null;
|
|
9
|
+
parameter: Node<T> | null;
|
|
10
|
+
wildcard: Node<T> | null;
|
|
9
11
|
constructor(parent?: Node<T>['parent']);
|
|
10
12
|
add(path: string, route: Route<T>): Node<T>;
|
|
11
13
|
find(path: string): {
|
|
12
14
|
parameters?: Readonly<Record<PropertyKey, unknown>>;
|
|
13
15
|
route?: Readonly<Route<T>>;
|
|
14
16
|
};
|
|
15
|
-
remove(path: string): void;
|
|
16
17
|
}
|
|
17
18
|
export { Node };
|
package/build/router/node.js
CHANGED
|
@@ -1,36 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PARAMETER, STATIC, WILDCARD } from '../constants.js';
|
|
2
2
|
class Node {
|
|
3
|
-
children = null;
|
|
4
3
|
parent = null;
|
|
5
4
|
path = null;
|
|
6
|
-
property = null;
|
|
7
5
|
route = null;
|
|
6
|
+
static = null;
|
|
8
7
|
type = null;
|
|
8
|
+
name = null;
|
|
9
|
+
parameter = null;
|
|
10
|
+
wildcard = null;
|
|
9
11
|
constructor(parent = null) {
|
|
10
12
|
this.parent = parent;
|
|
11
13
|
}
|
|
12
14
|
add(path, route) {
|
|
13
15
|
let node = this, segments = path.split('/'), type = STATIC, unnamed = 0;
|
|
14
16
|
for (let i = 0, n = segments.length; i < n; i++) {
|
|
15
|
-
let
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
node.
|
|
17
|
+
let segment = segments[i], symbol = segment[0];
|
|
18
|
+
if (symbol === ':') {
|
|
19
|
+
if (!node.parameter) {
|
|
20
|
+
node.parameter = new Node(node);
|
|
21
|
+
node.parameter.name = (segment.slice(1) || unnamed++).toString();
|
|
20
22
|
}
|
|
21
|
-
node
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
node = node.parameter;
|
|
24
|
+
type = PARAMETER;
|
|
25
|
+
}
|
|
26
|
+
else if (symbol === '*') {
|
|
27
|
+
if (!node.wildcard) {
|
|
28
|
+
node.wildcard = new Node(node);
|
|
29
|
+
node.wildcard.name = (segment.slice(2) || unnamed++).toString();
|
|
26
30
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
node = node.wildcard;
|
|
32
|
+
type = WILDCARD;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
let next = node.static?.get(segment);
|
|
36
|
+
if (!next) {
|
|
37
|
+
next = new Node(node);
|
|
38
|
+
(node.static ??= new Map()).set(segment, next);
|
|
31
39
|
}
|
|
40
|
+
node = next;
|
|
32
41
|
}
|
|
33
|
-
node = child;
|
|
34
42
|
}
|
|
35
43
|
node.path = path;
|
|
36
44
|
node.route = route;
|
|
@@ -38,56 +46,35 @@ class Node {
|
|
|
38
46
|
return node;
|
|
39
47
|
}
|
|
40
48
|
find(path) {
|
|
41
|
-
let node = this, parameters
|
|
49
|
+
let node = this, parameters, segments = path.split('/'), wildcard;
|
|
42
50
|
for (let i = 0, n = segments.length; i < n; i++) {
|
|
43
|
-
let segment = segments[i]
|
|
44
|
-
if (
|
|
51
|
+
let segment = segments[i];
|
|
52
|
+
if (node.wildcard) {
|
|
45
53
|
wildcard = {
|
|
46
|
-
node:
|
|
54
|
+
node: node.wildcard,
|
|
47
55
|
value: segments.slice(i).join('/')
|
|
48
56
|
};
|
|
49
57
|
}
|
|
50
|
-
let next = node.
|
|
58
|
+
let next = node.static?.get(segment);
|
|
51
59
|
if (next) {
|
|
52
60
|
node = next;
|
|
61
|
+
continue;
|
|
53
62
|
}
|
|
54
|
-
|
|
55
|
-
node =
|
|
56
|
-
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
parameters[node.property] = segment;
|
|
63
|
+
if (!node.parameter) {
|
|
64
|
+
node = undefined;
|
|
65
|
+
break;
|
|
60
66
|
}
|
|
67
|
+
node = node.parameter;
|
|
68
|
+
(parameters ??= {})[node.name] = segment;
|
|
61
69
|
}
|
|
62
|
-
if ((node === undefined || node.route === null) && wildcard
|
|
70
|
+
if ((node === undefined || node.route === null) && wildcard) {
|
|
63
71
|
node = wildcard.node;
|
|
64
|
-
parameters[node.
|
|
65
|
-
}
|
|
66
|
-
if (!node) {
|
|
67
|
-
return {};
|
|
72
|
+
(parameters ??= {})[node.name] = wildcard.value;
|
|
68
73
|
}
|
|
69
74
|
return {
|
|
70
75
|
parameters,
|
|
71
|
-
route: node
|
|
76
|
+
route: node?.route || undefined
|
|
72
77
|
};
|
|
73
78
|
}
|
|
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
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
let parent = node.parent;
|
|
86
|
-
if (parent && parent.children) {
|
|
87
|
-
parent.children.delete(segments[segments.length - 1]);
|
|
88
|
-
parent.children.delete(WILDCARD);
|
|
89
|
-
parent.children.delete(PLACEHOLDER);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
79
|
}
|
|
93
80
|
export { Node };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@esportsplus/pipeline": "^1.1.5",
|
|
5
5
|
"@esportsplus/reactivity": "^0.12.4",
|
|
6
|
-
"@esportsplus/utilities": "^0.
|
|
6
|
+
"@esportsplus/utilities": "^0.22.0"
|
|
7
7
|
},
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@esportsplus/typescript": "^0.9.2"
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
"name": "@esportsplus/routing",
|
|
13
13
|
"private": false,
|
|
14
14
|
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
15
16
|
"types": "./build/index.d.ts",
|
|
16
|
-
"version": "0.
|
|
17
|
+
"version": "0.2.0",
|
|
17
18
|
"scripts": {
|
|
18
19
|
"build": "tsc && tsc-alias",
|
|
19
|
-
"-": "-"
|
|
20
|
+
"-": "-",
|
|
21
|
+
"bench": "node bench/index.mjs"
|
|
20
22
|
}
|
|
21
23
|
}
|
package/src/browser.ts
CHANGED
|
@@ -44,10 +44,11 @@ function href<T>() {
|
|
|
44
44
|
|
|
45
45
|
function match<T>(request: Request<T>, router: Router<T>, subdomain?: string) {
|
|
46
46
|
if (router.subdomains !== null) {
|
|
47
|
-
let
|
|
47
|
+
let hostname = request.hostname,
|
|
48
|
+
subdomains = router.subdomains;
|
|
48
49
|
|
|
49
50
|
for (let i = 0, n = subdomains.length; i < n; i++) {
|
|
50
|
-
if (!
|
|
51
|
+
if (!hostname.startsWith(subdomains[i])) {
|
|
51
52
|
continue;
|
|
52
53
|
}
|
|
53
54
|
|
package/src/constants.ts
CHANGED
|
@@ -7,11 +7,11 @@ const ON_POST = ['POST'];
|
|
|
7
7
|
const ON_PUT = ['PUT'];
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const PARAMETER = 0;
|
|
11
11
|
|
|
12
12
|
const STATIC = 1;
|
|
13
13
|
|
|
14
14
|
const WILDCARD = 2;
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
export { ON_DELETE, ON_GET, ON_POST, ON_PUT,
|
|
17
|
+
export { ON_DELETE, ON_GET, ON_POST, ON_PUT, PARAMETER, STATIC, WILDCARD };
|
package/src/index.ts
CHANGED
package/src/router/index.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { ON_DELETE, ON_GET, ON_POST, ON_PUT, STATIC } from '
|
|
2
|
-
import { Name, Options, Request, Route, RouteOptions } from '
|
|
1
|
+
import { ON_DELETE, ON_GET, ON_POST, ON_PUT, STATIC } from '../constants';
|
|
2
|
+
import { Name, Options, Request, Route, RouteOptions } from '../types';
|
|
3
3
|
import { Node } from './node';
|
|
4
4
|
import pipeline from '@esportsplus/pipeline';
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
function key(method: string, subdomain?: string | null) {
|
|
8
|
+
return (method + (subdomain ? subdomain + ' ' : '')).toUpperCase();
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
function normalize(path: string) {
|
|
8
12
|
if (path) {
|
|
9
13
|
if (path[0] !== '/') {
|
|
10
14
|
path = '/' + path;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
if (path.
|
|
17
|
+
if (path.length > 1 && path[path.length - 1] === '/') {
|
|
14
18
|
path = path.slice(0, -1);
|
|
15
19
|
}
|
|
16
20
|
}
|
|
@@ -18,21 +22,19 @@ function normalize(path: string) {
|
|
|
18
22
|
return path || '/';
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
function radixkey(method: string, path: string, subdomain?: string | null) {
|
|
22
|
-
return ((subdomain ? subdomain + ' ' : '') + method).toUpperCase() + ' ' + normalize(path);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
25
|
function set<T>(route: Route<T>, options: Options<T> | RouteOptions<T>) {
|
|
26
|
+
let pipeline = route.pipeline;
|
|
27
|
+
|
|
26
28
|
for (let key in options) {
|
|
27
29
|
let value = options[key as keyof typeof options] as any;
|
|
28
30
|
|
|
29
31
|
if (key === 'middleware') {
|
|
30
32
|
for (let i = 0, n = value.length; i < n; i++) {
|
|
31
|
-
|
|
33
|
+
pipeline.add(value[i]);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
else if (key === 'responder') {
|
|
35
|
-
|
|
37
|
+
pipeline.add(value);
|
|
36
38
|
}
|
|
37
39
|
else {
|
|
38
40
|
// @ts-ignore
|
|
@@ -43,25 +45,24 @@ function set<T>(route: Route<T>, options: Options<T> | RouteOptions<T>) {
|
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
class Router<T> {
|
|
48
|
+
bucket: Record<ReturnType<typeof key>, { root: Node<T>, static: Record<string, Route<T>> }> = {};
|
|
46
49
|
groups: Options<T>[] = [];
|
|
47
|
-
root: Node<T>;
|
|
48
50
|
routes: Record<Name, Route<T>> = {};
|
|
49
|
-
static: Record<Name, Route<T>> = {};
|
|
50
51
|
subdomains: string[] | null = null;
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
private add(method: string, path: string, route: Route<T>) {
|
|
55
|
+
let bucket = this.bucket[ key(method, route.subdomain) ] ??= {
|
|
56
|
+
root: new Node(),
|
|
57
|
+
static: {}
|
|
58
|
+
};
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new Error(`Routing: static path '${radixkey}' is already in use`);
|
|
60
|
+
if (path.indexOf(':') === -1 || bucket.root.add(path, route).type === STATIC) {
|
|
61
|
+
if (path in bucket.static) {
|
|
62
|
+
throw new Error(`Routing: static path '${path}' is already in use`);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
bucket.static[path] = route;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
return this;
|
|
@@ -113,55 +114,59 @@ class Router<T> {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
match(method: string, path: string, subdomain?: string | null) {
|
|
116
|
-
let
|
|
117
|
+
let bucket = this.bucket[ key(method, subdomain) ];
|
|
117
118
|
|
|
118
|
-
if (
|
|
119
|
-
return {
|
|
120
|
-
route: this.static[key] as Readonly<Route<T>>
|
|
121
|
-
};
|
|
119
|
+
if (!bucket) {
|
|
120
|
+
return {};
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
|
|
123
|
+
path = normalize(path);
|
|
124
|
+
|
|
125
|
+
if (path in bucket.static) {
|
|
126
|
+
return { route: bucket.static[path] };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return bucket.root.find(path);
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
on(methods: string[], options: RouteOptions<T>) {
|
|
128
133
|
let route = this.route(options);
|
|
129
134
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
let name = route.name,
|
|
136
|
+
path = route.path,
|
|
137
|
+
subdomain = route.subdomain;
|
|
138
|
+
|
|
139
|
+
if (name) {
|
|
140
|
+
if (this.routes[name]) {
|
|
141
|
+
throw new Error(`Routing: '${name}' is already in use`);
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
this.routes[
|
|
144
|
+
this.routes[name] = route;
|
|
136
145
|
}
|
|
137
146
|
|
|
138
|
-
if (
|
|
147
|
+
if (path) {
|
|
139
148
|
for (let i = 0, n = methods.length; i < n; i++) {
|
|
140
|
-
let
|
|
149
|
+
let method = methods[i];
|
|
150
|
+
|
|
151
|
+
if (path.indexOf('?:') !== -1) {
|
|
152
|
+
let segments = path.split('?:'),
|
|
153
|
+
url = segments[0];
|
|
141
154
|
|
|
142
|
-
|
|
143
|
-
let segments = key.split('?:'),
|
|
144
|
-
url = '';
|
|
155
|
+
this.add(method, url, route);
|
|
145
156
|
|
|
146
|
-
for (let i =
|
|
147
|
-
|
|
157
|
+
for (let i = 1; i < segments.length; i++) {
|
|
158
|
+
url += '/:' + segments[i];
|
|
159
|
+
this.add(method, url, route);
|
|
148
160
|
}
|
|
149
161
|
}
|
|
150
162
|
else {
|
|
151
|
-
this.add(
|
|
163
|
+
this.add(method, path, route);
|
|
152
164
|
}
|
|
153
165
|
}
|
|
154
166
|
}
|
|
155
167
|
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!this.subdomains) {
|
|
160
|
-
this.subdomains = [subdomain];
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
this.subdomains.push(subdomain);
|
|
164
|
-
}
|
|
168
|
+
if (subdomain) {
|
|
169
|
+
(this.subdomains ??= []).push( subdomain.toLowerCase() );
|
|
165
170
|
}
|
|
166
171
|
|
|
167
172
|
return this;
|
package/src/router/node.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PARAMETER, STATIC, WILDCARD } from '../constants';
|
|
2
2
|
import { Route } from './index';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Node<T> {
|
|
6
|
-
children: Map<string | number, Node<T>> | null = null;
|
|
7
6
|
parent: Node<T> | null = null;
|
|
8
7
|
path: string | null = null;
|
|
9
|
-
property: string | null = null;
|
|
10
8
|
route: Route<T> | null = null;
|
|
9
|
+
static: Map<string | number, Node<T>> | null = null;
|
|
11
10
|
type: number | null = null;
|
|
12
11
|
|
|
12
|
+
// Parameter or Wildcard parameter name
|
|
13
|
+
name: string | null = null;
|
|
14
|
+
parameter: Node<T> | null = null;
|
|
15
|
+
wildcard: Node<T> | null = null;
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
constructor(parent: Node<T>['parent'] = null) {
|
|
15
19
|
this.parent = parent;
|
|
@@ -23,33 +27,40 @@ class Node<T> {
|
|
|
23
27
|
unnamed = 0;
|
|
24
28
|
|
|
25
29
|
for (let i = 0, n = segments.length; i < n; i++) {
|
|
26
|
-
let
|
|
30
|
+
let segment = segments[i],
|
|
31
|
+
symbol = segment[0];
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
// Named name
|
|
34
|
+
if (symbol === ':') {
|
|
35
|
+
if (!node.parameter) {
|
|
36
|
+
node.parameter = new Node<T>(node);
|
|
37
|
+
node.parameter.name = (segment.slice(1) || unnamed++).toString();
|
|
38
|
+
}
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
node = node.parameter;
|
|
41
|
+
type = PARAMETER;
|
|
42
|
+
}
|
|
43
|
+
// "*:" Wildcard name
|
|
44
|
+
else if (symbol === '*') {
|
|
45
|
+
if (!node.wildcard) {
|
|
46
|
+
node.wildcard = new Node<T>(node);
|
|
47
|
+
node.wildcard.name = (segment.slice(2) || unnamed++).toString();
|
|
34
48
|
}
|
|
35
49
|
|
|
36
|
-
node
|
|
50
|
+
node = node.wildcard;
|
|
51
|
+
type = WILDCARD;
|
|
52
|
+
}
|
|
53
|
+
// Static name
|
|
54
|
+
else {
|
|
55
|
+
let next: Node<T> | undefined = node.static?.get(segment);
|
|
37
56
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
node.children.set(PLACEHOLDER, child);
|
|
42
|
-
type = null;
|
|
57
|
+
if (!next) {
|
|
58
|
+
next = new Node<T>(node);
|
|
59
|
+
(node.static ??= new Map()).set(segment, next);
|
|
43
60
|
}
|
|
44
|
-
// "*:" Wildcard property
|
|
45
|
-
else if (symbol === '*') {
|
|
46
|
-
child.property = (segment.slice(2) || unnamed++).toString();
|
|
47
|
-
node.children.set(WILDCARD, child);
|
|
48
|
-
type = null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
node = next;
|
|
63
|
+
}
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
node.path = path;
|
|
@@ -64,77 +75,47 @@ class Node<T> {
|
|
|
64
75
|
route?: Readonly<Route<T>>;
|
|
65
76
|
} {
|
|
66
77
|
let node: Node<T> | undefined = this,
|
|
67
|
-
parameters: Record<PropertyKey, unknown>
|
|
78
|
+
parameters: Record<PropertyKey, unknown> | undefined,
|
|
68
79
|
segments = path.split('/'),
|
|
69
|
-
wildcard: { node: Node<T>, value: string } |
|
|
80
|
+
wildcard: { node: Node<T>, value: string } | undefined;
|
|
70
81
|
|
|
71
82
|
for (let i = 0, n = segments.length; i < n; i++) {
|
|
72
|
-
let segment = segments[i]
|
|
73
|
-
wc = node.children?.get(WILDCARD);
|
|
83
|
+
let segment = segments[i];
|
|
74
84
|
|
|
75
|
-
if (
|
|
85
|
+
if (node.wildcard) {
|
|
76
86
|
wildcard = {
|
|
77
|
-
node:
|
|
87
|
+
node: node.wildcard,
|
|
78
88
|
value: segments.slice(i).join('/')
|
|
79
89
|
};
|
|
80
90
|
}
|
|
81
91
|
|
|
82
|
-
// Exact matches take precedence over
|
|
83
|
-
let next: Node<T> | undefined = node.
|
|
92
|
+
// Exact matches take precedence over parameters
|
|
93
|
+
let next: Node<T> | undefined = node.static?.get(segment) as Node<T> | undefined;
|
|
84
94
|
|
|
85
95
|
if (next) {
|
|
86
96
|
node = next;
|
|
97
|
+
continue;
|
|
87
98
|
}
|
|
88
|
-
else {
|
|
89
|
-
node = node.children?.get(PLACEHOLDER);
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
parameters[ node.property! ] = segment;
|
|
100
|
+
if (!node.parameter) {
|
|
101
|
+
node = undefined;
|
|
102
|
+
break;
|
|
96
103
|
}
|
|
97
|
-
}
|
|
98
104
|
|
|
99
|
-
|
|
100
|
-
node =
|
|
101
|
-
parameters[ node.property! ] = wildcard.value;
|
|
105
|
+
node = node.parameter;
|
|
106
|
+
(parameters ??= {})[node.name!] = segment;
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
if (
|
|
105
|
-
|
|
109
|
+
if ((node === undefined || node.route === null) && wildcard) {
|
|
110
|
+
node = wildcard.node;
|
|
111
|
+
(parameters ??= {})[ node.name! ] = wildcard.value;
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
return {
|
|
109
115
|
parameters,
|
|
110
|
-
route: node
|
|
116
|
+
route: node?.route || undefined
|
|
111
117
|
};
|
|
112
118
|
}
|
|
113
|
-
|
|
114
|
-
remove(path: string) {
|
|
115
|
-
let node: Node<T> | undefined = this,
|
|
116
|
-
segments = path.split('/');
|
|
117
|
-
|
|
118
|
-
for (let i = 0, n = segments.length; i < n; i++) {
|
|
119
|
-
node = node.children?.get( segments[i] );
|
|
120
|
-
|
|
121
|
-
if (!node) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (node.children?.size) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let parent = node.parent;
|
|
131
|
-
|
|
132
|
-
if (parent && parent.children) {
|
|
133
|
-
parent.children.delete( segments[segments.length - 1] );
|
|
134
|
-
parent.children.delete(WILDCARD);
|
|
135
|
-
parent.children.delete(PLACEHOLDER);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
119
|
}
|
|
139
120
|
|
|
140
121
|
|
package/src/types.ts
CHANGED
package/build/slugify.d.ts
DELETED
package/build/slugify.js
DELETED
package/src/slugify.ts
DELETED