@alepha/react 0.9.5 → 0.10.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/README.md +85 -3
- package/dist/index.browser.js +110 -25
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +123 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +164 -45
- package/dist/index.js.map +1 -1
- package/package.json +14 -13
- package/src/descriptors/$page.ts +85 -0
- package/src/hooks/useAlepha.ts +1 -1
- package/src/hooks/useQueryParams.ts +9 -5
- package/src/hooks/useRouterEvents.ts +4 -4
- package/src/hooks/useStore.ts +5 -5
- package/src/providers/ReactBrowserProvider.ts +3 -3
- package/src/providers/ReactBrowserRouterProvider.ts +6 -6
- package/src/providers/ReactPageProvider.ts +2 -3
- package/src/providers/ReactServerProvider.ts +83 -35
- package/src/services/ReactRouter.ts +1 -1
- package/dist/index.cjs +0 -1631
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -811
- package/dist/index.d.cts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $env, $hook, $inject, $module, Alepha, AlephaError, Descriptor, KIND,
|
|
1
|
+
import { $env, $hook, $inject, $module, Alepha, AlephaError, Descriptor, KIND, createDescriptor, t } from "@alepha/core";
|
|
2
2
|
import { AlephaServer, HttpClient, ServerProvider, ServerRouterProvider, ServerTimingProvider } from "@alepha/server";
|
|
3
3
|
import { AlephaServerCache } from "@alepha/server-cache";
|
|
4
4
|
import { AlephaServerLinks, LinkProvider, ServerLinksProvider } from "@alepha/server-links";
|
|
@@ -15,6 +15,91 @@ import { RouterProvider } from "@alepha/router";
|
|
|
15
15
|
//#region src/descriptors/$page.ts
|
|
16
16
|
/**
|
|
17
17
|
* Main descriptor for defining a React route in the application.
|
|
18
|
+
*
|
|
19
|
+
* The $page descriptor is the core building block for creating type-safe, SSR-enabled React routes.
|
|
20
|
+
* It provides a declarative way to define pages with powerful features:
|
|
21
|
+
*
|
|
22
|
+
* **Routing & Navigation**
|
|
23
|
+
* - URL pattern matching with parameters (e.g., `/users/:id`)
|
|
24
|
+
* - Nested routing with parent-child relationships
|
|
25
|
+
* - Type-safe URL parameter and query string validation
|
|
26
|
+
*
|
|
27
|
+
* **Data Loading**
|
|
28
|
+
* - Server-side data fetching with the `resolve` function
|
|
29
|
+
* - Automatic serialization and hydration for SSR
|
|
30
|
+
* - Access to request context, URL params, and parent data
|
|
31
|
+
*
|
|
32
|
+
* **Component Loading**
|
|
33
|
+
* - Direct component rendering or lazy loading for code splitting
|
|
34
|
+
* - Client-only rendering when browser APIs are needed
|
|
35
|
+
* - Automatic fallback handling during hydration
|
|
36
|
+
*
|
|
37
|
+
* **Performance Optimization**
|
|
38
|
+
* - Static generation for pre-rendered pages at build time
|
|
39
|
+
* - Server-side caching with configurable TTL and providers
|
|
40
|
+
* - Code splitting through lazy component loading
|
|
41
|
+
*
|
|
42
|
+
* **Error Handling**
|
|
43
|
+
* - Custom error handlers with support for redirects
|
|
44
|
+
* - Hierarchical error handling (child → parent)
|
|
45
|
+
* - HTTP status code handling (404, 401, etc.)
|
|
46
|
+
*
|
|
47
|
+
* **Page Animations**
|
|
48
|
+
* - CSS-based enter/exit animations
|
|
49
|
+
* - Dynamic animations based on page state
|
|
50
|
+
* - Custom timing and easing functions
|
|
51
|
+
*
|
|
52
|
+
* **Lifecycle Management**
|
|
53
|
+
* - Server response hooks for headers and status codes
|
|
54
|
+
* - Page leave handlers for cleanup (browser only)
|
|
55
|
+
* - Permission-based access control
|
|
56
|
+
*
|
|
57
|
+
* @example Simple page with data fetching
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const userProfile = $page({
|
|
60
|
+
* path: "/users/:id",
|
|
61
|
+
* schema: {
|
|
62
|
+
* params: t.object({ id: t.int() }),
|
|
63
|
+
* query: t.object({ tab: t.optional(t.string()) })
|
|
64
|
+
* },
|
|
65
|
+
* resolve: async ({ params }) => {
|
|
66
|
+
* const user = await userApi.getUser(params.id);
|
|
67
|
+
* return { user };
|
|
68
|
+
* },
|
|
69
|
+
* lazy: () => import("./UserProfile.tsx")
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example Nested routing with error handling
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const projectSection = $page({
|
|
76
|
+
* path: "/projects/:id",
|
|
77
|
+
* children: () => [projectBoard, projectSettings],
|
|
78
|
+
* resolve: async ({ params }) => {
|
|
79
|
+
* const project = await projectApi.get(params.id);
|
|
80
|
+
* return { project };
|
|
81
|
+
* },
|
|
82
|
+
* errorHandler: (error) => {
|
|
83
|
+
* if (HttpError.is(error, 404)) {
|
|
84
|
+
* return <ProjectNotFound />;
|
|
85
|
+
* }
|
|
86
|
+
* }
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example Static generation with caching
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const blogPost = $page({
|
|
93
|
+
* path: "/blog/:slug",
|
|
94
|
+
* static: {
|
|
95
|
+
* entries: posts.map(p => ({ params: { slug: p.slug } }))
|
|
96
|
+
* },
|
|
97
|
+
* resolve: async ({ params }) => {
|
|
98
|
+
* const post = await loadPost(params.slug);
|
|
99
|
+
* return { post };
|
|
100
|
+
* }
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
18
103
|
*/
|
|
19
104
|
const $page = (options) => {
|
|
20
105
|
return createDescriptor(PageDescriptor, options);
|
|
@@ -248,7 +333,7 @@ const AlephaContext = createContext(void 0);
|
|
|
248
333
|
*
|
|
249
334
|
* - alepha.state() for state management
|
|
250
335
|
* - alepha.inject() for dependency injection
|
|
251
|
-
* - alepha.emit() for event handling
|
|
336
|
+
* - alepha.events.emit() for event handling
|
|
252
337
|
* etc...
|
|
253
338
|
*/
|
|
254
339
|
const useAlepha = () => {
|
|
@@ -275,10 +360,10 @@ const useRouterEvents = (opts = {}, deps = []) => {
|
|
|
275
360
|
const onEnd = opts.onEnd;
|
|
276
361
|
const onError = opts.onError;
|
|
277
362
|
const onSuccess = opts.onSuccess;
|
|
278
|
-
if (onBegin) subs.push(alepha.on("react:transition:begin", cb(onBegin)));
|
|
279
|
-
if (onEnd) subs.push(alepha.on("react:transition:end", cb(onEnd)));
|
|
280
|
-
if (onError) subs.push(alepha.on("react:transition:error", cb(onError)));
|
|
281
|
-
if (onSuccess) subs.push(alepha.on("react:transition:success", cb(onSuccess)));
|
|
363
|
+
if (onBegin) subs.push(alepha.events.on("react:transition:begin", cb(onBegin)));
|
|
364
|
+
if (onEnd) subs.push(alepha.events.on("react:transition:end", cb(onEnd)));
|
|
365
|
+
if (onError) subs.push(alepha.events.on("react:transition:error", cb(onError)));
|
|
366
|
+
if (onSuccess) subs.push(alepha.events.on("react:transition:success", cb(onSuccess)));
|
|
282
367
|
return () => {
|
|
283
368
|
for (const sub of subs) sub();
|
|
284
369
|
};
|
|
@@ -293,17 +378,17 @@ const useRouterEvents = (opts = {}, deps = []) => {
|
|
|
293
378
|
const useStore = (key, defaultValue) => {
|
|
294
379
|
const alepha = useAlepha();
|
|
295
380
|
useMemo(() => {
|
|
296
|
-
if (defaultValue != null && alepha.state(key) == null) alepha.state(key, defaultValue);
|
|
381
|
+
if (defaultValue != null && alepha.state.get(key) == null) alepha.state.set(key, defaultValue);
|
|
297
382
|
}, [defaultValue]);
|
|
298
|
-
const [state, setState] = useState(alepha.state(key));
|
|
383
|
+
const [state, setState] = useState(alepha.state.get(key));
|
|
299
384
|
useEffect(() => {
|
|
300
385
|
if (!alepha.isBrowser()) return;
|
|
301
|
-
return alepha.on("state:mutate", (ev) => {
|
|
386
|
+
return alepha.events.on("state:mutate", (ev) => {
|
|
302
387
|
if (ev.key === key) setState(ev.value);
|
|
303
388
|
});
|
|
304
389
|
}, []);
|
|
305
390
|
return [state, (value) => {
|
|
306
|
-
alepha.state(key, value);
|
|
391
|
+
alepha.state.set(key, value);
|
|
307
392
|
}];
|
|
308
393
|
};
|
|
309
394
|
|
|
@@ -537,8 +622,8 @@ var ReactPageProvider = class {
|
|
|
537
622
|
return root;
|
|
538
623
|
}
|
|
539
624
|
convertStringObjectToObject = (schema, value) => {
|
|
540
|
-
if (
|
|
541
|
-
for (const key in schema.properties) if (
|
|
625
|
+
if (t.schema.isObject(schema) && typeof value === "object") {
|
|
626
|
+
for (const key in schema.properties) if (t.schema.isObject(schema.properties[key]) && typeof value[key] === "string") try {
|
|
542
627
|
value[key] = this.alepha.parse(schema.properties[key], decodeURIComponent(value[key]));
|
|
543
628
|
} catch (e) {}
|
|
544
629
|
}
|
|
@@ -825,12 +910,13 @@ var ReactServerProvider = class {
|
|
|
825
910
|
serverTimingProvider = $inject(ServerTimingProvider);
|
|
826
911
|
env = $env(envSchema$1);
|
|
827
912
|
ROOT_DIV_REGEX = new RegExp(`<div([^>]*)\\s+id=["']${this.env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`, "is");
|
|
913
|
+
preprocessedTemplate = null;
|
|
828
914
|
onConfigure = $hook({
|
|
829
915
|
on: "configure",
|
|
830
916
|
handler: async () => {
|
|
831
917
|
const pages = this.alepha.descriptors($page);
|
|
832
918
|
const ssrEnabled = pages.length > 0 && this.env.REACT_SSR_ENABLED !== false;
|
|
833
|
-
this.alepha.state("react.server.ssr", ssrEnabled);
|
|
919
|
+
this.alepha.state.set("react.server.ssr", ssrEnabled);
|
|
834
920
|
for (const page of pages) {
|
|
835
921
|
page.render = this.createRenderFunction(page.name);
|
|
836
922
|
page.fetch = async (options) => {
|
|
@@ -886,6 +972,8 @@ var ReactServerProvider = class {
|
|
|
886
972
|
return this.alepha.env.REACT_SERVER_TEMPLATE ?? "<!DOCTYPE html><html lang='en'><head></head><body></body></html>";
|
|
887
973
|
}
|
|
888
974
|
async registerPages(templateLoader) {
|
|
975
|
+
const template = await templateLoader();
|
|
976
|
+
if (template) this.preprocessedTemplate = this.preprocessTemplate(template);
|
|
889
977
|
for (const page of this.pageApi.getPages()) {
|
|
890
978
|
if (page.children?.length) continue;
|
|
891
979
|
this.log.debug(`+ ${page.match} -> ${page.name}`);
|
|
@@ -932,7 +1020,7 @@ var ReactServerProvider = class {
|
|
|
932
1020
|
};
|
|
933
1021
|
const state = entry;
|
|
934
1022
|
this.log.trace("Rendering", { url });
|
|
935
|
-
await this.alepha.emit("react:server:render:begin", { state });
|
|
1023
|
+
await this.alepha.events.emit("react:server:render:begin", { state });
|
|
936
1024
|
const { redirect } = await this.pageApi.createLayers(page, state);
|
|
937
1025
|
if (redirect) return {
|
|
938
1026
|
state,
|
|
@@ -940,13 +1028,14 @@ var ReactServerProvider = class {
|
|
|
940
1028
|
redirect
|
|
941
1029
|
};
|
|
942
1030
|
if (!withIndex && !options.html) {
|
|
943
|
-
this.alepha.state("react.router.state", state);
|
|
1031
|
+
this.alepha.state.set("react.router.state", state);
|
|
944
1032
|
return {
|
|
945
1033
|
state,
|
|
946
1034
|
html: renderToString(this.pageApi.root(state))
|
|
947
1035
|
};
|
|
948
1036
|
}
|
|
949
|
-
const
|
|
1037
|
+
const template = this.template ?? "";
|
|
1038
|
+
const html = this.renderToHtml(template, state, options.hydration);
|
|
950
1039
|
if (html instanceof Redirection) return {
|
|
951
1040
|
state,
|
|
952
1041
|
html: "",
|
|
@@ -956,7 +1045,7 @@ var ReactServerProvider = class {
|
|
|
956
1045
|
state,
|
|
957
1046
|
html
|
|
958
1047
|
};
|
|
959
|
-
await this.alepha.emit("react:server:render:end", result);
|
|
1048
|
+
await this.alepha.events.emit("react:server:render:end", result);
|
|
960
1049
|
return result;
|
|
961
1050
|
};
|
|
962
1051
|
}
|
|
@@ -974,7 +1063,7 @@ var ReactServerProvider = class {
|
|
|
974
1063
|
layers: []
|
|
975
1064
|
};
|
|
976
1065
|
const state = entry;
|
|
977
|
-
if (this.alepha.has(ServerLinksProvider)) this.alepha.state("api", await this.alepha.inject(ServerLinksProvider).getUserApiLinks({
|
|
1066
|
+
if (this.alepha.has(ServerLinksProvider)) this.alepha.state.set("api", await this.alepha.inject(ServerLinksProvider).getUserApiLinks({
|
|
978
1067
|
user: serverRequest.user,
|
|
979
1068
|
authorization: serverRequest.headers.authorization
|
|
980
1069
|
}));
|
|
@@ -987,7 +1076,7 @@ var ReactServerProvider = class {
|
|
|
987
1076
|
}
|
|
988
1077
|
target = target.parent;
|
|
989
1078
|
}
|
|
990
|
-
await this.alepha.emit("react:server:render:begin", {
|
|
1079
|
+
await this.alepha.events.emit("react:server:render:begin", {
|
|
991
1080
|
request: serverRequest,
|
|
992
1081
|
state
|
|
993
1082
|
});
|
|
@@ -1009,7 +1098,7 @@ var ReactServerProvider = class {
|
|
|
1009
1098
|
state,
|
|
1010
1099
|
html
|
|
1011
1100
|
};
|
|
1012
|
-
await this.alepha.emit("react:server:render:end", event);
|
|
1101
|
+
await this.alepha.events.emit("react:server:render:end", event);
|
|
1013
1102
|
route.onServerResponse?.(serverRequest);
|
|
1014
1103
|
this.log.trace("Page rendered", { name: route.name });
|
|
1015
1104
|
return event.html;
|
|
@@ -1017,7 +1106,7 @@ var ReactServerProvider = class {
|
|
|
1017
1106
|
}
|
|
1018
1107
|
renderToHtml(template, state, hydration = true) {
|
|
1019
1108
|
const element = this.pageApi.root(state);
|
|
1020
|
-
this.alepha.state("react.router.state", state);
|
|
1109
|
+
this.alepha.state.set("react.router.state", state);
|
|
1021
1110
|
this.serverTimingProvider.beginTiming("renderToString");
|
|
1022
1111
|
let app = "";
|
|
1023
1112
|
try {
|
|
@@ -1055,18 +1144,48 @@ var ReactServerProvider = class {
|
|
|
1055
1144
|
}
|
|
1056
1145
|
return response.html;
|
|
1057
1146
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1147
|
+
preprocessTemplate(template) {
|
|
1148
|
+
const bodyCloseMatch = template.match(/<\/body>/i);
|
|
1149
|
+
const bodyCloseIndex = bodyCloseMatch?.index ?? template.length;
|
|
1150
|
+
const beforeScript = template.substring(0, bodyCloseIndex);
|
|
1151
|
+
const afterScript = template.substring(bodyCloseIndex);
|
|
1152
|
+
const rootDivMatch = beforeScript.match(this.ROOT_DIV_REGEX);
|
|
1153
|
+
if (rootDivMatch) {
|
|
1154
|
+
const beforeDiv = beforeScript.substring(0, rootDivMatch.index);
|
|
1155
|
+
const afterDivStart = rootDivMatch.index + rootDivMatch[0].length;
|
|
1156
|
+
const afterDiv = beforeScript.substring(afterDivStart);
|
|
1157
|
+
const beforeApp = `${beforeDiv}<div${rootDivMatch[1]} id="${this.env.REACT_ROOT_ID}"${rootDivMatch[2]}>`;
|
|
1158
|
+
const afterApp = `</div>${afterDiv}`;
|
|
1159
|
+
return {
|
|
1160
|
+
beforeApp,
|
|
1161
|
+
afterApp,
|
|
1162
|
+
beforeScript: "",
|
|
1163
|
+
afterScript
|
|
1164
|
+
};
|
|
1067
1165
|
}
|
|
1068
|
-
const
|
|
1069
|
-
if (
|
|
1166
|
+
const bodyMatch = beforeScript.match(/<body([^>]*)>/i);
|
|
1167
|
+
if (bodyMatch) {
|
|
1168
|
+
const beforeBody = beforeScript.substring(0, bodyMatch.index + bodyMatch[0].length);
|
|
1169
|
+
const afterBody = beforeScript.substring(bodyMatch.index + bodyMatch[0].length);
|
|
1170
|
+
const beforeApp = `${beforeBody}<div id="${this.env.REACT_ROOT_ID}">`;
|
|
1171
|
+
const afterApp = `</div>${afterBody}`;
|
|
1172
|
+
return {
|
|
1173
|
+
beforeApp,
|
|
1174
|
+
afterApp,
|
|
1175
|
+
beforeScript: "",
|
|
1176
|
+
afterScript
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
return {
|
|
1180
|
+
beforeApp: `<div id="${this.env.REACT_ROOT_ID}">`,
|
|
1181
|
+
afterApp: `</div>`,
|
|
1182
|
+
beforeScript,
|
|
1183
|
+
afterScript
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
fillTemplate(response, app, script) {
|
|
1187
|
+
if (!this.preprocessedTemplate) this.preprocessedTemplate = this.preprocessTemplate(response.html);
|
|
1188
|
+
response.html = this.preprocessedTemplate.beforeApp + app + this.preprocessedTemplate.afterApp + script + this.preprocessedTemplate.afterScript;
|
|
1070
1189
|
}
|
|
1071
1190
|
};
|
|
1072
1191
|
|
|
@@ -1099,8 +1218,8 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
|
|
|
1099
1218
|
meta
|
|
1100
1219
|
};
|
|
1101
1220
|
const state = entry;
|
|
1102
|
-
await this.alepha.emit("react:transition:begin", {
|
|
1103
|
-
previous: this.alepha.state("react.router.state"),
|
|
1221
|
+
await this.alepha.events.emit("react:transition:begin", {
|
|
1222
|
+
previous: this.alepha.state.get("react.router.state"),
|
|
1104
1223
|
state
|
|
1105
1224
|
});
|
|
1106
1225
|
try {
|
|
@@ -1119,7 +1238,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
|
|
|
1119
1238
|
index: 0,
|
|
1120
1239
|
path: "/"
|
|
1121
1240
|
});
|
|
1122
|
-
await this.alepha.emit("react:transition:success", { state });
|
|
1241
|
+
await this.alepha.events.emit("react:transition:success", { state });
|
|
1123
1242
|
} catch (e) {
|
|
1124
1243
|
this.log.error("Transition has failed", e);
|
|
1125
1244
|
state.layers = [{
|
|
@@ -1128,7 +1247,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
|
|
|
1128
1247
|
index: 0,
|
|
1129
1248
|
path: "/"
|
|
1130
1249
|
}];
|
|
1131
|
-
await this.alepha.emit("react:transition:error", {
|
|
1250
|
+
await this.alepha.events.emit("react:transition:error", {
|
|
1132
1251
|
error: e,
|
|
1133
1252
|
state
|
|
1134
1253
|
});
|
|
@@ -1137,8 +1256,8 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
|
|
|
1137
1256
|
const layer = previous[i];
|
|
1138
1257
|
if (state.layers[i]?.name !== layer.name) this.pageApi.page(layer.name)?.onLeave?.();
|
|
1139
1258
|
}
|
|
1140
|
-
this.alepha.state("react.router.state", state);
|
|
1141
|
-
await this.alepha.emit("react:transition:end", { state });
|
|
1259
|
+
this.alepha.state.set("react.router.state", state);
|
|
1260
|
+
await this.alepha.events.emit("react:transition:end", { state });
|
|
1142
1261
|
}
|
|
1143
1262
|
root(state) {
|
|
1144
1263
|
return this.pageApi.root(state);
|
|
@@ -1166,7 +1285,7 @@ var ReactBrowserProvider = class {
|
|
|
1166
1285
|
}
|
|
1167
1286
|
transitioning;
|
|
1168
1287
|
get state() {
|
|
1169
|
-
return this.alepha.state("react.router.state");
|
|
1288
|
+
return this.alepha.state.get("react.router.state");
|
|
1170
1289
|
}
|
|
1171
1290
|
/**
|
|
1172
1291
|
* Accessor for Document DOM API.
|
|
@@ -1282,11 +1401,11 @@ var ReactBrowserProvider = class {
|
|
|
1282
1401
|
const hydration = this.getHydrationState();
|
|
1283
1402
|
const previous = hydration?.layers ?? [];
|
|
1284
1403
|
if (hydration) {
|
|
1285
|
-
for (const [key, value] of Object.entries(hydration)) if (key !== "layers") this.alepha.state(key, value);
|
|
1404
|
+
for (const [key, value] of Object.entries(hydration)) if (key !== "layers") this.alepha.state.set(key, value);
|
|
1286
1405
|
}
|
|
1287
1406
|
await this.render({ previous });
|
|
1288
1407
|
const element = this.router.root(this.state);
|
|
1289
|
-
await this.alepha.emit("react:browser:render", {
|
|
1408
|
+
await this.alepha.events.emit("react:browser:render", {
|
|
1290
1409
|
element,
|
|
1291
1410
|
root: this.getRootElement(),
|
|
1292
1411
|
hydration,
|
|
@@ -1307,7 +1426,7 @@ var ReactRouter = class {
|
|
|
1307
1426
|
alepha = $inject(Alepha);
|
|
1308
1427
|
pageApi = $inject(ReactPageProvider);
|
|
1309
1428
|
get state() {
|
|
1310
|
-
return this.alepha.state("react.router.state");
|
|
1429
|
+
return this.alepha.state.get("react.router.state");
|
|
1311
1430
|
}
|
|
1312
1431
|
get pages() {
|
|
1313
1432
|
return this.pageApi.getPages();
|
|
@@ -1493,7 +1612,7 @@ const useQueryParams = (schema, options = {}) => {
|
|
|
1493
1612
|
const key = options.key ?? "q";
|
|
1494
1613
|
const router = useRouter();
|
|
1495
1614
|
const querystring = router.query[key];
|
|
1496
|
-
const [queryParams, setQueryParams] = useState(decode(alepha, schema, router.query[key]));
|
|
1615
|
+
const [queryParams = {}, setQueryParams] = useState(decode(alepha, schema, router.query[key]));
|
|
1497
1616
|
useEffect(() => {
|
|
1498
1617
|
setQueryParams(decode(alepha, schema, querystring));
|
|
1499
1618
|
}, [querystring]);
|
|
@@ -1513,8 +1632,8 @@ const encode = (alepha, schema, data) => {
|
|
|
1513
1632
|
const decode = (alepha, schema, data) => {
|
|
1514
1633
|
try {
|
|
1515
1634
|
return alepha.parse(schema, JSON.parse(atob(decodeURIComponent(data))));
|
|
1516
|
-
} catch
|
|
1517
|
-
return
|
|
1635
|
+
} catch {
|
|
1636
|
+
return;
|
|
1518
1637
|
}
|
|
1519
1638
|
};
|
|
1520
1639
|
|