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