olova 2.0.54 → 2.0.56
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 +34 -283
- package/dist/chunk-23UAGQ6N.js +2208 -0
- package/dist/chunk-23UAGQ6N.js.map +1 -0
- package/dist/chunk-D7SIC5TC.js +367 -0
- package/dist/chunk-D7SIC5TC.js.map +1 -0
- package/dist/entry-server.cjs +2341 -0
- package/dist/entry-server.cjs.map +1 -0
- package/dist/entry-server.js +114 -0
- package/dist/entry-server.js.map +1 -0
- package/dist/entry-worker.cjs +2354 -0
- package/dist/entry-worker.cjs.map +1 -0
- package/dist/entry-worker.js +126 -0
- package/dist/entry-worker.js.map +1 -0
- package/dist/main.cjs +18 -0
- package/dist/main.cjs.map +1 -0
- package/dist/main.js +16 -0
- package/dist/main.js.map +1 -0
- package/dist/olova.cjs +1684 -0
- package/dist/olova.cjs.map +1 -0
- package/dist/olova.d.cts +72 -0
- package/dist/olova.d.ts +72 -0
- package/dist/olova.js +1325 -0
- package/dist/olova.js.map +1 -0
- package/dist/performance.cjs +386 -0
- package/dist/performance.cjs.map +1 -0
- package/dist/performance.js +3 -0
- package/dist/performance.js.map +1 -0
- package/dist/router.cjs +646 -0
- package/dist/router.cjs.map +1 -0
- package/dist/router.d.cts +113 -0
- package/dist/router.d.ts +113 -0
- package/dist/router.js +632 -0
- package/dist/router.js.map +1 -0
- package/main.tsx +76 -0
- package/olova.ts +619 -0
- package/package.json +37 -55
- package/src/entry-server.tsx +165 -0
- package/src/entry-worker.tsx +201 -0
- package/src/generator/index.ts +409 -0
- package/src/hydration/flight.ts +320 -0
- package/src/hydration/index.ts +12 -0
- package/src/hydration/types.ts +225 -0
- package/src/logger.ts +182 -0
- package/src/main.tsx +24 -0
- package/src/performance.ts +488 -0
- package/src/plugin/index.ts +204 -0
- package/src/router/ErrorBoundary.tsx +145 -0
- package/src/router/Link.tsx +117 -0
- package/src/router/OlovaRouter.tsx +354 -0
- package/src/router/Outlet.tsx +8 -0
- package/src/router/context.ts +117 -0
- package/src/router/index.ts +29 -0
- package/src/router/matching.ts +63 -0
- package/src/router/router.tsx +23 -0
- package/src/router/search-params.ts +29 -0
- package/src/scanner/index.ts +116 -0
- package/src/types/index.ts +191 -0
- package/src/utils/export.ts +85 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/naming.ts +54 -0
- package/src/utils/path.ts +45 -0
- package/tsup.config.ts +35 -0
- package/dist/client-BBLXpllK.d.ts +0 -487
- package/dist/client-C0av_vTZ.d.cts +0 -487
- package/dist/client.cjs +0 -850
- package/dist/client.cjs.map +0 -1
- package/dist/client.d.cts +0 -5
- package/dist/client.d.ts +0 -5
- package/dist/client.js +0 -816
- package/dist/client.js.map +0 -1
- package/dist/image.cjs +0 -25
- package/dist/image.cjs.map +0 -1
- package/dist/image.d.cts +0 -1
- package/dist/image.d.ts +0 -1
- package/dist/image.js +0 -3
- package/dist/image.js.map +0 -1
- package/dist/index.cjs +0 -907
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -31
- package/dist/index.d.ts +0 -31
- package/dist/index.js +0 -868
- package/dist/index.js.map +0 -1
- package/dist/plugin/index.cjs +0 -1550
- package/dist/plugin/index.cjs.map +0 -1
- package/dist/plugin/index.d.cts +0 -131
- package/dist/plugin/index.d.ts +0 -131
- package/dist/plugin/index.js +0 -1490
- package/dist/plugin/index.js.map +0 -1
- package/dist/serialization-xKcOESDh.d.cts +0 -122
- package/dist/serialization-xKcOESDh.d.ts +0 -122
- package/dist/server.cjs +0 -547
- package/dist/server.cjs.map +0 -1
- package/dist/server.d.cts +0 -148
- package/dist/server.d.ts +0 -148
- package/dist/server.js +0 -493
- package/dist/server.js.map +0 -1
package/dist/client.js
DELETED
|
@@ -1,816 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __esm = (fn, res) => function __init() {
|
|
6
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
-
};
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
-
|
|
22
|
-
// src/hydration/scheduler.ts
|
|
23
|
-
var scheduler_exports = {};
|
|
24
|
-
__export(scheduler_exports, {
|
|
25
|
-
HydrationScheduler: () => HydrationScheduler
|
|
26
|
-
});
|
|
27
|
-
var HydrationScheduler;
|
|
28
|
-
var init_scheduler = __esm({
|
|
29
|
-
"src/hydration/scheduler.ts"() {
|
|
30
|
-
"use strict";
|
|
31
|
-
HydrationScheduler = class {
|
|
32
|
-
constructor() {
|
|
33
|
-
this.queue = [];
|
|
34
|
-
this.isProcessing = false;
|
|
35
|
-
this.state = "idle";
|
|
36
|
-
if (typeof window !== "undefined") {
|
|
37
|
-
window.__FLIGHT_CHUNK__ = this.receiveChunk.bind(this);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
getState() {
|
|
41
|
-
return this.state;
|
|
42
|
-
}
|
|
43
|
-
schedule(id, priority, task) {
|
|
44
|
-
const priorityValue = {
|
|
45
|
-
critical: 0,
|
|
46
|
-
high: 1,
|
|
47
|
-
low: 2,
|
|
48
|
-
idle: 3
|
|
49
|
-
}[priority];
|
|
50
|
-
this.queue.push({ id, priority: priorityValue, task });
|
|
51
|
-
this.queue.sort((a, b) => a.priority - b.priority);
|
|
52
|
-
if (!this.isProcessing) {
|
|
53
|
-
this.processQueue();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async processQueue() {
|
|
57
|
-
this.isProcessing = true;
|
|
58
|
-
this.state = "hydrating";
|
|
59
|
-
while (this.queue.length > 0) {
|
|
60
|
-
const item = this.queue.shift();
|
|
61
|
-
try {
|
|
62
|
-
if (item.priority >= 2 && "requestIdleCallback" in window) {
|
|
63
|
-
await new Promise((resolve) => {
|
|
64
|
-
window.requestIdleCallback(() => {
|
|
65
|
-
item.task();
|
|
66
|
-
resolve();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
} else {
|
|
70
|
-
await Promise.resolve().then(item.task);
|
|
71
|
-
}
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.error(`[Olova] Hydration failed for ${item.id}:`, error);
|
|
74
|
-
}
|
|
75
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
76
|
-
}
|
|
77
|
-
this.isProcessing = false;
|
|
78
|
-
this.state = "complete";
|
|
79
|
-
if (typeof window !== "undefined") {
|
|
80
|
-
window.dispatchEvent(new CustomEvent("olova:hydration-complete"));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
receiveChunk(chunk) {
|
|
84
|
-
this.state = "streaming";
|
|
85
|
-
const placeholder = document.querySelector(`[data-flight-id="${chunk.id}"]`);
|
|
86
|
-
if (placeholder) {
|
|
87
|
-
const temp = document.createElement("div");
|
|
88
|
-
temp.innerHTML = chunk.html;
|
|
89
|
-
while (temp.firstChild) {
|
|
90
|
-
placeholder.parentNode?.insertBefore(temp.firstChild, placeholder);
|
|
91
|
-
}
|
|
92
|
-
placeholder.remove();
|
|
93
|
-
if (chunk.data) {
|
|
94
|
-
this.schedule(chunk.id, "high", () => {
|
|
95
|
-
this.hydrateNode(chunk.id, chunk.data);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
hydrateNode(id, data) {
|
|
101
|
-
const element = document.querySelector(`[data-flight-id="${id}"]`);
|
|
102
|
-
if (element) {
|
|
103
|
-
element.setAttribute("data-hydrated", "true");
|
|
104
|
-
element.__FLIGHT_DATA__ = data;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// src/router/context.ts
|
|
112
|
-
import { createContext } from "react";
|
|
113
|
-
var pageCache = /* @__PURE__ */ new Map();
|
|
114
|
-
var prefetchPromises = /* @__PURE__ */ new Map();
|
|
115
|
-
var metadataCache = /* @__PURE__ */ new Map();
|
|
116
|
-
var NAVIGATE_EVENT = "olova:navigate";
|
|
117
|
-
var eventHandlers = /* @__PURE__ */ new Map();
|
|
118
|
-
var routerEvents = {
|
|
119
|
-
on(event, handler) {
|
|
120
|
-
if (!eventHandlers.has(event)) {
|
|
121
|
-
eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
122
|
-
}
|
|
123
|
-
eventHandlers.get(event).add(handler);
|
|
124
|
-
},
|
|
125
|
-
off(event, handler) {
|
|
126
|
-
eventHandlers.get(event)?.delete(handler);
|
|
127
|
-
},
|
|
128
|
-
emit(event, ...args) {
|
|
129
|
-
eventHandlers.get(event)?.forEach((handler) => handler(...args));
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
var RouterContext = createContext({
|
|
133
|
-
pathname: "/",
|
|
134
|
-
params: {},
|
|
135
|
-
query: {},
|
|
136
|
-
asPath: "/",
|
|
137
|
-
push: async () => true,
|
|
138
|
-
replace: async () => true,
|
|
139
|
-
back: () => {
|
|
140
|
-
},
|
|
141
|
-
forward: () => {
|
|
142
|
-
},
|
|
143
|
-
prefetch: async () => {
|
|
144
|
-
},
|
|
145
|
-
reload: () => {
|
|
146
|
-
},
|
|
147
|
-
isReady: false,
|
|
148
|
-
isNavigating: false,
|
|
149
|
-
isFallback: false,
|
|
150
|
-
events: routerEvents
|
|
151
|
-
});
|
|
152
|
-
var ParamsContext = createContext({});
|
|
153
|
-
|
|
154
|
-
// src/router/navigation.ts
|
|
155
|
-
function navigate(to, options = {}) {
|
|
156
|
-
return new Promise((resolve) => {
|
|
157
|
-
const { scroll = true } = options;
|
|
158
|
-
routerEvents.emit("routeChangeStart", to, { shallow: options.shallow });
|
|
159
|
-
routerEvents.emit("beforeHistoryChange", to);
|
|
160
|
-
window.history.pushState({ scroll }, "", to);
|
|
161
|
-
window.dispatchEvent(new CustomEvent(NAVIGATE_EVENT, {
|
|
162
|
-
detail: { path: to, scroll }
|
|
163
|
-
}));
|
|
164
|
-
if (scroll) {
|
|
165
|
-
window.scrollTo(0, 0);
|
|
166
|
-
}
|
|
167
|
-
routerEvents.emit("routeChangeComplete", to, { shallow: options.shallow });
|
|
168
|
-
resolve(true);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
function replace(to, options = {}) {
|
|
172
|
-
return new Promise((resolve) => {
|
|
173
|
-
const { scroll = false } = options;
|
|
174
|
-
routerEvents.emit("routeChangeStart", to, { shallow: options.shallow });
|
|
175
|
-
routerEvents.emit("beforeHistoryChange", to);
|
|
176
|
-
window.history.replaceState({ scroll }, "", to);
|
|
177
|
-
window.dispatchEvent(new CustomEvent(NAVIGATE_EVENT, {
|
|
178
|
-
detail: { path: to, scroll, replace: true }
|
|
179
|
-
}));
|
|
180
|
-
if (scroll) {
|
|
181
|
-
window.scrollTo(0, 0);
|
|
182
|
-
}
|
|
183
|
-
routerEvents.emit("routeChangeComplete", to, { shallow: options.shallow });
|
|
184
|
-
resolve(true);
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// src/router/match.ts
|
|
189
|
-
function matchRoute(routePath, currentPath) {
|
|
190
|
-
if (routePath === currentPath) {
|
|
191
|
-
return { matches: true, params: {}, score: 100 };
|
|
192
|
-
}
|
|
193
|
-
const routeSegments = routePath.split("/").filter(Boolean);
|
|
194
|
-
const pathSegments = currentPath.split("/").filter(Boolean);
|
|
195
|
-
const params = {};
|
|
196
|
-
let score = 0;
|
|
197
|
-
for (let i = 0; i < routeSegments.length; i++) {
|
|
198
|
-
const routeSegment = routeSegments[i];
|
|
199
|
-
const pathSegment = pathSegments[i];
|
|
200
|
-
if (routeSegment?.startsWith("*") && routeSegment?.endsWith("?")) {
|
|
201
|
-
const paramName = routeSegment.slice(1, -1);
|
|
202
|
-
const remainingPath = pathSegments.slice(i);
|
|
203
|
-
params[paramName] = remainingPath.length > 0 ? remainingPath : [];
|
|
204
|
-
score += 1;
|
|
205
|
-
return { matches: true, params, score };
|
|
206
|
-
}
|
|
207
|
-
if (routeSegment?.startsWith("*")) {
|
|
208
|
-
const paramName = routeSegment.slice(1);
|
|
209
|
-
const remainingPath = pathSegments.slice(i);
|
|
210
|
-
if (remainingPath.length > 0) {
|
|
211
|
-
params[paramName] = remainingPath;
|
|
212
|
-
score += 2;
|
|
213
|
-
return { matches: true, params, score };
|
|
214
|
-
}
|
|
215
|
-
return { matches: false, params: {}, score: 0 };
|
|
216
|
-
}
|
|
217
|
-
if (routeSegment?.startsWith(":") || routeSegment?.startsWith("[") && routeSegment?.endsWith("]")) {
|
|
218
|
-
if (!pathSegment) return { matches: false, params: {}, score: 0 };
|
|
219
|
-
const paramName = routeSegment.startsWith(":") ? routeSegment.slice(1) : routeSegment.slice(1, -1);
|
|
220
|
-
params[paramName] = pathSegment;
|
|
221
|
-
score += 5;
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (routeSegment !== pathSegment) return { matches: false, params: {}, score: 0 };
|
|
225
|
-
score += 10;
|
|
226
|
-
}
|
|
227
|
-
if (routeSegments.length !== pathSegments.length) return { matches: false, params: {}, score: 0 };
|
|
228
|
-
return { matches: true, params, score };
|
|
229
|
-
}
|
|
230
|
-
function findRoute(routes, path) {
|
|
231
|
-
const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
232
|
-
const exactMatch = routes.find((r) => r.path === normalizedPath);
|
|
233
|
-
if (exactMatch) return { route: exactMatch, params: {} };
|
|
234
|
-
let bestMatch = null;
|
|
235
|
-
for (const route of routes) {
|
|
236
|
-
const { matches, params, score } = matchRoute(route.path, normalizedPath);
|
|
237
|
-
if (matches && (!bestMatch || score > bestMatch.score)) {
|
|
238
|
-
bestMatch = { route, params, score };
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return bestMatch ? { route: bestMatch.route, params: bestMatch.params } : { route: null, params: {} };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// src/router/prefetch.ts
|
|
245
|
-
async function prefetchRoute(routes, path) {
|
|
246
|
-
const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
247
|
-
if (prefetchPromises.has(normalizedPath)) {
|
|
248
|
-
return prefetchPromises.get(normalizedPath);
|
|
249
|
-
}
|
|
250
|
-
const { route } = findRoute(routes, normalizedPath);
|
|
251
|
-
if (!route) return;
|
|
252
|
-
const cacheKey = route.path;
|
|
253
|
-
if (pageCache.has(cacheKey)) return;
|
|
254
|
-
const promise = route.component().then((module) => {
|
|
255
|
-
pageCache.set(cacheKey, module.default);
|
|
256
|
-
if (module.metadata) {
|
|
257
|
-
metadataCache.set(cacheKey, module.metadata);
|
|
258
|
-
}
|
|
259
|
-
}).catch(() => {
|
|
260
|
-
});
|
|
261
|
-
prefetchPromises.set(normalizedPath, promise);
|
|
262
|
-
return promise;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// src/router/hooks.ts
|
|
266
|
-
import { useState, useEffect, useContext } from "react";
|
|
267
|
-
function useRouter() {
|
|
268
|
-
return useContext(RouterContext);
|
|
269
|
-
}
|
|
270
|
-
function useParams() {
|
|
271
|
-
return useContext(ParamsContext);
|
|
272
|
-
}
|
|
273
|
-
function usePathname() {
|
|
274
|
-
const { pathname } = useContext(RouterContext);
|
|
275
|
-
return pathname;
|
|
276
|
-
}
|
|
277
|
-
function useSearchParams() {
|
|
278
|
-
const [searchParams, setSearchParams] = useState(
|
|
279
|
-
() => new URLSearchParams(window.location.search)
|
|
280
|
-
);
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
const handleNavigate = () => {
|
|
283
|
-
setSearchParams(new URLSearchParams(window.location.search));
|
|
284
|
-
};
|
|
285
|
-
window.addEventListener(NAVIGATE_EVENT, handleNavigate);
|
|
286
|
-
window.addEventListener("popstate", handleNavigate);
|
|
287
|
-
return () => {
|
|
288
|
-
window.removeEventListener(NAVIGATE_EVENT, handleNavigate);
|
|
289
|
-
window.removeEventListener("popstate", handleNavigate);
|
|
290
|
-
};
|
|
291
|
-
}, []);
|
|
292
|
-
return searchParams;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// src/router/Link.tsx
|
|
296
|
-
import { useEffect as useEffect2, useCallback, useRef } from "react";
|
|
297
|
-
import { jsx } from "react/jsx-runtime";
|
|
298
|
-
function Link({
|
|
299
|
-
href,
|
|
300
|
-
children,
|
|
301
|
-
prefetch: shouldPrefetch = true,
|
|
302
|
-
scroll = true,
|
|
303
|
-
replace: shouldReplace = false,
|
|
304
|
-
shallow = false,
|
|
305
|
-
...props
|
|
306
|
-
}) {
|
|
307
|
-
const prefetchedRef = useRef(false);
|
|
308
|
-
const linkRef = useRef(null);
|
|
309
|
-
const getRoutes = useCallback(() => {
|
|
310
|
-
return window.__OLOVA_ROUTES__ || [];
|
|
311
|
-
}, []);
|
|
312
|
-
useEffect2(() => {
|
|
313
|
-
if (!shouldPrefetch || prefetchedRef.current || !linkRef.current) return;
|
|
314
|
-
const observer = new IntersectionObserver(
|
|
315
|
-
(entries) => {
|
|
316
|
-
if (entries[0].isIntersecting && !prefetchedRef.current) {
|
|
317
|
-
prefetchedRef.current = true;
|
|
318
|
-
prefetchRoute(getRoutes(), href);
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
{ rootMargin: "200px" }
|
|
322
|
-
);
|
|
323
|
-
observer.observe(linkRef.current);
|
|
324
|
-
return () => observer.disconnect();
|
|
325
|
-
}, [href, shouldPrefetch, getRoutes]);
|
|
326
|
-
const handleMouseEnter = useCallback(() => {
|
|
327
|
-
if (!prefetchedRef.current) {
|
|
328
|
-
prefetchedRef.current = true;
|
|
329
|
-
prefetchRoute(getRoutes(), href);
|
|
330
|
-
}
|
|
331
|
-
}, [href, getRoutes]);
|
|
332
|
-
const handleClick = useCallback((e) => {
|
|
333
|
-
if (e.ctrlKey || e.metaKey || e.shiftKey || e.button !== 0 || props.target === "_blank" || href.startsWith("http") || href.startsWith("//")) {
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
e.preventDefault();
|
|
337
|
-
if (shouldReplace) {
|
|
338
|
-
replace(href, { scroll, shallow });
|
|
339
|
-
} else {
|
|
340
|
-
navigate(href, { scroll, shallow });
|
|
341
|
-
}
|
|
342
|
-
}, [href, scroll, shallow, shouldReplace, props.target]);
|
|
343
|
-
return /* @__PURE__ */ jsx(
|
|
344
|
-
"a",
|
|
345
|
-
{
|
|
346
|
-
ref: linkRef,
|
|
347
|
-
href,
|
|
348
|
-
onClick: handleClick,
|
|
349
|
-
onMouseEnter: handleMouseEnter,
|
|
350
|
-
...props,
|
|
351
|
-
children
|
|
352
|
-
}
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// src/router/Router.tsx
|
|
357
|
-
import React2, { Suspense, useMemo, useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
358
|
-
|
|
359
|
-
// src/router/BuiltinComponents.tsx
|
|
360
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
361
|
-
function NotFound() {
|
|
362
|
-
return /* @__PURE__ */ jsxs("div", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [
|
|
363
|
-
/* @__PURE__ */ jsx2("h1", { style: { fontSize: "4rem", margin: 0 }, children: "404" }),
|
|
364
|
-
/* @__PURE__ */ jsx2("p", { style: { fontSize: "1.25rem", color: "#666" }, children: "This page could not be found." }),
|
|
365
|
-
/* @__PURE__ */ jsx2(Link, { href: "/", style: { color: "#0070f3" }, children: "Go back home" })
|
|
366
|
-
] });
|
|
367
|
-
}
|
|
368
|
-
function ErrorPage() {
|
|
369
|
-
return /* @__PURE__ */ jsxs("div", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [
|
|
370
|
-
/* @__PURE__ */ jsx2("h1", { style: { fontSize: "2rem", margin: 0 }, children: "Something went wrong" }),
|
|
371
|
-
/* @__PURE__ */ jsx2("p", { style: { fontSize: "1rem", color: "#666" }, children: "Failed to load this page." }),
|
|
372
|
-
/* @__PURE__ */ jsx2("button", { onClick: () => window.location.reload(), style: {
|
|
373
|
-
padding: "0.5rem 1rem",
|
|
374
|
-
background: "#0070f3",
|
|
375
|
-
color: "white",
|
|
376
|
-
border: "none",
|
|
377
|
-
borderRadius: "4px",
|
|
378
|
-
cursor: "pointer"
|
|
379
|
-
}, children: "Try again" })
|
|
380
|
-
] });
|
|
381
|
-
}
|
|
382
|
-
function LoadingIndicator() {
|
|
383
|
-
return /* @__PURE__ */ jsx2("div", { style: { padding: "2rem", textAlign: "center" }, children: /* @__PURE__ */ jsx2("div", { style: {
|
|
384
|
-
width: "40px",
|
|
385
|
-
height: "40px",
|
|
386
|
-
border: "3px solid #f3f3f3",
|
|
387
|
-
borderTop: "3px solid #0070f3",
|
|
388
|
-
borderRadius: "50%",
|
|
389
|
-
animation: "olova-spin 1s linear infinite",
|
|
390
|
-
margin: "0 auto"
|
|
391
|
-
} }) });
|
|
392
|
-
}
|
|
393
|
-
function NavigationProgress() {
|
|
394
|
-
return /* @__PURE__ */ jsx2("div", { style: {
|
|
395
|
-
position: "fixed",
|
|
396
|
-
top: 0,
|
|
397
|
-
left: 0,
|
|
398
|
-
right: 0,
|
|
399
|
-
height: "3px",
|
|
400
|
-
background: "linear-gradient(90deg, #0070f3, #00a8ff)",
|
|
401
|
-
animation: "olova-loading 1s ease-in-out infinite",
|
|
402
|
-
zIndex: 9999
|
|
403
|
-
} });
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// src/router/Styles.ts
|
|
407
|
-
function injectStyles() {
|
|
408
|
-
if (typeof document !== "undefined") {
|
|
409
|
-
const style = document.createElement("style");
|
|
410
|
-
style.textContent = `
|
|
411
|
-
@keyframes olova-loading {
|
|
412
|
-
0% { transform: translateX(-100%); }
|
|
413
|
-
50% { transform: translateX(0); }
|
|
414
|
-
100% { transform: translateX(100%); }
|
|
415
|
-
}
|
|
416
|
-
@keyframes olova-spin {
|
|
417
|
-
0% { transform: rotate(0deg); }
|
|
418
|
-
100% { transform: rotate(360deg); }
|
|
419
|
-
}
|
|
420
|
-
`;
|
|
421
|
-
document.head.appendChild(style);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// src/router/useRouterInternal.ts
|
|
426
|
-
import { useState as useState2, useEffect as useEffect3, useRef as useRef2, startTransition } from "react";
|
|
427
|
-
|
|
428
|
-
// src/router/Metadata.ts
|
|
429
|
-
function applyMetadata(metadata) {
|
|
430
|
-
if (!metadata || typeof document === "undefined") return;
|
|
431
|
-
if (metadata.title) {
|
|
432
|
-
document.title = metadata.title;
|
|
433
|
-
}
|
|
434
|
-
const metaTags = {
|
|
435
|
-
description: metadata.description,
|
|
436
|
-
keywords: metadata.keywords
|
|
437
|
-
};
|
|
438
|
-
for (const [name, content] of Object.entries(metaTags)) {
|
|
439
|
-
if (content) {
|
|
440
|
-
let meta = document.querySelector(`meta[name="${name}"]`);
|
|
441
|
-
if (!meta) {
|
|
442
|
-
meta = document.createElement("meta");
|
|
443
|
-
meta.setAttribute("name", name);
|
|
444
|
-
document.head.appendChild(meta);
|
|
445
|
-
}
|
|
446
|
-
meta.setAttribute("content", content);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (metadata.openGraph) {
|
|
450
|
-
const og = metadata.openGraph;
|
|
451
|
-
const ogTags = {
|
|
452
|
-
"og:title": og.title,
|
|
453
|
-
"og:description": og.description,
|
|
454
|
-
"og:type": og.type,
|
|
455
|
-
"og:url": og.url,
|
|
456
|
-
"og:image": og.image
|
|
457
|
-
};
|
|
458
|
-
for (const [property, content] of Object.entries(ogTags)) {
|
|
459
|
-
if (content) {
|
|
460
|
-
let meta = document.querySelector(`meta[property="${property}"]`);
|
|
461
|
-
if (!meta) {
|
|
462
|
-
meta = document.createElement("meta");
|
|
463
|
-
meta.setAttribute("property", property);
|
|
464
|
-
document.head.appendChild(meta);
|
|
465
|
-
}
|
|
466
|
-
meta.setAttribute("content", content);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/router/Query.ts
|
|
473
|
-
function parseQueryString(search) {
|
|
474
|
-
const params = new URLSearchParams(search);
|
|
475
|
-
const result = {};
|
|
476
|
-
params.forEach((value, key) => {
|
|
477
|
-
if (result[key]) {
|
|
478
|
-
if (Array.isArray(result[key])) {
|
|
479
|
-
result[key].push(value);
|
|
480
|
-
} else {
|
|
481
|
-
result[key] = [result[key], value];
|
|
482
|
-
}
|
|
483
|
-
} else {
|
|
484
|
-
result[key] = value;
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
return result;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// src/router/useRouterInternal.ts
|
|
491
|
-
function useRouterInternal(routes) {
|
|
492
|
-
const [currentPath, setCurrentPath] = useState2(() => {
|
|
493
|
-
if (typeof window === "undefined") return "/";
|
|
494
|
-
const path = window.location.pathname;
|
|
495
|
-
return path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
496
|
-
});
|
|
497
|
-
const [query, setQuery] = useState2(() => {
|
|
498
|
-
if (typeof window === "undefined") return {};
|
|
499
|
-
return parseQueryString(window.location.search);
|
|
500
|
-
});
|
|
501
|
-
const [components, setComponents] = useState2({ page: null, layouts: [] });
|
|
502
|
-
const [params, setParams] = useState2({});
|
|
503
|
-
const [isNavigating, setIsNavigating] = useState2(false);
|
|
504
|
-
const [isReady, setIsReady] = useState2(false);
|
|
505
|
-
const isHydrating = useRef2(true);
|
|
506
|
-
useEffect3(() => {
|
|
507
|
-
const handleNavigate = (e) => {
|
|
508
|
-
const customEvent = e;
|
|
509
|
-
const newPath = customEvent.detail?.path || window.location.pathname;
|
|
510
|
-
const normalizedPath = newPath.endsWith("/") && newPath !== "/" ? newPath.slice(0, -1) : newPath;
|
|
511
|
-
isHydrating.current = false;
|
|
512
|
-
startTransition(() => {
|
|
513
|
-
setCurrentPath(normalizedPath);
|
|
514
|
-
setQuery(parseQueryString(window.location.search));
|
|
515
|
-
});
|
|
516
|
-
};
|
|
517
|
-
const handlePopState = () => {
|
|
518
|
-
const path = window.location.pathname;
|
|
519
|
-
const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
520
|
-
isHydrating.current = false;
|
|
521
|
-
startTransition(() => {
|
|
522
|
-
setCurrentPath(normalizedPath);
|
|
523
|
-
setQuery(parseQueryString(window.location.search));
|
|
524
|
-
});
|
|
525
|
-
};
|
|
526
|
-
window.addEventListener(NAVIGATE_EVENT, handleNavigate);
|
|
527
|
-
window.addEventListener("popstate", handlePopState);
|
|
528
|
-
return () => {
|
|
529
|
-
window.removeEventListener(NAVIGATE_EVENT, handleNavigate);
|
|
530
|
-
window.removeEventListener("popstate", handlePopState);
|
|
531
|
-
};
|
|
532
|
-
}, []);
|
|
533
|
-
useEffect3(() => {
|
|
534
|
-
const { route, params: extractedParams } = findRoute(routes, currentPath);
|
|
535
|
-
const loadComponent = async (loader) => {
|
|
536
|
-
if (!loader) return void 0;
|
|
537
|
-
const module = await loader();
|
|
538
|
-
return module.default;
|
|
539
|
-
};
|
|
540
|
-
if (!route) {
|
|
541
|
-
const findParentLayouts = async () => {
|
|
542
|
-
const segments = currentPath.split("/").filter(Boolean);
|
|
543
|
-
for (let i = segments.length - 1; i >= 0; i--) {
|
|
544
|
-
const partialPath = "/" + segments.slice(0, i).join("/");
|
|
545
|
-
const { route: parentRoute } = findRoute(routes, partialPath);
|
|
546
|
-
if (parentRoute) {
|
|
547
|
-
const layouts = await Promise.all((parentRoute.layouts || []).map((l) => loadComponent(l)));
|
|
548
|
-
return layouts.filter((l) => !!l);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return [];
|
|
552
|
-
};
|
|
553
|
-
findParentLayouts().then((layouts) => {
|
|
554
|
-
setComponents((prev) => ({ ...prev, page: NotFound, layouts }));
|
|
555
|
-
setParams({});
|
|
556
|
-
setIsReady(true);
|
|
557
|
-
});
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
setParams(extractedParams);
|
|
561
|
-
if (!isHydrating.current) setIsNavigating(true);
|
|
562
|
-
const loadRoute = async () => {
|
|
563
|
-
try {
|
|
564
|
-
const [page, layouts, loading, error, notFound2, template] = await Promise.all([
|
|
565
|
-
loadComponent(route.component),
|
|
566
|
-
Promise.all((route.layouts || []).map((l) => loadComponent(l))),
|
|
567
|
-
loadComponent(route.loading),
|
|
568
|
-
loadComponent(route.error),
|
|
569
|
-
loadComponent(route.notFound),
|
|
570
|
-
loadComponent(route.template)
|
|
571
|
-
]);
|
|
572
|
-
if (route.metadata) {
|
|
573
|
-
metadataCache.set(route.path, route.metadata);
|
|
574
|
-
applyMetadata(route.metadata);
|
|
575
|
-
}
|
|
576
|
-
startTransition(() => {
|
|
577
|
-
setComponents({
|
|
578
|
-
page: page || null,
|
|
579
|
-
layouts: layouts.filter((l) => !!l),
|
|
580
|
-
loading,
|
|
581
|
-
error,
|
|
582
|
-
notFound: notFound2,
|
|
583
|
-
template
|
|
584
|
-
});
|
|
585
|
-
setIsNavigating(false);
|
|
586
|
-
setIsReady(true);
|
|
587
|
-
isHydrating.current = false;
|
|
588
|
-
});
|
|
589
|
-
} catch (err) {
|
|
590
|
-
console.error("[Olova] Failed to load route:", err);
|
|
591
|
-
routerEvents.emit("routeChangeError", err, currentPath);
|
|
592
|
-
setComponents({ page: ErrorPage, layouts: [] });
|
|
593
|
-
setIsNavigating(false);
|
|
594
|
-
setIsReady(true);
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
|
-
loadRoute();
|
|
598
|
-
}, [currentPath, routes]);
|
|
599
|
-
return { currentPath, query, components, params, isNavigating, isReady, isHydrating };
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// src/router/Router.tsx
|
|
603
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
604
|
-
injectStyles();
|
|
605
|
-
var ErrorBoundary = class extends React2.Component {
|
|
606
|
-
constructor(props) {
|
|
607
|
-
super(props);
|
|
608
|
-
this.reset = () => this.setState({ hasError: false, error: null });
|
|
609
|
-
this.state = { hasError: false, error: null };
|
|
610
|
-
}
|
|
611
|
-
static getDerivedStateFromError(error) {
|
|
612
|
-
return { hasError: true, error };
|
|
613
|
-
}
|
|
614
|
-
render() {
|
|
615
|
-
if (this.state.hasError && this.props.fallback) {
|
|
616
|
-
const Fallback = this.props.fallback;
|
|
617
|
-
return /* @__PURE__ */ jsx3(Fallback, { error: this.state.error, reset: this.reset });
|
|
618
|
-
}
|
|
619
|
-
return this.props.children;
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
function Router({ routes }) {
|
|
623
|
-
const routesRef = useRef3(routes);
|
|
624
|
-
routesRef.current = routes;
|
|
625
|
-
const {
|
|
626
|
-
currentPath,
|
|
627
|
-
query,
|
|
628
|
-
components,
|
|
629
|
-
params,
|
|
630
|
-
isNavigating,
|
|
631
|
-
isReady
|
|
632
|
-
} = useRouterInternal(routes);
|
|
633
|
-
useEffect4(() => {
|
|
634
|
-
const handleHashChange = () => {
|
|
635
|
-
routerEvents.emit("hashChangeStart", window.location.hash);
|
|
636
|
-
routerEvents.emit("hashChangeComplete", window.location.hash);
|
|
637
|
-
};
|
|
638
|
-
window.addEventListener("hashchange", handleHashChange);
|
|
639
|
-
return () => window.removeEventListener("hashchange", handleHashChange);
|
|
640
|
-
}, []);
|
|
641
|
-
const routerContextValue = useMemo(() => ({
|
|
642
|
-
pathname: currentPath,
|
|
643
|
-
params,
|
|
644
|
-
query,
|
|
645
|
-
asPath: typeof window !== "undefined" ? currentPath + window.location.search + window.location.hash : currentPath,
|
|
646
|
-
push: navigate,
|
|
647
|
-
replace,
|
|
648
|
-
back: () => typeof window !== "undefined" && window.history.back(),
|
|
649
|
-
forward: () => typeof window !== "undefined" && window.history.forward(),
|
|
650
|
-
prefetch: (url) => prefetchRoute(routesRef.current, url),
|
|
651
|
-
reload: () => typeof window !== "undefined" && window.location.reload(),
|
|
652
|
-
isReady,
|
|
653
|
-
isNavigating,
|
|
654
|
-
isFallback: false,
|
|
655
|
-
events: routerEvents
|
|
656
|
-
}), [currentPath, params, query, isReady, isNavigating]);
|
|
657
|
-
if (!components.page) return null;
|
|
658
|
-
const { page: Page, layouts, loading: Loading, error: ErrorComp } = components;
|
|
659
|
-
let content = /* @__PURE__ */ jsx3(ErrorBoundary, { fallback: ErrorComp, children: /* @__PURE__ */ jsx3(Page, { params, searchParams: query }) });
|
|
660
|
-
for (let i = layouts.length - 1; i >= 0; i--) {
|
|
661
|
-
const Layout = layouts[i];
|
|
662
|
-
content = /* @__PURE__ */ jsx3(ErrorBoundary, { fallback: ErrorComp, children: /* @__PURE__ */ jsx3(Layout, { children: content, params, searchParams: query }) }, i);
|
|
663
|
-
}
|
|
664
|
-
return /* @__PURE__ */ jsx3(RouterContext.Provider, { value: routerContextValue, children: /* @__PURE__ */ jsx3(ParamsContext.Provider, { value: params, children: /* @__PURE__ */ jsxs2(Suspense, { fallback: Loading ? /* @__PURE__ */ jsx3(Loading, {}) : /* @__PURE__ */ jsx3(LoadingIndicator, {}), children: [
|
|
665
|
-
content,
|
|
666
|
-
isNavigating && /* @__PURE__ */ jsx3(NavigationProgress, {})
|
|
667
|
-
] }) }) });
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// src/head/Head.tsx
|
|
671
|
-
import React4, { useEffect as useEffect5, useContext as useContext2 } from "react";
|
|
672
|
-
|
|
673
|
-
// src/head/HeadProvider.tsx
|
|
674
|
-
import { createContext as createContext2, useState as useState3 } from "react";
|
|
675
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
676
|
-
var HeadContext = createContext2(null);
|
|
677
|
-
function HeadProvider({ children }) {
|
|
678
|
-
const [state, setState] = useState3({
|
|
679
|
-
metas: /* @__PURE__ */ new Map(),
|
|
680
|
-
links: /* @__PURE__ */ new Map()
|
|
681
|
-
});
|
|
682
|
-
const update = (updates) => {
|
|
683
|
-
setState((prev) => ({ ...prev, ...updates }));
|
|
684
|
-
};
|
|
685
|
-
return /* @__PURE__ */ jsx4(HeadContext.Provider, { value: { state, update }, children });
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// src/head/Head.tsx
|
|
689
|
-
function Head({ children }) {
|
|
690
|
-
const context = useContext2(HeadContext);
|
|
691
|
-
useEffect5(() => {
|
|
692
|
-
React4.Children.forEach(children, (child) => {
|
|
693
|
-
if (!React4.isValidElement(child)) return;
|
|
694
|
-
const { type, props } = child;
|
|
695
|
-
if (type === "title") {
|
|
696
|
-
const titleProps = props;
|
|
697
|
-
if (typeof titleProps.children === "string") {
|
|
698
|
-
document.title = titleProps.children;
|
|
699
|
-
context?.update({ title: titleProps.children });
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
if (type === "meta") {
|
|
703
|
-
const metaProps = props;
|
|
704
|
-
const key = metaProps.name || metaProps.property || metaProps.httpEquiv;
|
|
705
|
-
if (key) {
|
|
706
|
-
let meta = document.querySelector(`meta[name="${key}"], meta[property="${key}"]`);
|
|
707
|
-
if (!meta) {
|
|
708
|
-
meta = document.createElement("meta");
|
|
709
|
-
if (metaProps.name) meta.setAttribute("name", metaProps.name);
|
|
710
|
-
if (metaProps.property) meta.setAttribute("property", metaProps.property);
|
|
711
|
-
document.head.appendChild(meta);
|
|
712
|
-
}
|
|
713
|
-
meta.setAttribute("content", metaProps.content);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
if (type === "link") {
|
|
717
|
-
const linkProps = props;
|
|
718
|
-
const key = linkProps.href;
|
|
719
|
-
let link = document.querySelector(`link[href="${key}"]`);
|
|
720
|
-
if (!link) {
|
|
721
|
-
link = document.createElement("link");
|
|
722
|
-
link.setAttribute("rel", linkProps.rel);
|
|
723
|
-
link.setAttribute("href", linkProps.href);
|
|
724
|
-
if (linkProps.as) link.setAttribute("as", linkProps.as);
|
|
725
|
-
document.head.appendChild(link);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
});
|
|
729
|
-
return () => {
|
|
730
|
-
};
|
|
731
|
-
}, [children, context]);
|
|
732
|
-
return null;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// src/head/ssr.ts
|
|
736
|
-
import React5 from "react";
|
|
737
|
-
|
|
738
|
-
// src/data/provider.tsx
|
|
739
|
-
import { createContext as createContext3 } from "react";
|
|
740
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
741
|
-
var PageDataContext = createContext3(null);
|
|
742
|
-
|
|
743
|
-
// src/data/hooks.ts
|
|
744
|
-
import { useState as useState4, useEffect as useEffect6, useContext as useContext3 } from "react";
|
|
745
|
-
function usePageData() {
|
|
746
|
-
const context = useContext3(PageDataContext);
|
|
747
|
-
const [data, setData] = useState4(context);
|
|
748
|
-
useEffect6(() => {
|
|
749
|
-
if (typeof window !== "undefined" && window.__OLOVA_PAGE_DATA__) {
|
|
750
|
-
setData(window.__OLOVA_PAGE_DATA__);
|
|
751
|
-
}
|
|
752
|
-
}, []);
|
|
753
|
-
return data;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// src/hydration/serialization.ts
|
|
757
|
-
function deserializeProps(props) {
|
|
758
|
-
const result = {};
|
|
759
|
-
for (const [key, value] of Object.entries(props)) {
|
|
760
|
-
if (value && typeof value === "object") {
|
|
761
|
-
if (value.__type === "Date") {
|
|
762
|
-
result[key] = new Date(value.value);
|
|
763
|
-
} else if (value.__type === "Map") {
|
|
764
|
-
result[key] = new Map(value.value);
|
|
765
|
-
} else if (value.__type === "Set") {
|
|
766
|
-
result[key] = new Set(value.value);
|
|
767
|
-
} else {
|
|
768
|
-
result[key] = deserializeProps(value);
|
|
769
|
-
}
|
|
770
|
-
} else {
|
|
771
|
-
result[key] = value;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
return result;
|
|
775
|
-
}
|
|
776
|
-
function parseFlightPayload() {
|
|
777
|
-
if (typeof window === "undefined") return null;
|
|
778
|
-
const flight = window.__FLIGHT__;
|
|
779
|
-
return flight || null;
|
|
780
|
-
}
|
|
781
|
-
function getFlightData(key) {
|
|
782
|
-
const payload = parseFlightPayload();
|
|
783
|
-
if (!payload) return null;
|
|
784
|
-
return payload.data[key] ?? null;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// src/hydration/index.ts
|
|
788
|
-
init_scheduler();
|
|
789
|
-
var clientScheduler = null;
|
|
790
|
-
function getHydrationScheduler() {
|
|
791
|
-
if (typeof window === "undefined") return null;
|
|
792
|
-
if (!clientScheduler) {
|
|
793
|
-
const { HydrationScheduler: HydrationScheduler2 } = (init_scheduler(), __toCommonJS(scheduler_exports));
|
|
794
|
-
clientScheduler = new HydrationScheduler2();
|
|
795
|
-
}
|
|
796
|
-
return clientScheduler;
|
|
797
|
-
}
|
|
798
|
-
export {
|
|
799
|
-
Head,
|
|
800
|
-
HeadProvider,
|
|
801
|
-
HydrationScheduler,
|
|
802
|
-
Link,
|
|
803
|
-
Router,
|
|
804
|
-
deserializeProps,
|
|
805
|
-
getFlightData,
|
|
806
|
-
getHydrationScheduler,
|
|
807
|
-
navigate,
|
|
808
|
-
parseFlightPayload,
|
|
809
|
-
replace,
|
|
810
|
-
usePageData,
|
|
811
|
-
useParams,
|
|
812
|
-
usePathname,
|
|
813
|
-
useRouter,
|
|
814
|
-
useSearchParams
|
|
815
|
-
};
|
|
816
|
-
//# sourceMappingURL=client.js.map
|