@esportsplus/routing 0.6.0 → 0.6.2
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/.editorconfig +9 -9
- package/.gitattributes +2 -2
- package/.github/dependabot.yml +24 -24
- package/.github/workflows/bump.yml +8 -8
- package/.github/workflows/dependabot.yml +11 -11
- package/.github/workflows/publish.yml +16 -16
- package/README.md +217 -217
- package/build/client/index.d.ts +1 -1
- package/build/client/index.js +10 -10
- package/package.json +3 -3
- package/src/client/constants.ts +21 -21
- package/src/client/index.ts +195 -195
- package/src/client/router/index.ts +293 -293
- package/src/client/router/node.ts +121 -121
- package/src/client/types.ts +105 -105
- package/src/constants.ts +3 -3
- package/test/index.ts +648 -648
- package/tsconfig.json +8 -8
package/src/client/constants.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
const ON_DELETE = ['DELETE'];
|
|
2
|
-
|
|
3
|
-
const ON_GET = ['GET'];
|
|
4
|
-
|
|
5
|
-
const ON_POST = ['POST'];
|
|
6
|
-
|
|
7
|
-
const ON_PUT = ['PUT'];
|
|
8
|
-
|
|
9
|
-
const PARAMETER = 0;
|
|
10
|
-
|
|
11
|
-
const STATIC = 1;
|
|
12
|
-
|
|
13
|
-
const WILDCARD = 2;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
ON_DELETE, ON_GET, ON_POST, ON_PUT,
|
|
18
|
-
PARAMETER,
|
|
19
|
-
STATIC,
|
|
20
|
-
WILDCARD
|
|
21
|
-
};
|
|
1
|
+
const ON_DELETE = ['DELETE'];
|
|
2
|
+
|
|
3
|
+
const ON_GET = ['GET'];
|
|
4
|
+
|
|
5
|
+
const ON_POST = ['POST'];
|
|
6
|
+
|
|
7
|
+
const ON_PUT = ['PUT'];
|
|
8
|
+
|
|
9
|
+
const PARAMETER = 0;
|
|
10
|
+
|
|
11
|
+
const STATIC = 1;
|
|
12
|
+
|
|
13
|
+
const WILDCARD = 2;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
ON_DELETE, ON_GET, ON_POST, ON_PUT,
|
|
18
|
+
PARAMETER,
|
|
19
|
+
STATIC,
|
|
20
|
+
WILDCARD
|
|
21
|
+
};
|
|
22
22
|
export { PACKAGE_NAME } from '~/constants';
|
package/src/client/index.ts
CHANGED
|
@@ -1,195 +1,195 @@
|
|
|
1
|
-
import { effect, reactive, root } from '@esportsplus/reactivity';
|
|
2
|
-
import { AccumulateRoutes, ExtractOptionalParams, ExtractRequiredParams, InferOutput, Middleware, Next, PathParamsObject, Request, Route, RouteFactory, RoutePath } from './types';
|
|
3
|
-
import { Router } from './router';
|
|
4
|
-
import pipeline from '@esportsplus/pipeline';
|
|
5
|
-
import { PACKAGE_NAME } from './constants';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let cache: Request<any>[] = [],
|
|
9
|
-
location = window.location;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function back() {
|
|
13
|
-
window.history.back();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function forward() {
|
|
17
|
-
window.history.forward();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function href<T>() {
|
|
21
|
-
let hash = location.hash || '#/',
|
|
22
|
-
path = hash ? hash.slice(1).split('?') : ['/', ''],
|
|
23
|
-
request = {
|
|
24
|
-
hostname: location.hostname,
|
|
25
|
-
href: location.href,
|
|
26
|
-
method: 'GET',
|
|
27
|
-
origin: location.origin,
|
|
28
|
-
path: path[0],
|
|
29
|
-
port: location.port,
|
|
30
|
-
protocol: location.protocol,
|
|
31
|
-
query: {} as Record<PropertyKey, unknown>
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
if (path[1]) {
|
|
35
|
-
let params = new URLSearchParams(path[1]),
|
|
36
|
-
query = request.query;
|
|
37
|
-
|
|
38
|
-
for (let [key, value] of params.entries()) {
|
|
39
|
-
query[key] = value;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return request as Request<T>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function match<T>(request: Request<T>, router: Router<T>, subdomain?: string) {
|
|
47
|
-
if (router.subdomains !== null) {
|
|
48
|
-
let hostname = request.hostname,
|
|
49
|
-
subdomains = router.subdomains;
|
|
50
|
-
|
|
51
|
-
for (let i = 0, n = subdomains.length; i < n; i++) {
|
|
52
|
-
if (!hostname.startsWith(subdomains[i])) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
subdomain = subdomains[i];
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return router.match(request.method, request.path, subdomain || '');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function middleware<T>(request: Request<T>, router: Router<T>) {
|
|
65
|
-
let middleware = pipeline<Request<T>, T>();
|
|
66
|
-
|
|
67
|
-
function host(...stages: Middleware<T>[]) {
|
|
68
|
-
for (let i = 0, n = stages.length; i < n; i++) {
|
|
69
|
-
middleware.add( stages[i] );
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return middleware.dispatch(request) as T;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
host.dispatch = (request: Request<T>) => {
|
|
76
|
-
let { route } = request.data;
|
|
77
|
-
|
|
78
|
-
if (route === undefined) {
|
|
79
|
-
throw new Error(`${PACKAGE_NAME}: route is undefined!`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return route.pipeline.dispatch(request);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
host.match = (fallback: Route<T>) => {
|
|
86
|
-
let state = reactive<ReturnType<typeof router.match>>({
|
|
87
|
-
parameters: undefined,
|
|
88
|
-
route: undefined
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (fallback === undefined) {
|
|
92
|
-
throw new Error(`${PACKAGE_NAME}: fallback route does not exist`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
effect(() => {
|
|
96
|
-
let { parameters, route } = match(request, router);
|
|
97
|
-
|
|
98
|
-
state.parameters = parameters;
|
|
99
|
-
state.route = route || fallback;
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return (request: Request<T>, next: Next<T>) => {
|
|
103
|
-
if (state.route === undefined) {
|
|
104
|
-
throw new Error(`${PACKAGE_NAME}: route is undefined`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return root(() => {
|
|
108
|
-
request.data = {
|
|
109
|
-
parameters: state.parameters,
|
|
110
|
-
route: state.route
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
return next(request);
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
return host;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function normalize(uri: string) {
|
|
122
|
-
if (uri[0] === '/') {
|
|
123
|
-
return '#' + uri;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return uri;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function onpopstate() {
|
|
130
|
-
let values = href();
|
|
131
|
-
|
|
132
|
-
for (let i = 0, n = cache.length; i < n; i++) {
|
|
133
|
-
let state = cache[i];
|
|
134
|
-
|
|
135
|
-
for (let key in values) {
|
|
136
|
-
// @ts-ignore
|
|
137
|
-
state[key] = values[key];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const router = <const Factories extends readonly RouteFactory<any>[]>(...factories: Factories) => {
|
|
144
|
-
type Routes = AccumulateRoutes<Factories>;
|
|
145
|
-
type T = InferOutput<Factories[number]>;
|
|
146
|
-
|
|
147
|
-
let instance = factories.reduce(
|
|
148
|
-
(r, factory) => factory(r),
|
|
149
|
-
new Router<T, {}>() as Router<T, any>
|
|
150
|
-
) as Router<T, Routes>,
|
|
151
|
-
request = reactive<Request<T>>(Object.assign(href<T>(), { data: {} } as any));
|
|
152
|
-
|
|
153
|
-
if (cache.push(request) === 1) {
|
|
154
|
-
window.addEventListener('hashchange', onpopstate);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
back,
|
|
159
|
-
forward,
|
|
160
|
-
middleware: middleware(request, instance as Router<T>),
|
|
161
|
-
redirect: <RouteName extends keyof Routes>(
|
|
162
|
-
name: RouteName,
|
|
163
|
-
...values: ExtractRequiredParams<RoutePath<Routes, RouteName>> extends never
|
|
164
|
-
? ExtractOptionalParams<RoutePath<Routes, RouteName>> extends never
|
|
165
|
-
? []
|
|
166
|
-
: [params?: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
167
|
-
: [params: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
168
|
-
) => {
|
|
169
|
-
if ((name as string).indexOf('://') !== -1) {
|
|
170
|
-
return window.location.replace(name as any);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
window.location.hash = normalize(instance.uri(name as any, values as any));
|
|
174
|
-
},
|
|
175
|
-
routes: instance.routes,
|
|
176
|
-
uri: <RouteName extends keyof Routes>(
|
|
177
|
-
name: RouteName,
|
|
178
|
-
...values: ExtractRequiredParams<RoutePath<Routes, RouteName>> extends never
|
|
179
|
-
? ExtractOptionalParams<RoutePath<Routes, RouteName>> extends never
|
|
180
|
-
? []
|
|
181
|
-
: [params?: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
182
|
-
: [params: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
183
|
-
) => {
|
|
184
|
-
return normalize(instance.uri(name as any, values as any));
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
export { router };
|
|
191
|
-
export type {
|
|
192
|
-
Middleware,
|
|
193
|
-
Next,
|
|
194
|
-
Request, Route, RouteFactory
|
|
195
|
-
} from './types';
|
|
1
|
+
import { effect, reactive, root } from '@esportsplus/reactivity';
|
|
2
|
+
import { AccumulateRoutes, ExtractOptionalParams, ExtractRequiredParams, InferOutput, Middleware, Next, PathParamsObject, Request, Route, RouteFactory, RoutePath } from './types';
|
|
3
|
+
import { Router } from './router';
|
|
4
|
+
import pipeline from '@esportsplus/pipeline';
|
|
5
|
+
import { PACKAGE_NAME } from './constants';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
let cache: Request<any>[] = [],
|
|
9
|
+
location = window.location;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function back() {
|
|
13
|
+
window.history.back();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function forward() {
|
|
17
|
+
window.history.forward();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function href<T>() {
|
|
21
|
+
let hash = location.hash || '#/',
|
|
22
|
+
path = hash ? hash.slice(1).split('?') : ['/', ''],
|
|
23
|
+
request = {
|
|
24
|
+
hostname: location.hostname,
|
|
25
|
+
href: location.href,
|
|
26
|
+
method: 'GET',
|
|
27
|
+
origin: location.origin,
|
|
28
|
+
path: path[0],
|
|
29
|
+
port: location.port,
|
|
30
|
+
protocol: location.protocol,
|
|
31
|
+
query: {} as Record<PropertyKey, unknown>
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (path[1]) {
|
|
35
|
+
let params = new URLSearchParams(path[1]),
|
|
36
|
+
query = request.query;
|
|
37
|
+
|
|
38
|
+
for (let [key, value] of params.entries()) {
|
|
39
|
+
query[key] = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return request as Request<T>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function match<T>(request: Request<T>, router: Router<T>, subdomain?: string) {
|
|
47
|
+
if (router.subdomains !== null) {
|
|
48
|
+
let hostname = request.hostname,
|
|
49
|
+
subdomains = router.subdomains;
|
|
50
|
+
|
|
51
|
+
for (let i = 0, n = subdomains.length; i < n; i++) {
|
|
52
|
+
if (!hostname.startsWith(subdomains[i])) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
subdomain = subdomains[i];
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return router.match(request.method, request.path, subdomain || '');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function middleware<T>(request: Request<T>, router: Router<T>) {
|
|
65
|
+
let middleware = pipeline<Request<T>, T>();
|
|
66
|
+
|
|
67
|
+
function host(...stages: Middleware<T>[]) {
|
|
68
|
+
for (let i = 0, n = stages.length; i < n; i++) {
|
|
69
|
+
middleware.add( stages[i] );
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return middleware.dispatch(request) as T;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
host.dispatch = (request: Request<T>) => {
|
|
76
|
+
let { route } = request.data;
|
|
77
|
+
|
|
78
|
+
if (route === undefined) {
|
|
79
|
+
throw new Error(`${PACKAGE_NAME}: route is undefined!`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return route.pipeline.dispatch(request);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
host.match = (fallback: Route<T>) => {
|
|
86
|
+
let state = reactive<ReturnType<typeof router.match>>({
|
|
87
|
+
parameters: undefined,
|
|
88
|
+
route: undefined
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (fallback === undefined) {
|
|
92
|
+
throw new Error(`${PACKAGE_NAME}: fallback route does not exist`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
effect(() => {
|
|
96
|
+
let { parameters, route } = match(request, router);
|
|
97
|
+
|
|
98
|
+
state.parameters = parameters;
|
|
99
|
+
state.route = route || fallback;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return (request: Request<T>, next: Next<T>) => {
|
|
103
|
+
if (state.route === undefined) {
|
|
104
|
+
throw new Error(`${PACKAGE_NAME}: route is undefined`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return root(() => {
|
|
108
|
+
request.data = {
|
|
109
|
+
parameters: state.parameters,
|
|
110
|
+
route: state.route
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return next(request);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return host;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalize(uri: string) {
|
|
122
|
+
if (uri[0] === '/') {
|
|
123
|
+
return '#' + uri;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return uri;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function onpopstate() {
|
|
130
|
+
let values = href();
|
|
131
|
+
|
|
132
|
+
for (let i = 0, n = cache.length; i < n; i++) {
|
|
133
|
+
let state = cache[i];
|
|
134
|
+
|
|
135
|
+
for (let key in values) {
|
|
136
|
+
// @ts-ignore
|
|
137
|
+
state[key] = values[key];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
const router = <const Factories extends readonly RouteFactory<any>[]>(...factories: Factories) => {
|
|
144
|
+
type Routes = AccumulateRoutes<Factories>;
|
|
145
|
+
type T = InferOutput<Factories[number]>;
|
|
146
|
+
|
|
147
|
+
let instance = factories.reduce(
|
|
148
|
+
(r, factory) => factory(r),
|
|
149
|
+
new Router<T, {}>() as Router<T, any>
|
|
150
|
+
) as Router<T, Routes>,
|
|
151
|
+
request = reactive<Request<T>>(Object.assign(href<T>(), { data: {} } as any));
|
|
152
|
+
|
|
153
|
+
if (cache.push(request) === 1) {
|
|
154
|
+
window.addEventListener('hashchange', onpopstate);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
back,
|
|
159
|
+
forward,
|
|
160
|
+
middleware: middleware(request, instance as Router<T>),
|
|
161
|
+
redirect: <RouteName extends keyof Routes>(
|
|
162
|
+
name: RouteName,
|
|
163
|
+
...values: ExtractRequiredParams<RoutePath<Routes, RouteName>> extends never
|
|
164
|
+
? ExtractOptionalParams<RoutePath<Routes, RouteName>> extends never
|
|
165
|
+
? []
|
|
166
|
+
: [params?: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
167
|
+
: [params: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
168
|
+
) => {
|
|
169
|
+
if ((name as string).indexOf('://') !== -1) {
|
|
170
|
+
return window.location.replace(name as any);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
window.location.hash = normalize(instance.uri(name as any, values as any));
|
|
174
|
+
},
|
|
175
|
+
routes: instance.routes,
|
|
176
|
+
uri: <RouteName extends keyof Routes>(
|
|
177
|
+
name: RouteName,
|
|
178
|
+
...values: ExtractRequiredParams<RoutePath<Routes, RouteName>> extends never
|
|
179
|
+
? ExtractOptionalParams<RoutePath<Routes, RouteName>> extends never
|
|
180
|
+
? []
|
|
181
|
+
: [params?: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
182
|
+
: [params: PathParamsObject<RoutePath<Routes, RouteName>>]
|
|
183
|
+
) => {
|
|
184
|
+
return normalize(instance.uri(name as any, values as any));
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
export { router };
|
|
191
|
+
export type {
|
|
192
|
+
Middleware,
|
|
193
|
+
Next,
|
|
194
|
+
Request, Route, Router, RouteFactory
|
|
195
|
+
} from './types';
|