valyrian.js 7.2.11 → 8.0.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/README.md +6 -6
- package/dist/flux-store/index.d.ts +32 -0
- package/dist/flux-store/index.d.ts.map +1 -0
- package/dist/flux-store/index.js +258 -0
- package/dist/flux-store/index.js.map +7 -0
- package/dist/flux-store/index.min.js +1 -0
- package/dist/flux-store/index.min.js.map +1 -0
- package/dist/flux-store/index.mjs +237 -0
- package/dist/flux-store/index.mjs.map +7 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +42 -75
- package/dist/hooks/index.js.map +2 -2
- package/dist/hooks/index.min.js +1 -0
- package/dist/hooks/index.min.js.map +1 -0
- package/dist/hooks/index.mjs +43 -76
- package/dist/hooks/index.mjs.map +2 -2
- package/dist/index.d.ts +52 -54
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +397 -328
- package/dist/index.js.map +3 -3
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.mjs +397 -328
- package/dist/index.mjs.map +3 -3
- package/dist/native-store/index.d.ts +14 -0
- package/dist/native-store/index.d.ts.map +1 -0
- package/dist/native-store/index.js +103 -0
- package/dist/native-store/index.js.map +7 -0
- package/dist/native-store/index.min.js +1 -0
- package/dist/native-store/index.min.js.map +1 -0
- package/dist/native-store/index.mjs +82 -0
- package/dist/native-store/index.mjs.map +7 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +223 -86
- package/dist/node/index.js.map +4 -4
- package/dist/node/index.mjs +223 -86
- package/dist/node/index.mjs.map +4 -4
- package/dist/node/node.sw.js +152 -0
- package/dist/node/utils/inline.d.ts.map +1 -1
- package/dist/node/utils/node.sw.js +152 -0
- package/dist/node/utils/session-storage.d.ts +22 -0
- package/dist/node/utils/session-storage.d.ts.map +1 -0
- package/dist/node/utils/sw.d.ts.map +1 -1
- package/dist/node/utils/tree-adapter.d.ts +9 -0
- package/dist/node/utils/tree-adapter.d.ts.map +1 -1
- package/dist/pulse-store/index.d.ts +13 -0
- package/dist/pulse-store/index.d.ts.map +1 -0
- package/dist/pulse-store/index.js +143 -0
- package/dist/pulse-store/index.js.map +7 -0
- package/dist/pulse-store/index.min.js +1 -0
- package/dist/pulse-store/index.min.js.map +1 -0
- package/dist/pulse-store/index.mjs +122 -0
- package/dist/pulse-store/index.mjs.map +7 -0
- package/dist/request/index.d.ts.map +1 -1
- package/dist/request/index.js +68 -89
- package/dist/request/index.js.map +2 -2
- package/dist/request/index.min.js +1 -0
- package/dist/request/index.min.js.map +1 -0
- package/dist/request/index.mjs +68 -89
- package/dist/request/index.mjs.map +2 -2
- package/dist/router/index.d.ts +32 -31
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +256 -104
- package/dist/router/index.js.map +3 -3
- package/dist/router/index.min.js +1 -0
- package/dist/router/index.min.js.map +1 -0
- package/dist/router/index.mjs +256 -104
- package/dist/router/index.mjs.map +3 -3
- package/dist/signals/index.d.ts +6 -0
- package/dist/signals/index.d.ts.map +1 -0
- package/dist/signals/index.js +92 -0
- package/dist/signals/index.js.map +7 -0
- package/dist/signals/index.min.js +1 -0
- package/dist/signals/index.min.js.map +1 -0
- package/dist/signals/index.mjs +71 -0
- package/dist/signals/index.mjs.map +7 -0
- package/dist/suspense/index.d.ts +6 -0
- package/dist/suspense/index.d.ts.map +1 -0
- package/dist/suspense/index.js +67 -0
- package/dist/suspense/index.js.map +7 -0
- package/dist/suspense/index.min.js +1 -0
- package/dist/suspense/index.min.js.map +1 -0
- package/dist/suspense/index.mjs +46 -0
- package/dist/suspense/index.mjs.map +7 -0
- package/dist/sw/index.min.js +1 -0
- package/dist/sw/index.min.js.map +1 -0
- package/dist/translate/index.d.ts +19 -0
- package/dist/translate/index.d.ts.map +1 -0
- package/dist/translate/index.js +150 -0
- package/dist/translate/index.js.map +7 -0
- package/dist/translate/index.min.js +1 -0
- package/dist/translate/index.min.js.map +1 -0
- package/dist/translate/index.mjs +129 -0
- package/dist/translate/index.mjs.map +7 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/deep-freeze.d.ts +3 -0
- package/dist/utils/deep-freeze.d.ts.map +1 -0
- package/dist/utils/getter-setter.d.ts +3 -0
- package/dist/utils/getter-setter.d.ts.map +1 -0
- package/dist/utils/has-changed.d.ts +2 -0
- package/dist/utils/has-changed.d.ts.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +138 -0
- package/dist/utils/index.js.map +7 -0
- package/dist/utils/index.min.js +1 -0
- package/dist/utils/index.min.js.map +1 -0
- package/dist/utils/index.mjs +115 -0
- package/dist/utils/index.mjs.map +7 -0
- package/lib/flux-store/index.ts +301 -0
- package/lib/hooks/index.ts +52 -101
- package/lib/index.ts +479 -719
- package/lib/native-store/index.ts +106 -0
- package/lib/node/index.ts +5 -3
- package/lib/node/utils/icons.ts +5 -5
- package/lib/node/utils/inline.ts +17 -17
- package/lib/node/utils/node.sw.js +152 -0
- package/lib/node/utils/session-storage.ts +117 -0
- package/lib/node/utils/sw.ts +35 -11
- package/lib/node/utils/tree-adapter.ts +99 -52
- package/lib/pulse-store/index.ts +181 -0
- package/lib/request/index.ts +86 -116
- package/lib/router/index.ts +358 -170
- package/lib/signals/index.ts +98 -0
- package/lib/suspense/index.ts +57 -0
- package/lib/translate/index.ts +156 -0
- package/lib/utils/deep-freeze.ts +54 -0
- package/lib/utils/getter-setter.ts +40 -0
- package/lib/utils/has-changed.ts +43 -0
- package/lib/utils/index.ts +3 -0
- package/package.json +38 -50
- package/tsconfig.json +1 -1
- package/dist/dataset/index.d.ts +0 -24
- package/dist/dataset/index.d.ts.map +0 -1
- package/dist/dataset/index.js +0 -178
- package/dist/dataset/index.js.map +0 -7
- package/dist/dataset/index.mjs +0 -157
- package/dist/dataset/index.mjs.map +0 -7
- package/dist/node/node.sw.tpl +0 -133
- package/dist/node/utils/node.sw.tpl +0 -133
- package/dist/proxy-signal/index.d.ts +0 -23
- package/dist/proxy-signal/index.d.ts.map +0 -1
- package/dist/proxy-signal/index.js +0 -138
- package/dist/proxy-signal/index.js.map +0 -7
- package/dist/proxy-signal/index.mjs +0 -117
- package/dist/proxy-signal/index.mjs.map +0 -7
- package/dist/signal/index.d.ts +0 -20
- package/dist/signal/index.d.ts.map +0 -1
- package/dist/signal/index.js +0 -95
- package/dist/signal/index.js.map +0 -7
- package/dist/signal/index.mjs +0 -74
- package/dist/signal/index.mjs.map +0 -7
- package/dist/store/index.d.ts +0 -16
- package/dist/store/index.d.ts.map +0 -1
- package/dist/store/index.js +0 -93
- package/dist/store/index.js.map +0 -7
- package/dist/store/index.mjs +0 -72
- package/dist/store/index.mjs.map +0 -7
- package/lib/dataset/index.ts +0 -193
- package/lib/index.d.ts +0 -0
- package/lib/interfaces.ts.bak +0 -141
- package/lib/node/utils/node.sw.tpl +0 -133
- package/lib/proxy-signal/index.ts +0 -187
- package/lib/signal/index.ts +0 -161
- package/lib/store/index.ts +0 -101
package/lib/router/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
/* eslint-disable no-use-before-define */
|
|
2
3
|
import {
|
|
3
4
|
Component,
|
|
@@ -20,12 +21,12 @@ interface Request {
|
|
|
20
21
|
path: string;
|
|
21
22
|
matches: string[];
|
|
22
23
|
// eslint-disable-next-line no-unused-vars
|
|
23
|
-
redirect: (path: string
|
|
24
|
+
redirect: (path: string) => Promise<string | void>;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
interface Middleware {
|
|
27
28
|
// eslint-disable-next-line no-unused-vars
|
|
28
|
-
(req: Request,
|
|
29
|
+
(req: Request, err?: any):
|
|
29
30
|
| Promise<any | Component | POJOComponent | VnodeComponentInterface>
|
|
30
31
|
| any
|
|
31
32
|
| Component
|
|
@@ -35,39 +36,13 @@ interface Middleware {
|
|
|
35
36
|
|
|
36
37
|
interface Middlewares extends Array<Middleware> {}
|
|
37
38
|
|
|
38
|
-
interface Path {
|
|
39
|
-
method: string;
|
|
40
|
-
path: string;
|
|
41
|
-
middlewares: Middlewares;
|
|
42
|
-
params: string[];
|
|
43
|
-
regexp: RegExp;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface RouterInterface {
|
|
47
|
-
paths: Path[];
|
|
48
|
-
container: Element | string | null;
|
|
49
|
-
query: Record<string, string | number>;
|
|
50
|
-
options: Record<string, any>;
|
|
51
|
-
url: string;
|
|
52
|
-
path: string;
|
|
53
|
-
params: Record<string, string | number | any>;
|
|
54
|
-
matches: string[];
|
|
55
|
-
pathPrefix: string;
|
|
56
|
-
// eslint-disable-next-line no-unused-vars
|
|
57
|
-
add(method: string, ...args: Middlewares): Router;
|
|
58
|
-
// eslint-disable-next-line no-unused-vars
|
|
59
|
-
use(...args: (string | Middleware | Router)[]): Router;
|
|
60
|
-
|
|
61
|
-
routes(): string[];
|
|
62
|
-
// eslint-disable-next-line no-unused-vars
|
|
63
|
-
go(path: string, parentComponent?: Component | POJOComponent | VnodeComponentInterface): Promise<string | void>;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
39
|
interface RedirectFunction {
|
|
67
|
-
// eslint-disable-next-line no-unused-vars
|
|
68
40
|
(
|
|
41
|
+
// eslint-disable-next-line no-unused-vars
|
|
69
42
|
path: string,
|
|
43
|
+
// eslint-disable-next-line no-unused-vars
|
|
70
44
|
parentComponent?: Component | POJOComponent | VnodeComponentInterface,
|
|
45
|
+
// eslint-disable-next-line no-unused-vars
|
|
71
46
|
preventPushState?: boolean
|
|
72
47
|
): Promise<string | void>;
|
|
73
48
|
}
|
|
@@ -88,132 +63,121 @@ function getPathWithoutLastSlash(path: string) {
|
|
|
88
63
|
return pathWithoutLastSlash;
|
|
89
64
|
}
|
|
90
65
|
|
|
91
|
-
const addPath = ({
|
|
92
|
-
router,
|
|
93
|
-
method,
|
|
94
|
-
path,
|
|
95
|
-
middlewares
|
|
96
|
-
}: {
|
|
97
|
-
router: Router;
|
|
98
|
-
method: string;
|
|
99
|
-
path: string;
|
|
100
|
-
middlewares: Middleware[];
|
|
101
|
-
}): void => {
|
|
102
|
-
if (!method || !path || !Array.isArray(middlewares) || middlewares.length === 0) {
|
|
103
|
-
throw new Error(`Invalid route input: ${method} ${path} ${middlewares}`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Trim trailing slashes from the path
|
|
107
|
-
let realpath = path.replace(/(\S)(\/+)$/, "$1");
|
|
108
|
-
|
|
109
|
-
// Find the express-like params in the path
|
|
110
|
-
let params = (realpath.match(/:(\w+)?/gi) || [])
|
|
111
|
-
// Set the names of the params found
|
|
112
|
-
.map((param) => param.slice(1));
|
|
113
|
-
|
|
114
|
-
// Generate a regular expression to match the path
|
|
115
|
-
let regexpPath = "^" + realpath.replace(/:(\w+)/gi, "([^\\/\\s]+)") + "$";
|
|
116
|
-
|
|
117
|
-
router.paths.push({
|
|
118
|
-
method,
|
|
119
|
-
path: realpath,
|
|
120
|
-
middlewares: flat(middlewares),
|
|
121
|
-
params,
|
|
122
|
-
regexp: new RegExp(regexpPath, "i")
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
|
|
126
66
|
// Parse a query string into an object
|
|
127
|
-
function parseQuery(queryParts?: string): Record<string,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
query[name] = value || "";
|
|
67
|
+
function parseQuery(queryParts?: string): Record<string, any> {
|
|
68
|
+
const parts = queryParts ? queryParts.split("&") : [];
|
|
69
|
+
const query: Record<string, any> = {};
|
|
70
|
+
|
|
71
|
+
for (const nameValue of parts) {
|
|
72
|
+
const [name, value] = nameValue.split("=", 2);
|
|
73
|
+
query[name] =
|
|
74
|
+
isNaN(Number(value)) === false ? Number(value) : value === "true" ? true : value === "false" ? false : value;
|
|
136
75
|
}
|
|
137
76
|
|
|
138
77
|
return query;
|
|
139
78
|
}
|
|
140
79
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
80
|
+
interface RouteNode {
|
|
81
|
+
segment: string;
|
|
82
|
+
children: Map<string, RouteNode>;
|
|
83
|
+
middlewares?: Middlewares;
|
|
84
|
+
paramKey?: string;
|
|
85
|
+
isDynamic: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class RouteTree {
|
|
89
|
+
root: RouteNode = { segment: "", children: new Map(), isDynamic: false };
|
|
146
90
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let
|
|
91
|
+
addRoute(path: string, middlewares: Middlewares) {
|
|
92
|
+
const segments = path === "/" ? [path] : path.split("/").filter(Boolean); // Divide the path into segments
|
|
93
|
+
let currentNode = this.root;
|
|
150
94
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
match.shift();
|
|
95
|
+
for (const segment of segments) {
|
|
96
|
+
const isDynamic = segment.startsWith(":");
|
|
97
|
+
const key = isDynamic ? ":" : segment; // If the segment is dynamic, use ":" as key
|
|
155
98
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
99
|
+
if (!currentNode.children.has(key)) {
|
|
100
|
+
currentNode.children.set(key, {
|
|
101
|
+
segment: segment,
|
|
102
|
+
children: new Map(),
|
|
103
|
+
isDynamic: isDynamic,
|
|
104
|
+
paramKey: isDynamic ? segment.slice(1) : undefined
|
|
105
|
+
});
|
|
159
106
|
}
|
|
160
107
|
|
|
161
|
-
|
|
162
|
-
|
|
108
|
+
currentNode = currentNode.children.get(key)!;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
currentNode.middlewares = middlewares; // Assign the middlewares to the last node
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Search for a route in the tree
|
|
115
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
116
|
+
findRoute(path: string): { middlewares?: Middlewares; params: Record<string, string> } | null {
|
|
117
|
+
const pathWithoutLastSlash = getPathWithoutLastSlash(path);
|
|
118
|
+
const segments =
|
|
119
|
+
pathWithoutLastSlash === "/" ? [pathWithoutLastSlash] : pathWithoutLastSlash.split("/").filter(Boolean);
|
|
120
|
+
let currentNode: RouteNode | null = this.root;
|
|
121
|
+
const params: Record<string, string> = {};
|
|
122
|
+
|
|
123
|
+
const wildcardMiddlewares: Middlewares = []; // Middlewares for wildcard routes
|
|
124
|
+
const segmentsLength = segments.length;
|
|
163
125
|
|
|
164
|
-
|
|
165
|
-
|
|
126
|
+
for (let i = 0; i < segmentsLength; i++) {
|
|
127
|
+
if (!currentNode) {
|
|
166
128
|
break;
|
|
167
129
|
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
130
|
|
|
171
|
-
|
|
172
|
-
router.matches = matches;
|
|
131
|
+
const segment = segments[i];
|
|
173
132
|
|
|
174
|
-
|
|
175
|
-
}
|
|
133
|
+
let found = false;
|
|
176
134
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
params: router.params,
|
|
184
|
-
query: router.query,
|
|
185
|
-
url: router.url,
|
|
186
|
-
path: router.path,
|
|
187
|
-
matches: router.matches,
|
|
188
|
-
redirect: (path: string, parentComponent?: Component | POJOComponent | VnodeComponentInterface) => {
|
|
189
|
-
router.go(path, parentComponent);
|
|
190
|
-
// Return false to stop the middleware chain
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
135
|
+
for (const [key, child] of currentNode.children) {
|
|
136
|
+
if (key === segment) {
|
|
137
|
+
currentNode = child;
|
|
138
|
+
found = true;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
194
141
|
|
|
195
|
-
|
|
196
|
-
|
|
142
|
+
if (segment !== ".*" && key === ":") {
|
|
143
|
+
currentNode = child;
|
|
144
|
+
params[child.paramKey!] = segment;
|
|
145
|
+
found = true;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
197
148
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
149
|
+
if (key === ".*" && !found) {
|
|
150
|
+
wildcardMiddlewares.push(...(child.middlewares || []));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
202
153
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
154
|
+
if (!found) {
|
|
155
|
+
if (currentNode.children.has(".*")) {
|
|
156
|
+
return { middlewares: wildcardMiddlewares, params };
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
206
160
|
}
|
|
207
161
|
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
162
|
+
// Add the wildcard middlewares to the current node middlewares
|
|
163
|
+
const allMiddlewares = [...wildcardMiddlewares, ...(currentNode.middlewares || [])];
|
|
164
|
+
|
|
165
|
+
// If there are no middlewares, return null
|
|
166
|
+
if (allMiddlewares.length === 0) {
|
|
167
|
+
return null;
|
|
211
168
|
}
|
|
169
|
+
|
|
170
|
+
// If there are middlewares, return them
|
|
171
|
+
return { middlewares: allMiddlewares, params };
|
|
212
172
|
}
|
|
213
173
|
}
|
|
214
174
|
|
|
215
|
-
export class
|
|
216
|
-
|
|
175
|
+
export const RouterError = class RouterError extends Error {
|
|
176
|
+
status: number | undefined = 500;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export class Router {
|
|
180
|
+
private routeTree = new RouteTree();
|
|
217
181
|
container: Element | string | null = null;
|
|
218
182
|
query: Record<string, string | number> = {};
|
|
219
183
|
options: Record<string, any> = {};
|
|
@@ -223,70 +187,129 @@ export class Router implements RouterInterface {
|
|
|
223
187
|
matches: string[] = [];
|
|
224
188
|
pathPrefix: string = "";
|
|
225
189
|
|
|
190
|
+
private errorHandlers: Map<number | string | Error | "generic", Middlewares> = new Map();
|
|
191
|
+
|
|
226
192
|
constructor(pathPrefix: string = "") {
|
|
227
193
|
this.pathPrefix = pathPrefix;
|
|
228
194
|
}
|
|
229
195
|
|
|
230
|
-
add(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
use(...middlewares: Middlewares | Router[] | string[]): Router {
|
|
237
|
-
let path = getPathWithoutLastSlash(
|
|
238
|
-
`${this.pathPrefix}${typeof middlewares[0] === "string" ? middlewares.shift() : "/"}`
|
|
196
|
+
add(...args: (string | Middleware | Router)[]): Router {
|
|
197
|
+
const flatArgs = flat(args);
|
|
198
|
+
const path = getPathWithoutLastSlash(
|
|
199
|
+
`${this.pathPrefix}${typeof flatArgs[0] === "string" ? flatArgs.shift() : "/.*"}`
|
|
239
200
|
);
|
|
240
201
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
continue;
|
|
202
|
+
// If the first argument is a Router, add all its routes
|
|
203
|
+
if (flatArgs.length === 1 && flatArgs[0] instanceof Router) {
|
|
204
|
+
const subrouter = flatArgs[0] as Router;
|
|
205
|
+
for (const subroute of subrouter.routes()) {
|
|
206
|
+
const subroutePath = `${path}${subroute}`;
|
|
207
|
+
this.routeTree.addRoute(subroutePath, subrouter.routeTree.findRoute(subroute)!.middlewares || []);
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
// Verify that no middlewares are added when a subrouter is added
|
|
211
|
+
if (flatArgs.some((item) => item instanceof Router)) {
|
|
212
|
+
throw new RouterError("You cannot add middlewares when adding a subrouter.");
|
|
253
213
|
}
|
|
254
214
|
|
|
255
|
-
|
|
256
|
-
|
|
215
|
+
// Verify that all middlewares are functions
|
|
216
|
+
if (flatArgs.some((item) => typeof item !== "function")) {
|
|
217
|
+
throw new RouterError("All middlewares must be functions.");
|
|
257
218
|
}
|
|
219
|
+
|
|
220
|
+
this.routeTree.addRoute(path, flatArgs as Middlewares);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
catch(...args: (number | string | Error | Middleware)[]): Router {
|
|
227
|
+
const condition =
|
|
228
|
+
typeof args[0] === "number" || typeof args[0] === "string" || args[0].name.includes("Error")
|
|
229
|
+
? (args.shift() as number | string | Error)
|
|
230
|
+
: "generic";
|
|
231
|
+
|
|
232
|
+
if (typeof condition !== "number" && typeof condition !== "string" && !condition.name.includes("Error")) {
|
|
233
|
+
throw new RouterError("The condition must be a number, string or an instance of Error.");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Verify that all middlewares are functions
|
|
237
|
+
if (args.some((item) => typeof item !== "function")) {
|
|
238
|
+
throw new RouterError("All middlewares must be functions.");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let handlers = this.errorHandlers.get(condition);
|
|
242
|
+
if (!handlers) {
|
|
243
|
+
handlers = [];
|
|
244
|
+
this.errorHandlers.set(condition, handlers);
|
|
258
245
|
}
|
|
259
246
|
|
|
247
|
+
handlers.push(...(args as Middlewares));
|
|
260
248
|
return this;
|
|
261
249
|
}
|
|
262
250
|
|
|
263
251
|
routes(): string[] {
|
|
264
|
-
return this.
|
|
252
|
+
return this.getAllRoutes(this.routeTree.root, "");
|
|
265
253
|
}
|
|
266
254
|
|
|
255
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
267
256
|
async go(
|
|
268
257
|
path: string,
|
|
269
|
-
parentComponent?: Component | POJOComponent | VnodeComponentInterface
|
|
270
|
-
preventPushState = false
|
|
258
|
+
parentComponent?: Component | POJOComponent | VnodeComponentInterface
|
|
271
259
|
): Promise<string | void> {
|
|
272
260
|
if (!path) {
|
|
273
|
-
|
|
261
|
+
return this.handleError(new RouterError("The URL is empty."), parentComponent);
|
|
274
262
|
}
|
|
275
263
|
|
|
276
|
-
|
|
264
|
+
if (/%[^0-9A-Fa-f]{2}/.test(path)) {
|
|
265
|
+
return this.handleError(new RouterError(`The URL ${path} is malformed.`));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const constructedPath = getPathWithoutLastSlash(`${this.pathPrefix}${path}`);
|
|
277
269
|
const parts = constructedPath.split("?", 2);
|
|
278
270
|
this.url = constructedPath;
|
|
279
271
|
this.query = parseQuery(parts[1]);
|
|
280
272
|
|
|
281
|
-
const
|
|
282
|
-
|
|
273
|
+
const finalPath = parts[0].replace(/(.+)\/$/, "$1").split("#")[0];
|
|
274
|
+
this.path = path;
|
|
275
|
+
|
|
276
|
+
let route = this.routeTree.findRoute(finalPath);
|
|
277
|
+
|
|
278
|
+
if (!route || !route.middlewares) {
|
|
279
|
+
// If the route is not found, search for a wildcard route
|
|
280
|
+
const finalPathParts = finalPath.split("/"); // Divide the path into segments
|
|
281
|
+
|
|
282
|
+
while (finalPathParts.length > 0) {
|
|
283
|
+
finalPathParts.pop(); // Remove the last segment
|
|
284
|
+
const wildcardRoute = this.routeTree.findRoute(finalPathParts.join("/") + "/.*"); // Search for a wildcard route
|
|
285
|
+
if (wildcardRoute) {
|
|
286
|
+
route = wildcardRoute;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// If no route is found, return a 404 error
|
|
292
|
+
if (!route || !route.middlewares) {
|
|
293
|
+
const error = new RouterError(`The URL ${constructedPath} was not found in the router's registered paths.`);
|
|
294
|
+
(error as any).status = 404;
|
|
295
|
+
return this.handleError(error, parentComponent);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const { middlewares, params } = route;
|
|
300
|
+
this.params = params;
|
|
301
|
+
|
|
302
|
+
let component = await this.searchComponent(middlewares, parentComponent);
|
|
283
303
|
|
|
284
304
|
if (component === false) {
|
|
285
305
|
return;
|
|
286
306
|
}
|
|
287
307
|
|
|
288
308
|
if (!component) {
|
|
289
|
-
|
|
309
|
+
return this.handleError(
|
|
310
|
+
new RouterError(`The URL ${constructedPath} did not return a valid component.`),
|
|
311
|
+
parentComponent
|
|
312
|
+
);
|
|
290
313
|
}
|
|
291
314
|
|
|
292
315
|
if (isComponent(parentComponent) || isVnodeComponent(parentComponent)) {
|
|
@@ -299,7 +322,7 @@ export class Router implements RouterInterface {
|
|
|
299
322
|
}
|
|
300
323
|
}
|
|
301
324
|
|
|
302
|
-
if (!isNodeJs &&
|
|
325
|
+
if (!isNodeJs && window.location.pathname + window.location.search !== constructedPath) {
|
|
303
326
|
window.history.pushState(null, "", constructedPath);
|
|
304
327
|
}
|
|
305
328
|
|
|
@@ -310,23 +333,188 @@ export class Router implements RouterInterface {
|
|
|
310
333
|
|
|
311
334
|
getOnClickHandler(url: string) {
|
|
312
335
|
return (e: MouseEvent) => {
|
|
336
|
+
if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.defaultPrevented) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
313
340
|
if (typeof url === "string" && url.length > 0) {
|
|
314
341
|
this.go(url);
|
|
315
342
|
}
|
|
316
343
|
e.preventDefault();
|
|
317
344
|
};
|
|
318
345
|
}
|
|
346
|
+
|
|
347
|
+
private getAllRoutes(node: RouteNode, prefix: string): string[] {
|
|
348
|
+
const routes: string[] = [];
|
|
349
|
+
|
|
350
|
+
for (const [key, child] of node.children) {
|
|
351
|
+
// eslint-disable-next-line sonarjs/no-nested-template-literals
|
|
352
|
+
const newPrefix = `${prefix}/${child.isDynamic ? `:${child.paramKey}` : key}`.replace(/\/$/, "");
|
|
353
|
+
if (child.middlewares) {
|
|
354
|
+
routes.push(newPrefix);
|
|
355
|
+
}
|
|
356
|
+
routes.push(...this.getAllRoutes(child, newPrefix));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return routes;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private createRequest(): Request {
|
|
363
|
+
return {
|
|
364
|
+
params: this.params,
|
|
365
|
+
query: this.query,
|
|
366
|
+
url: this.url,
|
|
367
|
+
path: this.path,
|
|
368
|
+
matches: this.matches,
|
|
369
|
+
redirect: (path: string) => this.go(path)
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private getErrorConditionMiddlewares(error: any): Middlewares | false {
|
|
374
|
+
// Search first for class and name errors
|
|
375
|
+
for (const [condition, middlewares] of this.errorHandlers) {
|
|
376
|
+
if (
|
|
377
|
+
typeof condition !== "number" &&
|
|
378
|
+
typeof condition !== "string" &&
|
|
379
|
+
error instanceof (condition as any) &&
|
|
380
|
+
error.name === condition.name
|
|
381
|
+
) {
|
|
382
|
+
return middlewares;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// then for code errors
|
|
387
|
+
for (const [condition, middlewares] of this.errorHandlers) {
|
|
388
|
+
if (typeof condition === "number" && (error.status === condition || error.code === condition)) {
|
|
389
|
+
return middlewares;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// and then for message errors
|
|
394
|
+
for (const [condition, middlewares] of this.errorHandlers) {
|
|
395
|
+
if (typeof condition === "string" && (error.name === condition || error.message.includes(condition))) {
|
|
396
|
+
return middlewares;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// If no specific error handler is found, return the generic one
|
|
401
|
+
return this.errorHandlers.get("generic") || false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
405
|
+
private async handleError(
|
|
406
|
+
error: Error,
|
|
407
|
+
parentComponent?: Component | POJOComponent | VnodeComponentInterface
|
|
408
|
+
): Promise<void | string> {
|
|
409
|
+
const request: Request = this.createRequest();
|
|
410
|
+
let component = null;
|
|
411
|
+
const middlewares = this.getErrorConditionMiddlewares(error);
|
|
412
|
+
|
|
413
|
+
// If no error handler is found, throw the error
|
|
414
|
+
if (middlewares === false) {
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let response;
|
|
419
|
+
try {
|
|
420
|
+
for (const middleware of middlewares) {
|
|
421
|
+
response = await middleware(request, error);
|
|
422
|
+
|
|
423
|
+
// If the response is a component or vnode, return it for rendering
|
|
424
|
+
if (response !== undefined && (isComponent(response) || isVnodeComponent(response))) {
|
|
425
|
+
component = response;
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// If the response is false, stop the middleware chain
|
|
430
|
+
if (response === false) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
} catch (err) {
|
|
435
|
+
// If an error occurs during the error handling, we handle it recursively
|
|
436
|
+
(err as Error).cause = error;
|
|
437
|
+
|
|
438
|
+
let errorCauseCount = 0;
|
|
439
|
+
while ((err as Error).cause) {
|
|
440
|
+
errorCauseCount++;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (errorCauseCount > 20) {
|
|
444
|
+
throw new RouterError("Too many error causes. Possible circular error handling.");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return this.handleError(err as Error, parentComponent);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (component) {
|
|
451
|
+
// If there is an error middleware that returns a component, we return it
|
|
452
|
+
if (isComponent(parentComponent) || isVnodeComponent(parentComponent)) {
|
|
453
|
+
const childComponent = isVnodeComponent(component) ? component : v(component as Component, {});
|
|
454
|
+
if (isVnodeComponent(parentComponent)) {
|
|
455
|
+
parentComponent.children.push(childComponent);
|
|
456
|
+
component = parentComponent;
|
|
457
|
+
} else {
|
|
458
|
+
component = v(parentComponent, {}, childComponent) as VnodeComponentInterface;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// If we are in the browser, we update the URL
|
|
463
|
+
if (!isNodeJs && window.location.pathname + window.location.search !== this.url) {
|
|
464
|
+
window.history.pushState(null, "", this.url);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// If there is a container, we mount the component
|
|
468
|
+
if (this.container) {
|
|
469
|
+
return mount(this.container, component);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// If there is no component to render, we throw the error
|
|
474
|
+
throw error;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private async searchComponent(
|
|
478
|
+
middlewares: Middlewares,
|
|
479
|
+
parentComponent?: Component | POJOComponent | VnodeComponentInterface
|
|
480
|
+
) {
|
|
481
|
+
const request: Request = this.createRequest();
|
|
482
|
+
|
|
483
|
+
let response;
|
|
484
|
+
|
|
485
|
+
for (const middleware of middlewares) {
|
|
486
|
+
try {
|
|
487
|
+
response = await middleware(request);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return this.handleError(error as Error, parentComponent);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// If the response is a component or vnode, return it for rendering
|
|
493
|
+
if (response !== undefined && (isComponent(response) || isVnodeComponent(response))) {
|
|
494
|
+
return response;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// If the response is false, stop the middleware chain
|
|
498
|
+
if (response === false) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return response;
|
|
504
|
+
}
|
|
319
505
|
}
|
|
320
506
|
|
|
321
507
|
let localRedirect: RedirectFunction;
|
|
322
508
|
|
|
323
|
-
export function redirect(
|
|
509
|
+
export async function redirect(
|
|
324
510
|
url: string,
|
|
325
511
|
parentComponent?: Component | POJOComponent | VnodeComponentInterface,
|
|
326
512
|
preventPushState = false
|
|
327
513
|
): Promise<string | void> {
|
|
328
514
|
if (!localRedirect) {
|
|
329
|
-
|
|
515
|
+
// eslint-disable-next-line no-console
|
|
516
|
+
console.warn("Redirect function is not initialized. Please mount the router first.");
|
|
517
|
+
return;
|
|
330
518
|
}
|
|
331
519
|
return localRedirect(url, parentComponent, preventPushState);
|
|
332
520
|
}
|
|
@@ -337,15 +525,15 @@ export function mountRouter(elementContainer: string | any, router: Router): voi
|
|
|
337
525
|
|
|
338
526
|
if (!isNodeJs) {
|
|
339
527
|
function onPopStateGoToRoute(): void {
|
|
340
|
-
|
|
341
|
-
(router as unknown as Router).go(pathWithoutPrefix
|
|
528
|
+
const pathWithoutPrefix = getPathWithoutPrefix(document.location.pathname, router.pathPrefix);
|
|
529
|
+
(router as unknown as Router).go(pathWithoutPrefix);
|
|
342
530
|
}
|
|
343
531
|
window.addEventListener("popstate", onPopStateGoToRoute, false);
|
|
344
532
|
onPopStateGoToRoute();
|
|
345
533
|
}
|
|
346
534
|
|
|
347
|
-
directive("route", (url: string, vnode: VnodeWithDom
|
|
348
|
-
setAttribute("href", url, vnode
|
|
349
|
-
setAttribute("onclick", router.getOnClickHandler(url), vnode
|
|
535
|
+
directive("route", (url: string, vnode: VnodeWithDom): void => {
|
|
536
|
+
setAttribute("href", url, vnode);
|
|
537
|
+
setAttribute("onclick", router.getOnClickHandler(url), vnode);
|
|
350
538
|
});
|
|
351
539
|
}
|