@esportsplus/routing 0.2.7 → 0.3.1
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/.github/workflows/publish.yml +2 -2
- package/build/browser.d.ts +7 -6
- package/build/browser.js +10 -10
- package/build/router/index.d.ts +46 -11
- package/build/router/index.js +11 -6
- package/build/types.d.ts +13 -1
- package/package.json +8 -4
- package/src/browser.ts +22 -15
- package/src/router/index.ts +45 -24
- package/src/types.ts +45 -2
package/build/browser.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { Middleware, Next, Request, Route, Router } from './types.js';
|
|
1
|
+
import { Middleware, Next, PathParamsTuple, Request, Route, Router, RouteRegistry } from './types.js';
|
|
2
|
+
import { Router as RouterClass } from './router/index.js';
|
|
2
3
|
declare function back(): void;
|
|
3
4
|
declare function forward(): void;
|
|
4
|
-
declare const _default: <T>(instance?:
|
|
5
|
+
declare const _default: <T, TRoutes extends RouteRegistry = {}>(instance?: RouterClass<T, TRoutes>) => {
|
|
5
6
|
back: typeof back;
|
|
6
7
|
forward: typeof forward;
|
|
7
8
|
middleware: {
|
|
8
|
-
(...middleware: Middleware<T>[]): T
|
|
9
|
+
(...middleware: Middleware<T>[]): import("@esportsplus/pipeline").Pipeline<Request<T>, T>;
|
|
9
10
|
dispatch(request: Request<T>): T;
|
|
10
11
|
match(fallback: Route<T>): (request: Request<T>, next: Next<T>) => T;
|
|
11
12
|
};
|
|
12
|
-
redirect: (
|
|
13
|
-
router: Router<T>;
|
|
14
|
-
uri: (
|
|
13
|
+
redirect: <TName extends keyof TRoutes & string>(name: TName, values?: PathParamsTuple<TRoutes[TName]["path"]>) => void;
|
|
14
|
+
router: Router<T, TRoutes>;
|
|
15
|
+
uri: <TName extends keyof TRoutes & string>(name: TName, values?: PathParamsTuple<TRoutes[TName]["path"]>) => string;
|
|
15
16
|
};
|
|
16
17
|
export default _default;
|
package/build/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { effect, reactive, root } from '@esportsplus/reactivity';
|
|
2
|
-
import
|
|
2
|
+
import pipeline from '@esportsplus/pipeline';
|
|
3
3
|
import factory from './router/index.js';
|
|
4
4
|
let cache = [], location = window.location;
|
|
5
5
|
function back() {
|
|
@@ -10,8 +10,8 @@ function forward() {
|
|
|
10
10
|
}
|
|
11
11
|
function href() {
|
|
12
12
|
let hash = location.hash || '#/', path = hash ? hash.slice(1).split('?') : ['/', ''], request = {
|
|
13
|
-
href: location.href,
|
|
14
13
|
hostname: location.hostname,
|
|
14
|
+
href: location.href,
|
|
15
15
|
method: 'GET',
|
|
16
16
|
origin: location.origin,
|
|
17
17
|
path: path[0],
|
|
@@ -20,7 +20,7 @@ function href() {
|
|
|
20
20
|
query: {}
|
|
21
21
|
};
|
|
22
22
|
if (path[1]) {
|
|
23
|
-
let
|
|
23
|
+
let params = new URLSearchParams(path[1]), query = request.query;
|
|
24
24
|
for (let [key, value] of params.entries()) {
|
|
25
25
|
query[key] = value;
|
|
26
26
|
}
|
|
@@ -42,7 +42,7 @@ function match(request, router, subdomain) {
|
|
|
42
42
|
}
|
|
43
43
|
function middleware(request, router) {
|
|
44
44
|
function host(...middleware) {
|
|
45
|
-
return
|
|
45
|
+
return pipeline(middleware);
|
|
46
46
|
}
|
|
47
47
|
;
|
|
48
48
|
host.dispatch = (request) => {
|
|
@@ -104,15 +104,15 @@ export default (instance) => {
|
|
|
104
104
|
back,
|
|
105
105
|
forward,
|
|
106
106
|
middleware: middleware(request, router),
|
|
107
|
-
redirect: (
|
|
108
|
-
if (
|
|
109
|
-
return window.location.replace(
|
|
107
|
+
redirect: (name, values = []) => {
|
|
108
|
+
if (name.indexOf('://') !== -1) {
|
|
109
|
+
return window.location.replace(name);
|
|
110
110
|
}
|
|
111
|
-
window.location.hash = normalize(router.uri(
|
|
111
|
+
window.location.hash = normalize(router.uri(name, values));
|
|
112
112
|
},
|
|
113
113
|
router,
|
|
114
|
-
uri: (
|
|
115
|
-
return normalize(router.uri(
|
|
114
|
+
uri: (name, values = []) => {
|
|
115
|
+
return normalize(router.uri(name, values));
|
|
116
116
|
}
|
|
117
117
|
};
|
|
118
118
|
};
|
package/build/router/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Name, Options, Route, RouteOptions } from '../types.js';
|
|
1
|
+
import { Name, Options, PathParamsTuple, Route, RouteOptions, RouteRegistry } from '../types.js';
|
|
2
2
|
import { Node } from './node.js';
|
|
3
3
|
declare function key(method: string, subdomain?: string | null): string;
|
|
4
|
-
declare class Router<T> {
|
|
4
|
+
declare class Router<T, TRoutes extends RouteRegistry = {}> {
|
|
5
5
|
bucket: Record<ReturnType<typeof key>, {
|
|
6
6
|
root: Node<T>;
|
|
7
7
|
static: Record<string, Route<T>>;
|
|
@@ -10,22 +10,57 @@ declare class Router<T> {
|
|
|
10
10
|
routes: Record<Name, Route<T>>;
|
|
11
11
|
subdomains: string[] | null;
|
|
12
12
|
private add;
|
|
13
|
-
private
|
|
14
|
-
delete(options: RouteOptions<T>
|
|
15
|
-
|
|
13
|
+
private create;
|
|
14
|
+
delete<TName extends string = string, TPath extends string = string>(options: RouteOptions<T> & {
|
|
15
|
+
name?: TName;
|
|
16
|
+
path?: TPath;
|
|
17
|
+
}): Router<T, TRoutes & (TName extends string ? TPath extends string ? {
|
|
18
|
+
[K in TName]: {
|
|
19
|
+
path: TPath;
|
|
20
|
+
};
|
|
21
|
+
} : TRoutes : TRoutes)>;
|
|
22
|
+
get<TName extends string = string, TPath extends string = string>(options: RouteOptions<T> & {
|
|
23
|
+
name?: TName;
|
|
24
|
+
path?: TPath;
|
|
25
|
+
}): Router<T, TRoutes & (TName extends string ? TPath extends string ? {
|
|
26
|
+
[K in TName]: {
|
|
27
|
+
path: TPath;
|
|
28
|
+
};
|
|
29
|
+
} : TRoutes : TRoutes)>;
|
|
16
30
|
group(options: Options<T>): {
|
|
17
|
-
routes: (fn: (router: Router<T>) => void) =>
|
|
31
|
+
routes: (fn: (router: Router<T, TRoutes>) => void) => Router<T, TRoutes>;
|
|
18
32
|
};
|
|
19
33
|
match(method: string, path: string, subdomain?: string | null): {
|
|
20
34
|
parameters?: Readonly<Record<PropertyKey, unknown>>;
|
|
21
35
|
route?: Readonly<Route<T>> | undefined;
|
|
22
36
|
};
|
|
23
|
-
on(methods: string[], options: RouteOptions<T>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
on<TName extends string = string, TPath extends string = string>(methods: string[], options: RouteOptions<T> & {
|
|
38
|
+
name?: TName;
|
|
39
|
+
path?: TPath;
|
|
40
|
+
}): Router<T, TRoutes & (TName extends string ? TPath extends string ? {
|
|
41
|
+
[K in TName]: {
|
|
42
|
+
path: TPath;
|
|
43
|
+
};
|
|
44
|
+
} : TRoutes : TRoutes)>;
|
|
45
|
+
post<TName extends string = string, TPath extends string = string>(options: RouteOptions<T> & {
|
|
46
|
+
name?: TName;
|
|
47
|
+
path?: TPath;
|
|
48
|
+
}): Router<T, TRoutes & (TName extends string ? TPath extends string ? {
|
|
49
|
+
[K in TName]: {
|
|
50
|
+
path: TPath;
|
|
51
|
+
};
|
|
52
|
+
} : TRoutes : TRoutes)>;
|
|
53
|
+
put<TName extends string = string, TPath extends string = string>(options: RouteOptions<T> & {
|
|
54
|
+
name?: TName;
|
|
55
|
+
path?: TPath;
|
|
56
|
+
}): Router<T, TRoutes & (TName extends string ? TPath extends string ? {
|
|
57
|
+
[K in TName]: {
|
|
58
|
+
path: TPath;
|
|
59
|
+
};
|
|
60
|
+
} : TRoutes : TRoutes)>;
|
|
61
|
+
uri<TName extends keyof TRoutes & string>(name: TName, values?: PathParamsTuple<TRoutes[TName]['path']>): string;
|
|
27
62
|
}
|
|
28
|
-
declare const _default: <T>() => Router<T>;
|
|
63
|
+
declare const _default: <T>() => Router<T, {}>;
|
|
29
64
|
export default _default;
|
|
30
65
|
export { Router };
|
|
31
66
|
export type { Route };
|
package/build/router/index.js
CHANGED
|
@@ -53,7 +53,7 @@ class Router {
|
|
|
53
53
|
}
|
|
54
54
|
return this;
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
create(options) {
|
|
57
57
|
let groups = this.groups, route = {
|
|
58
58
|
name: null,
|
|
59
59
|
path: null,
|
|
@@ -73,10 +73,12 @@ class Router {
|
|
|
73
73
|
return route;
|
|
74
74
|
}
|
|
75
75
|
delete(options) {
|
|
76
|
-
|
|
76
|
+
this.on(ON_DELETE, options);
|
|
77
|
+
return this;
|
|
77
78
|
}
|
|
78
79
|
get(options) {
|
|
79
|
-
|
|
80
|
+
this.on(ON_GET, options);
|
|
81
|
+
return this;
|
|
80
82
|
}
|
|
81
83
|
group(options) {
|
|
82
84
|
return {
|
|
@@ -84,6 +86,7 @@ class Router {
|
|
|
84
86
|
this.groups.push(options);
|
|
85
87
|
fn(this);
|
|
86
88
|
this.groups.pop();
|
|
89
|
+
return this;
|
|
87
90
|
}
|
|
88
91
|
};
|
|
89
92
|
}
|
|
@@ -99,7 +102,7 @@ class Router {
|
|
|
99
102
|
return bucket.root.find(path);
|
|
100
103
|
}
|
|
101
104
|
on(methods, options) {
|
|
102
|
-
let route = this.
|
|
105
|
+
let route = this.create(options);
|
|
103
106
|
let name = route.name, path = route.path, subdomain = route.subdomain;
|
|
104
107
|
if (name) {
|
|
105
108
|
if (this.routes[name]) {
|
|
@@ -129,10 +132,12 @@ class Router {
|
|
|
129
132
|
return this;
|
|
130
133
|
}
|
|
131
134
|
post(options) {
|
|
132
|
-
|
|
135
|
+
this.on(ON_POST, options);
|
|
136
|
+
return this;
|
|
133
137
|
}
|
|
134
138
|
put(options) {
|
|
135
|
-
|
|
139
|
+
this.on(ON_PUT, options);
|
|
140
|
+
return this;
|
|
136
141
|
}
|
|
137
142
|
uri(name, values = []) {
|
|
138
143
|
let path = this.routes[name]?.path;
|
package/build/types.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { NeverAsync } from '@esportsplus/utilities';
|
|
2
2
|
import { Router } from './router/index.js';
|
|
3
3
|
import pipeline from '@esportsplus/pipeline';
|
|
4
|
+
type ExtractOptionalParamsTuple<Path extends string> = Path extends `${infer _Start}?:${infer Param}/${infer Rest}` ? [Param, ...ExtractOptionalParamsTuple<`/${Rest}`>] : Path extends `${infer _Start}?:${infer Param}` ? [Param] : [];
|
|
5
|
+
type ExtractParamsTuple<Path extends string> = Path extends `${infer _Start}:${infer Param}/${infer Rest}` ? [Param, ...ExtractParamsTuple<`/${Rest}`>] : Path extends `${infer _Start}:${infer Param}` ? [Param] : [];
|
|
6
|
+
type ExtractWildcard<Path extends string> = Path extends `${string}*:${infer Param}` ? Param : never;
|
|
7
|
+
type LabeledParamsTuple<Params extends string[]> = Params extends [infer _First extends string, ...infer Rest extends string[]] ? [_First: string | number, ...LabeledParamsTuple<Rest>] : [];
|
|
4
8
|
type Middleware<T> = NeverAsync<(input: Request<T>, next: Next<T>) => T>;
|
|
5
9
|
type Name = string;
|
|
6
10
|
type Next<T> = NeverAsync<(input: Request<T>) => T>;
|
|
@@ -10,6 +14,11 @@ type Options<T> = {
|
|
|
10
14
|
path?: string;
|
|
11
15
|
subdomain?: string;
|
|
12
16
|
};
|
|
17
|
+
type PathParamsTuple<Path extends string> = LabeledParamsTuple<ExtractParamsTuple<Path>>;
|
|
18
|
+
type PathParamsTupleWithOptional<Path extends string> = [
|
|
19
|
+
...LabeledParamsTuple<ExtractParamsTuple<Path>>,
|
|
20
|
+
...Partial<LabeledParamsTuple<ExtractOptionalParamsTuple<Path>>>
|
|
21
|
+
];
|
|
13
22
|
type Request<T> = {
|
|
14
23
|
data: Record<PropertyKey, unknown> & ReturnType<Router<T>['match']>;
|
|
15
24
|
href: string;
|
|
@@ -31,4 +40,7 @@ type Route<T> = {
|
|
|
31
40
|
type RouteOptions<T> = Options<T> & {
|
|
32
41
|
responder: Next<T>;
|
|
33
42
|
};
|
|
34
|
-
|
|
43
|
+
type RouteRegistry = Record<string, {
|
|
44
|
+
path: string;
|
|
45
|
+
}>;
|
|
46
|
+
export type { ExtractOptionalParamsTuple, ExtractParamsTuple, ExtractWildcard, LabeledParamsTuple, Middleware, Name, Next, Options, PathParamsTuple, PathParamsTupleWithOptional, Request, Route, RouteOptions, Router, RouteRegistry };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
|
-
"@esportsplus/pipeline": "^1.
|
|
5
|
-
"@esportsplus/reactivity": "^0.
|
|
6
|
-
"@esportsplus/utilities": "^0.
|
|
4
|
+
"@esportsplus/pipeline": "^1.2.0",
|
|
5
|
+
"@esportsplus/reactivity": "^0.22.1",
|
|
6
|
+
"@esportsplus/utilities": "^0.26.0"
|
|
7
7
|
},
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@esportsplus/typescript": "^0.9.2"
|
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
"main": "./build/index.js",
|
|
12
12
|
"name": "@esportsplus/routing",
|
|
13
13
|
"private": false,
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/esportsplus/routing"
|
|
17
|
+
},
|
|
14
18
|
"type": "module",
|
|
15
19
|
"sideEffects": false,
|
|
16
20
|
"types": "./build/index.d.ts",
|
|
17
|
-
"version": "0.
|
|
21
|
+
"version": "0.3.1",
|
|
18
22
|
"scripts": {
|
|
19
23
|
"build": "tsc && tsc-alias",
|
|
20
24
|
"-": "-",
|
package/src/browser.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { effect, reactive, root } from '@esportsplus/reactivity';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import pipeline from '@esportsplus/pipeline';
|
|
3
|
+
import { Middleware, Next, PathParamsTuple, Request, Route, Router, RouteRegistry } from './types';
|
|
4
|
+
import { Router as RouterClass } from './router';
|
|
4
5
|
import factory from './router';
|
|
5
6
|
|
|
6
7
|
|
|
@@ -20,8 +21,8 @@ function href<T>() {
|
|
|
20
21
|
let hash = location.hash || '#/',
|
|
21
22
|
path = hash ? hash.slice(1).split('?') : ['/', ''],
|
|
22
23
|
request = {
|
|
23
|
-
href: location.href,
|
|
24
24
|
hostname: location.hostname,
|
|
25
|
+
href: location.href,
|
|
25
26
|
method: 'GET',
|
|
26
27
|
origin: location.origin,
|
|
27
28
|
path: path[0],
|
|
@@ -31,8 +32,8 @@ function href<T>() {
|
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
if (path[1]) {
|
|
34
|
-
let
|
|
35
|
-
|
|
35
|
+
let params = new URLSearchParams(path[1]),
|
|
36
|
+
query = request.query;
|
|
36
37
|
|
|
37
38
|
for (let [key, value] of params.entries()) {
|
|
38
39
|
query[key] = value;
|
|
@@ -62,7 +63,7 @@ function match<T>(request: Request<T>, router: Router<T>, subdomain?: string) {
|
|
|
62
63
|
|
|
63
64
|
function middleware<T>(request: Request<T>, router: Router<T>) {
|
|
64
65
|
function host(...middleware: Middleware<T>[]) {
|
|
65
|
-
return
|
|
66
|
+
return pipeline(middleware);
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
host.dispatch = (request: Request<T>) => {
|
|
@@ -133,9 +134,9 @@ function onpopstate() {
|
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
|
|
136
|
-
export default <T>(instance?:
|
|
137
|
+
export default <T, TRoutes extends RouteRegistry = {}>(instance?: RouterClass<T, TRoutes>) => {
|
|
137
138
|
let request = reactive( Object.assign(href<T>(), { data: {} } as any) as Request<T> ),
|
|
138
|
-
router = instance || factory<T>()
|
|
139
|
+
router = instance || factory<T>() as RouterClass<T, TRoutes>;
|
|
139
140
|
|
|
140
141
|
if (cache.push(request) === 1) {
|
|
141
142
|
window.addEventListener('hashchange', onpopstate);
|
|
@@ -144,17 +145,23 @@ export default <T>(instance?: Router<T>) => {
|
|
|
144
145
|
return {
|
|
145
146
|
back,
|
|
146
147
|
forward,
|
|
147
|
-
middleware: middleware(request, router),
|
|
148
|
-
redirect:
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
middleware: middleware(request, router as Router<T>),
|
|
149
|
+
redirect: <TName extends keyof TRoutes & string>(
|
|
150
|
+
name: TName,
|
|
151
|
+
values: PathParamsTuple<TRoutes[TName]['path']> = [] as any
|
|
152
|
+
) => {
|
|
153
|
+
if ((name as string).indexOf('://') !== -1) {
|
|
154
|
+
return window.location.replace(name);
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
window.location.hash = normalize( router.uri(
|
|
157
|
+
window.location.hash = normalize( router.uri(name, values) );
|
|
154
158
|
},
|
|
155
159
|
router,
|
|
156
|
-
uri:
|
|
157
|
-
|
|
160
|
+
uri: <TName extends keyof TRoutes & string>(
|
|
161
|
+
name: TName,
|
|
162
|
+
values: PathParamsTuple<TRoutes[TName]['path']> = [] as any
|
|
163
|
+
) => {
|
|
164
|
+
return normalize( router.uri(name, values) );
|
|
158
165
|
}
|
|
159
166
|
};
|
|
160
167
|
};
|
package/src/router/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ON_DELETE, ON_GET, ON_POST, ON_PUT } from '../constants';
|
|
2
|
-
import { Name, Options, Request, Route, RouteOptions } from '../types';
|
|
2
|
+
import { Name, Options, PathParamsTuple, Request, Route, RouteOptions, RouteRegistry } from '../types';
|
|
3
3
|
import { Node } from './node';
|
|
4
4
|
import pipeline from '@esportsplus/pipeline';
|
|
5
5
|
|
|
@@ -44,7 +44,7 @@ function set<T>(route: Route<T>, options: Options<T> | RouteOptions<T>) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class Router<T> {
|
|
47
|
+
class Router<T, TRoutes extends RouteRegistry = {}> {
|
|
48
48
|
bucket: Record<ReturnType<typeof key>, { root: Node<T>, static: Record<string, Route<T>> }> = {};
|
|
49
49
|
groups: Options<T>[] = [];
|
|
50
50
|
routes: Record<Name, Route<T>> = {};
|
|
@@ -71,7 +71,7 @@ class Router<T> {
|
|
|
71
71
|
return this;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
private
|
|
74
|
+
private create(options: RouteOptions<T>) {
|
|
75
75
|
let groups = this.groups,
|
|
76
76
|
route: Route<T> = {
|
|
77
77
|
name: null,
|
|
@@ -98,22 +98,31 @@ class Router<T> {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
delete
|
|
102
|
-
|
|
101
|
+
delete<TName extends string = string, TPath extends string = string>(
|
|
102
|
+
options: RouteOptions<T> & { name?: TName; path?: TPath }
|
|
103
|
+
): Router<T, TRoutes & (TName extends string ? TPath extends string ? { [K in TName]: { path: TPath } } : TRoutes : TRoutes)> {
|
|
104
|
+
this.on(ON_DELETE, options);
|
|
105
|
+
return this as any;
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
get
|
|
106
|
-
|
|
108
|
+
get<TName extends string = string, TPath extends string = string>(
|
|
109
|
+
options: RouteOptions<T> & { name?: TName; path?: TPath }
|
|
110
|
+
): Router<T, TRoutes & (TName extends string ? TPath extends string ? { [K in TName]: { path: TPath } } : TRoutes : TRoutes)> {
|
|
111
|
+
this.on(ON_GET, options);
|
|
112
|
+
return this as any;
|
|
107
113
|
}
|
|
108
114
|
|
|
109
|
-
group(options: Options<T>) {
|
|
115
|
+
group(options: Options<T>): {
|
|
116
|
+
routes: (fn: (router: Router<T, TRoutes>) => void) => Router<T, TRoutes>
|
|
117
|
+
} {
|
|
110
118
|
return {
|
|
111
|
-
routes: (fn: (router: Router<T>) => void) => {
|
|
119
|
+
routes: (fn: (router: Router<T, TRoutes>) => void) => {
|
|
112
120
|
this.groups.push(options);
|
|
113
121
|
fn(this);
|
|
114
122
|
this.groups.pop();
|
|
123
|
+
return this;
|
|
115
124
|
}
|
|
116
|
-
}
|
|
125
|
+
};
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
match(method: string, path: string, subdomain?: string | null) {
|
|
@@ -132,8 +141,11 @@ class Router<T> {
|
|
|
132
141
|
return bucket.root.find(path);
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
on
|
|
136
|
-
|
|
144
|
+
on<TName extends string = string, TPath extends string = string>(
|
|
145
|
+
methods: string[],
|
|
146
|
+
options: RouteOptions<T> & { name?: TName; path?: TPath }
|
|
147
|
+
): Router<T, TRoutes & (TName extends string ? TPath extends string ? { [K in TName]: { path: TPath } } : TRoutes : TRoutes)> {
|
|
148
|
+
let route = this.create(options);
|
|
137
149
|
|
|
138
150
|
let name = route.name,
|
|
139
151
|
path = route.path,
|
|
@@ -172,25 +184,34 @@ class Router<T> {
|
|
|
172
184
|
(this.subdomains ??= []).push( subdomain.toLowerCase() );
|
|
173
185
|
}
|
|
174
186
|
|
|
175
|
-
return this;
|
|
187
|
+
return this as any;
|
|
176
188
|
}
|
|
177
189
|
|
|
178
|
-
post
|
|
179
|
-
|
|
190
|
+
post<TName extends string = string, TPath extends string = string>(
|
|
191
|
+
options: RouteOptions<T> & { name?: TName; path?: TPath }
|
|
192
|
+
): Router<T, TRoutes & (TName extends string ? TPath extends string ? { [K in TName]: { path: TPath } } : TRoutes : TRoutes)> {
|
|
193
|
+
this.on(ON_POST, options);
|
|
194
|
+
return this as any;
|
|
180
195
|
}
|
|
181
196
|
|
|
182
|
-
put
|
|
183
|
-
|
|
197
|
+
put<TName extends string = string, TPath extends string = string>(
|
|
198
|
+
options: RouteOptions<T> & { name?: TName; path?: TPath }
|
|
199
|
+
): Router<T, TRoutes & (TName extends string ? TPath extends string ? { [K in TName]: { path: TPath } } : TRoutes : TRoutes)> {
|
|
200
|
+
this.on(ON_PUT, options);
|
|
201
|
+
return this as any;
|
|
184
202
|
}
|
|
185
203
|
|
|
186
|
-
uri
|
|
204
|
+
uri<TName extends keyof TRoutes & string>(
|
|
205
|
+
name: TName,
|
|
206
|
+
values: PathParamsTuple<TRoutes[TName]['path']> = [] as any
|
|
207
|
+
): string {
|
|
187
208
|
let path = this.routes[name]?.path;
|
|
188
209
|
|
|
189
210
|
if (!path) {
|
|
190
211
|
throw new Error(`Routing: route name '${name}' does not exist or it does not provide a path`);
|
|
191
212
|
}
|
|
192
213
|
|
|
193
|
-
let resolved
|
|
214
|
+
let resolved: (string | number)[] = [],
|
|
194
215
|
segments = path.split('/'),
|
|
195
216
|
v = 0;
|
|
196
217
|
|
|
@@ -199,18 +220,18 @@ class Router<T> {
|
|
|
199
220
|
symbol = segment[0];
|
|
200
221
|
|
|
201
222
|
if (symbol === ':') {
|
|
202
|
-
resolved.push(values[v++]);
|
|
223
|
+
resolved.push((values as (string | number)[])[v++]);
|
|
203
224
|
}
|
|
204
225
|
else if (symbol === '?') {
|
|
205
|
-
if (values[v] === undefined) {
|
|
226
|
+
if ((values as (string | number)[])[v] === undefined) {
|
|
206
227
|
break;
|
|
207
228
|
}
|
|
208
229
|
|
|
209
|
-
resolved.push(values[v++]);
|
|
230
|
+
resolved.push((values as (string | number)[])[v++]);
|
|
210
231
|
}
|
|
211
232
|
else if (symbol === '*') {
|
|
212
233
|
for (let n = values.length; v < n; v++) {
|
|
213
|
-
resolved.push(
|
|
234
|
+
resolved.push((values as (string | number)[])[v]);
|
|
214
235
|
}
|
|
215
236
|
break;
|
|
216
237
|
}
|
|
@@ -224,6 +245,6 @@ class Router<T> {
|
|
|
224
245
|
}
|
|
225
246
|
|
|
226
247
|
|
|
227
|
-
export default <T>() => new Router<T>();
|
|
248
|
+
export default <T>() => new Router<T, {}>();
|
|
228
249
|
export { Router };
|
|
229
250
|
export type { Route };
|
package/src/types.ts
CHANGED
|
@@ -3,6 +3,30 @@ import { Router } from './router';
|
|
|
3
3
|
import pipeline from '@esportsplus/pipeline';
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
type ExtractOptionalParamsTuple<Path extends string> =
|
|
7
|
+
Path extends `${infer _Start}?:${infer Param}/${infer Rest}`
|
|
8
|
+
? [Param, ...ExtractOptionalParamsTuple<`/${Rest}`>]
|
|
9
|
+
: Path extends `${infer _Start}?:${infer Param}`
|
|
10
|
+
? [Param]
|
|
11
|
+
: [];
|
|
12
|
+
|
|
13
|
+
type ExtractParamsTuple<Path extends string> =
|
|
14
|
+
Path extends `${infer _Start}:${infer Param}/${infer Rest}`
|
|
15
|
+
? [Param, ...ExtractParamsTuple<`/${Rest}`>]
|
|
16
|
+
: Path extends `${infer _Start}:${infer Param}`
|
|
17
|
+
? [Param]
|
|
18
|
+
: [];
|
|
19
|
+
|
|
20
|
+
type ExtractWildcard<Path extends string> =
|
|
21
|
+
Path extends `${string}*:${infer Param}`
|
|
22
|
+
? Param
|
|
23
|
+
: never;
|
|
24
|
+
|
|
25
|
+
type LabeledParamsTuple<Params extends string[]> =
|
|
26
|
+
Params extends [infer _First extends string, ...infer Rest extends string[]]
|
|
27
|
+
? [_First: string | number, ...LabeledParamsTuple<Rest>]
|
|
28
|
+
: [];
|
|
29
|
+
|
|
6
30
|
type Middleware<T> = NeverAsync<(input: Request<T>, next: Next<T>) => T>;
|
|
7
31
|
|
|
8
32
|
type Name = string;
|
|
@@ -16,6 +40,12 @@ type Options<T> = {
|
|
|
16
40
|
subdomain?: string;
|
|
17
41
|
};
|
|
18
42
|
|
|
43
|
+
type PathParamsTuple<Path extends string> =
|
|
44
|
+
LabeledParamsTuple<ExtractParamsTuple<Path>>;
|
|
45
|
+
|
|
46
|
+
type PathParamsTupleWithOptional<Path extends string> =
|
|
47
|
+
[...LabeledParamsTuple<ExtractParamsTuple<Path>>, ...Partial<LabeledParamsTuple<ExtractOptionalParamsTuple<Path>>>];
|
|
48
|
+
|
|
19
49
|
type Request<T> = {
|
|
20
50
|
data: Record<PropertyKey, unknown> & ReturnType<Router<T>['match']>;
|
|
21
51
|
href: string;
|
|
@@ -40,10 +70,23 @@ type RouteOptions<T> = Options<T> & {
|
|
|
40
70
|
responder: Next<T>;
|
|
41
71
|
};
|
|
42
72
|
|
|
73
|
+
type RouteRegistry = Record<string, { path: string }>;
|
|
74
|
+
|
|
43
75
|
|
|
44
76
|
export type {
|
|
77
|
+
ExtractOptionalParamsTuple,
|
|
78
|
+
ExtractParamsTuple,
|
|
79
|
+
ExtractWildcard,
|
|
80
|
+
LabeledParamsTuple,
|
|
45
81
|
Middleware,
|
|
46
|
-
Name,
|
|
82
|
+
Name,
|
|
83
|
+
Next,
|
|
47
84
|
Options,
|
|
48
|
-
|
|
85
|
+
PathParamsTuple,
|
|
86
|
+
PathParamsTupleWithOptional,
|
|
87
|
+
Request,
|
|
88
|
+
Route,
|
|
89
|
+
RouteOptions,
|
|
90
|
+
Router,
|
|
91
|
+
RouteRegistry
|
|
49
92
|
};
|