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/index.cjs DELETED
@@ -1,907 +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/index.ts
123
- var index_exports = {};
124
- __export(index_exports, {
125
- Head: () => Head,
126
- HeadProvider: () => HeadProvider,
127
- HydrationScheduler: () => HydrationScheduler,
128
- Link: () => Link,
129
- PageDataProvider: () => PageDataProvider,
130
- Router: () => Router,
131
- defineConfig: () => defineConfig,
132
- deserializeProps: () => deserializeProps,
133
- getFlightData: () => getFlightData,
134
- getHydrationScheduler: () => getHydrationScheduler,
135
- navigate: () => navigate,
136
- notFound: () => notFound,
137
- parseFlightPayload: () => parseFlightPayload,
138
- permanentRedirect: () => permanentRedirect,
139
- redirect: () => redirect,
140
- replace: () => replace,
141
- usePageData: () => usePageData,
142
- useParams: () => useParams,
143
- usePathname: () => usePathname,
144
- useRouter: () => useRouter,
145
- useSearchParams: () => useSearchParams
146
- });
147
- module.exports = __toCommonJS(index_exports);
148
-
149
- // src/core/config.ts
150
- var defaultConfig = {
151
- outDir: ".olova/dist",
152
- basePath: "/",
153
- trailingSlash: false,
154
- experimental: {
155
- prefetch: true,
156
- serverComponents: false
157
- },
158
- staticGeneration: {
159
- timeout: 3e4,
160
- fallback: "blocking"
161
- }
162
- };
163
- function defineConfig(config = {}) {
164
- return {
165
- ...defaultConfig,
166
- ...config,
167
- experimental: {
168
- ...defaultConfig.experimental,
169
- ...config.experimental
170
- },
171
- staticGeneration: {
172
- ...defaultConfig.staticGeneration,
173
- ...config.staticGeneration
174
- }
175
- };
176
- }
177
-
178
- // src/router/context.ts
179
- var import_react = require("react");
180
- var pageCache = /* @__PURE__ */ new Map();
181
- var prefetchPromises = /* @__PURE__ */ new Map();
182
- var metadataCache = /* @__PURE__ */ new Map();
183
- var NAVIGATE_EVENT = "olova:navigate";
184
- var eventHandlers = /* @__PURE__ */ new Map();
185
- var routerEvents = {
186
- on(event, handler) {
187
- if (!eventHandlers.has(event)) {
188
- eventHandlers.set(event, /* @__PURE__ */ new Set());
189
- }
190
- eventHandlers.get(event).add(handler);
191
- },
192
- off(event, handler) {
193
- eventHandlers.get(event)?.delete(handler);
194
- },
195
- emit(event, ...args) {
196
- eventHandlers.get(event)?.forEach((handler) => handler(...args));
197
- }
198
- };
199
- var RouterContext = (0, import_react.createContext)({
200
- pathname: "/",
201
- params: {},
202
- query: {},
203
- asPath: "/",
204
- push: async () => true,
205
- replace: async () => true,
206
- back: () => {
207
- },
208
- forward: () => {
209
- },
210
- prefetch: async () => {
211
- },
212
- reload: () => {
213
- },
214
- isReady: false,
215
- isNavigating: false,
216
- isFallback: false,
217
- events: routerEvents
218
- });
219
- var ParamsContext = (0, import_react.createContext)({});
220
-
221
- // src/router/navigation.ts
222
- function navigate(to, options = {}) {
223
- return new Promise((resolve) => {
224
- const { scroll = true } = options;
225
- routerEvents.emit("routeChangeStart", to, { shallow: options.shallow });
226
- routerEvents.emit("beforeHistoryChange", to);
227
- window.history.pushState({ scroll }, "", to);
228
- window.dispatchEvent(new CustomEvent(NAVIGATE_EVENT, {
229
- detail: { path: to, scroll }
230
- }));
231
- if (scroll) {
232
- window.scrollTo(0, 0);
233
- }
234
- routerEvents.emit("routeChangeComplete", to, { shallow: options.shallow });
235
- resolve(true);
236
- });
237
- }
238
- function replace(to, options = {}) {
239
- return new Promise((resolve) => {
240
- const { scroll = false } = options;
241
- routerEvents.emit("routeChangeStart", to, { shallow: options.shallow });
242
- routerEvents.emit("beforeHistoryChange", to);
243
- window.history.replaceState({ scroll }, "", to);
244
- window.dispatchEvent(new CustomEvent(NAVIGATE_EVENT, {
245
- detail: { path: to, scroll, replace: true }
246
- }));
247
- if (scroll) {
248
- window.scrollTo(0, 0);
249
- }
250
- routerEvents.emit("routeChangeComplete", to, { shallow: options.shallow });
251
- resolve(true);
252
- });
253
- }
254
- function redirect(url) {
255
- if (typeof window !== "undefined") {
256
- window.location.href = url;
257
- }
258
- throw new Error("NEXT_REDIRECT");
259
- }
260
- function permanentRedirect(url) {
261
- redirect(url);
262
- }
263
- function notFound() {
264
- throw new Error("NEXT_NOT_FOUND");
265
- }
266
-
267
- // src/router/match.ts
268
- function matchRoute(routePath, currentPath) {
269
- if (routePath === currentPath) {
270
- return { matches: true, params: {}, score: 100 };
271
- }
272
- const routeSegments = routePath.split("/").filter(Boolean);
273
- const pathSegments = currentPath.split("/").filter(Boolean);
274
- const params = {};
275
- let score = 0;
276
- for (let i = 0; i < routeSegments.length; i++) {
277
- const routeSegment = routeSegments[i];
278
- const pathSegment = pathSegments[i];
279
- if (routeSegment?.startsWith("*") && routeSegment?.endsWith("?")) {
280
- const paramName = routeSegment.slice(1, -1);
281
- const remainingPath = pathSegments.slice(i);
282
- params[paramName] = remainingPath.length > 0 ? remainingPath : [];
283
- score += 1;
284
- return { matches: true, params, score };
285
- }
286
- if (routeSegment?.startsWith("*")) {
287
- const paramName = routeSegment.slice(1);
288
- const remainingPath = pathSegments.slice(i);
289
- if (remainingPath.length > 0) {
290
- params[paramName] = remainingPath;
291
- score += 2;
292
- return { matches: true, params, score };
293
- }
294
- return { matches: false, params: {}, score: 0 };
295
- }
296
- if (routeSegment?.startsWith(":") || routeSegment?.startsWith("[") && routeSegment?.endsWith("]")) {
297
- if (!pathSegment) return { matches: false, params: {}, score: 0 };
298
- const paramName = routeSegment.startsWith(":") ? routeSegment.slice(1) : routeSegment.slice(1, -1);
299
- params[paramName] = pathSegment;
300
- score += 5;
301
- continue;
302
- }
303
- if (routeSegment !== pathSegment) return { matches: false, params: {}, score: 0 };
304
- score += 10;
305
- }
306
- if (routeSegments.length !== pathSegments.length) return { matches: false, params: {}, score: 0 };
307
- return { matches: true, params, score };
308
- }
309
- function findRoute(routes, path) {
310
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
311
- const exactMatch = routes.find((r) => r.path === normalizedPath);
312
- if (exactMatch) return { route: exactMatch, params: {} };
313
- let bestMatch = null;
314
- for (const route of routes) {
315
- const { matches, params, score } = matchRoute(route.path, normalizedPath);
316
- if (matches && (!bestMatch || score > bestMatch.score)) {
317
- bestMatch = { route, params, score };
318
- }
319
- }
320
- return bestMatch ? { route: bestMatch.route, params: bestMatch.params } : { route: null, params: {} };
321
- }
322
-
323
- // src/router/prefetch.ts
324
- async function prefetchRoute(routes, path) {
325
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
326
- if (prefetchPromises.has(normalizedPath)) {
327
- return prefetchPromises.get(normalizedPath);
328
- }
329
- const { route } = findRoute(routes, normalizedPath);
330
- if (!route) return;
331
- const cacheKey = route.path;
332
- if (pageCache.has(cacheKey)) return;
333
- const promise = route.component().then((module2) => {
334
- pageCache.set(cacheKey, module2.default);
335
- if (module2.metadata) {
336
- metadataCache.set(cacheKey, module2.metadata);
337
- }
338
- }).catch(() => {
339
- });
340
- prefetchPromises.set(normalizedPath, promise);
341
- return promise;
342
- }
343
-
344
- // src/router/hooks.ts
345
- var import_react2 = require("react");
346
- function useRouter() {
347
- return (0, import_react2.useContext)(RouterContext);
348
- }
349
- function useParams() {
350
- return (0, import_react2.useContext)(ParamsContext);
351
- }
352
- function usePathname() {
353
- const { pathname } = (0, import_react2.useContext)(RouterContext);
354
- return pathname;
355
- }
356
- function useSearchParams() {
357
- const [searchParams, setSearchParams] = (0, import_react2.useState)(
358
- () => new URLSearchParams(window.location.search)
359
- );
360
- (0, import_react2.useEffect)(() => {
361
- const handleNavigate = () => {
362
- setSearchParams(new URLSearchParams(window.location.search));
363
- };
364
- window.addEventListener(NAVIGATE_EVENT, handleNavigate);
365
- window.addEventListener("popstate", handleNavigate);
366
- return () => {
367
- window.removeEventListener(NAVIGATE_EVENT, handleNavigate);
368
- window.removeEventListener("popstate", handleNavigate);
369
- };
370
- }, []);
371
- return searchParams;
372
- }
373
-
374
- // src/router/Link.tsx
375
- var import_react3 = require("react");
376
- var import_jsx_runtime = require("react/jsx-runtime");
377
- function Link({
378
- href,
379
- children,
380
- prefetch: shouldPrefetch = true,
381
- scroll = true,
382
- replace: shouldReplace = false,
383
- shallow = false,
384
- ...props
385
- }) {
386
- const prefetchedRef = (0, import_react3.useRef)(false);
387
- const linkRef = (0, import_react3.useRef)(null);
388
- const getRoutes = (0, import_react3.useCallback)(() => {
389
- return window.__OLOVA_ROUTES__ || [];
390
- }, []);
391
- (0, import_react3.useEffect)(() => {
392
- if (!shouldPrefetch || prefetchedRef.current || !linkRef.current) return;
393
- const observer = new IntersectionObserver(
394
- (entries) => {
395
- if (entries[0].isIntersecting && !prefetchedRef.current) {
396
- prefetchedRef.current = true;
397
- prefetchRoute(getRoutes(), href);
398
- }
399
- },
400
- { rootMargin: "200px" }
401
- );
402
- observer.observe(linkRef.current);
403
- return () => observer.disconnect();
404
- }, [href, shouldPrefetch, getRoutes]);
405
- const handleMouseEnter = (0, import_react3.useCallback)(() => {
406
- if (!prefetchedRef.current) {
407
- prefetchedRef.current = true;
408
- prefetchRoute(getRoutes(), href);
409
- }
410
- }, [href, getRoutes]);
411
- const handleClick = (0, import_react3.useCallback)((e) => {
412
- if (e.ctrlKey || e.metaKey || e.shiftKey || e.button !== 0 || props.target === "_blank" || href.startsWith("http") || href.startsWith("//")) {
413
- return;
414
- }
415
- e.preventDefault();
416
- if (shouldReplace) {
417
- replace(href, { scroll, shallow });
418
- } else {
419
- navigate(href, { scroll, shallow });
420
- }
421
- }, [href, scroll, shallow, shouldReplace, props.target]);
422
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
423
- "a",
424
- {
425
- ref: linkRef,
426
- href,
427
- onClick: handleClick,
428
- onMouseEnter: handleMouseEnter,
429
- ...props,
430
- children
431
- }
432
- );
433
- }
434
-
435
- // src/router/Router.tsx
436
- var import_react5 = __toESM(require("react"), 1);
437
-
438
- // src/router/BuiltinComponents.tsx
439
- var import_jsx_runtime2 = require("react/jsx-runtime");
440
- function NotFound() {
441
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [
442
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: { fontSize: "4rem", margin: 0 }, children: "404" }),
443
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: "1.25rem", color: "#666" }, children: "This page could not be found." }),
444
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Link, { href: "/", style: { color: "#0070f3" }, children: "Go back home" })
445
- ] });
446
- }
447
- function ErrorPage() {
448
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [
449
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: { fontSize: "2rem", margin: 0 }, children: "Something went wrong" }),
450
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: "1rem", color: "#666" }, children: "Failed to load this page." }),
451
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => window.location.reload(), style: {
452
- padding: "0.5rem 1rem",
453
- background: "#0070f3",
454
- color: "white",
455
- border: "none",
456
- borderRadius: "4px",
457
- cursor: "pointer"
458
- }, children: "Try again" })
459
- ] });
460
- }
461
- function LoadingIndicator() {
462
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: "2rem", textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
463
- width: "40px",
464
- height: "40px",
465
- border: "3px solid #f3f3f3",
466
- borderTop: "3px solid #0070f3",
467
- borderRadius: "50%",
468
- animation: "olova-spin 1s linear infinite",
469
- margin: "0 auto"
470
- } }) });
471
- }
472
- function NavigationProgress() {
473
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
474
- position: "fixed",
475
- top: 0,
476
- left: 0,
477
- right: 0,
478
- height: "3px",
479
- background: "linear-gradient(90deg, #0070f3, #00a8ff)",
480
- animation: "olova-loading 1s ease-in-out infinite",
481
- zIndex: 9999
482
- } });
483
- }
484
-
485
- // src/router/Styles.ts
486
- function injectStyles() {
487
- if (typeof document !== "undefined") {
488
- const style = document.createElement("style");
489
- style.textContent = `
490
- @keyframes olova-loading {
491
- 0% { transform: translateX(-100%); }
492
- 50% { transform: translateX(0); }
493
- 100% { transform: translateX(100%); }
494
- }
495
- @keyframes olova-spin {
496
- 0% { transform: rotate(0deg); }
497
- 100% { transform: rotate(360deg); }
498
- }
499
- `;
500
- document.head.appendChild(style);
501
- }
502
- }
503
-
504
- // src/router/useRouterInternal.ts
505
- var import_react4 = require("react");
506
-
507
- // src/router/Metadata.ts
508
- function applyMetadata(metadata) {
509
- if (!metadata || typeof document === "undefined") return;
510
- if (metadata.title) {
511
- document.title = metadata.title;
512
- }
513
- const metaTags = {
514
- description: metadata.description,
515
- keywords: metadata.keywords
516
- };
517
- for (const [name, content] of Object.entries(metaTags)) {
518
- if (content) {
519
- let meta = document.querySelector(`meta[name="${name}"]`);
520
- if (!meta) {
521
- meta = document.createElement("meta");
522
- meta.setAttribute("name", name);
523
- document.head.appendChild(meta);
524
- }
525
- meta.setAttribute("content", content);
526
- }
527
- }
528
- if (metadata.openGraph) {
529
- const og = metadata.openGraph;
530
- const ogTags = {
531
- "og:title": og.title,
532
- "og:description": og.description,
533
- "og:type": og.type,
534
- "og:url": og.url,
535
- "og:image": og.image
536
- };
537
- for (const [property, content] of Object.entries(ogTags)) {
538
- if (content) {
539
- let meta = document.querySelector(`meta[property="${property}"]`);
540
- if (!meta) {
541
- meta = document.createElement("meta");
542
- meta.setAttribute("property", property);
543
- document.head.appendChild(meta);
544
- }
545
- meta.setAttribute("content", content);
546
- }
547
- }
548
- }
549
- }
550
-
551
- // src/router/Query.ts
552
- function parseQueryString(search) {
553
- const params = new URLSearchParams(search);
554
- const result = {};
555
- params.forEach((value, key) => {
556
- if (result[key]) {
557
- if (Array.isArray(result[key])) {
558
- result[key].push(value);
559
- } else {
560
- result[key] = [result[key], value];
561
- }
562
- } else {
563
- result[key] = value;
564
- }
565
- });
566
- return result;
567
- }
568
-
569
- // src/router/useRouterInternal.ts
570
- function useRouterInternal(routes) {
571
- const [currentPath, setCurrentPath] = (0, import_react4.useState)(() => {
572
- if (typeof window === "undefined") return "/";
573
- const path = window.location.pathname;
574
- return path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
575
- });
576
- const [query, setQuery] = (0, import_react4.useState)(() => {
577
- if (typeof window === "undefined") return {};
578
- return parseQueryString(window.location.search);
579
- });
580
- const [components, setComponents] = (0, import_react4.useState)({ page: null, layouts: [] });
581
- const [params, setParams] = (0, import_react4.useState)({});
582
- const [isNavigating, setIsNavigating] = (0, import_react4.useState)(false);
583
- const [isReady, setIsReady] = (0, import_react4.useState)(false);
584
- const isHydrating = (0, import_react4.useRef)(true);
585
- (0, import_react4.useEffect)(() => {
586
- const handleNavigate = (e) => {
587
- const customEvent = e;
588
- const newPath = customEvent.detail?.path || window.location.pathname;
589
- const normalizedPath = newPath.endsWith("/") && newPath !== "/" ? newPath.slice(0, -1) : newPath;
590
- isHydrating.current = false;
591
- (0, import_react4.startTransition)(() => {
592
- setCurrentPath(normalizedPath);
593
- setQuery(parseQueryString(window.location.search));
594
- });
595
- };
596
- const handlePopState = () => {
597
- const path = window.location.pathname;
598
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
599
- isHydrating.current = false;
600
- (0, import_react4.startTransition)(() => {
601
- setCurrentPath(normalizedPath);
602
- setQuery(parseQueryString(window.location.search));
603
- });
604
- };
605
- window.addEventListener(NAVIGATE_EVENT, handleNavigate);
606
- window.addEventListener("popstate", handlePopState);
607
- return () => {
608
- window.removeEventListener(NAVIGATE_EVENT, handleNavigate);
609
- window.removeEventListener("popstate", handlePopState);
610
- };
611
- }, []);
612
- (0, import_react4.useEffect)(() => {
613
- const { route, params: extractedParams } = findRoute(routes, currentPath);
614
- const loadComponent = async (loader) => {
615
- if (!loader) return void 0;
616
- const module2 = await loader();
617
- return module2.default;
618
- };
619
- if (!route) {
620
- const findParentLayouts = async () => {
621
- const segments = currentPath.split("/").filter(Boolean);
622
- for (let i = segments.length - 1; i >= 0; i--) {
623
- const partialPath = "/" + segments.slice(0, i).join("/");
624
- const { route: parentRoute } = findRoute(routes, partialPath);
625
- if (parentRoute) {
626
- const layouts = await Promise.all((parentRoute.layouts || []).map((l) => loadComponent(l)));
627
- return layouts.filter((l) => !!l);
628
- }
629
- }
630
- return [];
631
- };
632
- findParentLayouts().then((layouts) => {
633
- setComponents((prev) => ({ ...prev, page: NotFound, layouts }));
634
- setParams({});
635
- setIsReady(true);
636
- });
637
- return;
638
- }
639
- setParams(extractedParams);
640
- if (!isHydrating.current) setIsNavigating(true);
641
- const loadRoute = async () => {
642
- try {
643
- const [page, layouts, loading, error, notFound2, template] = await Promise.all([
644
- loadComponent(route.component),
645
- Promise.all((route.layouts || []).map((l) => loadComponent(l))),
646
- loadComponent(route.loading),
647
- loadComponent(route.error),
648
- loadComponent(route.notFound),
649
- loadComponent(route.template)
650
- ]);
651
- if (route.metadata) {
652
- metadataCache.set(route.path, route.metadata);
653
- applyMetadata(route.metadata);
654
- }
655
- (0, import_react4.startTransition)(() => {
656
- setComponents({
657
- page: page || null,
658
- layouts: layouts.filter((l) => !!l),
659
- loading,
660
- error,
661
- notFound: notFound2,
662
- template
663
- });
664
- setIsNavigating(false);
665
- setIsReady(true);
666
- isHydrating.current = false;
667
- });
668
- } catch (err) {
669
- console.error("[Olova] Failed to load route:", err);
670
- routerEvents.emit("routeChangeError", err, currentPath);
671
- setComponents({ page: ErrorPage, layouts: [] });
672
- setIsNavigating(false);
673
- setIsReady(true);
674
- }
675
- };
676
- loadRoute();
677
- }, [currentPath, routes]);
678
- return { currentPath, query, components, params, isNavigating, isReady, isHydrating };
679
- }
680
-
681
- // src/router/Router.tsx
682
- var import_jsx_runtime3 = require("react/jsx-runtime");
683
- injectStyles();
684
- var ErrorBoundary = class extends import_react5.default.Component {
685
- constructor(props) {
686
- super(props);
687
- this.reset = () => this.setState({ hasError: false, error: null });
688
- this.state = { hasError: false, error: null };
689
- }
690
- static getDerivedStateFromError(error) {
691
- return { hasError: true, error };
692
- }
693
- render() {
694
- if (this.state.hasError && this.props.fallback) {
695
- const Fallback = this.props.fallback;
696
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Fallback, { error: this.state.error, reset: this.reset });
697
- }
698
- return this.props.children;
699
- }
700
- };
701
- function Router({ routes }) {
702
- const routesRef = (0, import_react5.useRef)(routes);
703
- routesRef.current = routes;
704
- const {
705
- currentPath,
706
- query,
707
- components,
708
- params,
709
- isNavigating,
710
- isReady
711
- } = useRouterInternal(routes);
712
- (0, import_react5.useEffect)(() => {
713
- const handleHashChange = () => {
714
- routerEvents.emit("hashChangeStart", window.location.hash);
715
- routerEvents.emit("hashChangeComplete", window.location.hash);
716
- };
717
- window.addEventListener("hashchange", handleHashChange);
718
- return () => window.removeEventListener("hashchange", handleHashChange);
719
- }, []);
720
- const routerContextValue = (0, import_react5.useMemo)(() => ({
721
- pathname: currentPath,
722
- params,
723
- query,
724
- asPath: typeof window !== "undefined" ? currentPath + window.location.search + window.location.hash : currentPath,
725
- push: navigate,
726
- replace,
727
- back: () => typeof window !== "undefined" && window.history.back(),
728
- forward: () => typeof window !== "undefined" && window.history.forward(),
729
- prefetch: (url) => prefetchRoute(routesRef.current, url),
730
- reload: () => typeof window !== "undefined" && window.location.reload(),
731
- isReady,
732
- isNavigating,
733
- isFallback: false,
734
- events: routerEvents
735
- }), [currentPath, params, query, isReady, isNavigating]);
736
- if (!components.page) return null;
737
- const { page: Page, layouts, loading: Loading, error: ErrorComp } = components;
738
- let content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ErrorBoundary, { fallback: ErrorComp, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Page, { params, searchParams: query }) });
739
- for (let i = layouts.length - 1; i >= 0; i--) {
740
- const Layout = layouts[i];
741
- content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ErrorBoundary, { fallback: ErrorComp, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Layout, { children: content, params, searchParams: query }) }, i);
742
- }
743
- 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: [
744
- content,
745
- isNavigating && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NavigationProgress, {})
746
- ] }) }) });
747
- }
748
-
749
- // src/head/Head.tsx
750
- var import_react7 = __toESM(require("react"), 1);
751
-
752
- // src/head/HeadProvider.tsx
753
- var import_react6 = require("react");
754
- var import_jsx_runtime4 = require("react/jsx-runtime");
755
- var HeadContext = (0, import_react6.createContext)(null);
756
- function HeadProvider({ children }) {
757
- const [state, setState] = (0, import_react6.useState)({
758
- metas: /* @__PURE__ */ new Map(),
759
- links: /* @__PURE__ */ new Map()
760
- });
761
- const update = (updates) => {
762
- setState((prev) => ({ ...prev, ...updates }));
763
- };
764
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadContext.Provider, { value: { state, update }, children });
765
- }
766
-
767
- // src/head/Head.tsx
768
- function Head({ children }) {
769
- const context = (0, import_react7.useContext)(HeadContext);
770
- (0, import_react7.useEffect)(() => {
771
- import_react7.default.Children.forEach(children, (child) => {
772
- if (!import_react7.default.isValidElement(child)) return;
773
- const { type, props } = child;
774
- if (type === "title") {
775
- const titleProps = props;
776
- if (typeof titleProps.children === "string") {
777
- document.title = titleProps.children;
778
- context?.update({ title: titleProps.children });
779
- }
780
- }
781
- if (type === "meta") {
782
- const metaProps = props;
783
- const key = metaProps.name || metaProps.property || metaProps.httpEquiv;
784
- if (key) {
785
- let meta = document.querySelector(`meta[name="${key}"], meta[property="${key}"]`);
786
- if (!meta) {
787
- meta = document.createElement("meta");
788
- if (metaProps.name) meta.setAttribute("name", metaProps.name);
789
- if (metaProps.property) meta.setAttribute("property", metaProps.property);
790
- document.head.appendChild(meta);
791
- }
792
- meta.setAttribute("content", metaProps.content);
793
- }
794
- }
795
- if (type === "link") {
796
- const linkProps = props;
797
- const key = linkProps.href;
798
- let link = document.querySelector(`link[href="${key}"]`);
799
- if (!link) {
800
- link = document.createElement("link");
801
- link.setAttribute("rel", linkProps.rel);
802
- link.setAttribute("href", linkProps.href);
803
- if (linkProps.as) link.setAttribute("as", linkProps.as);
804
- document.head.appendChild(link);
805
- }
806
- }
807
- });
808
- return () => {
809
- };
810
- }, [children, context]);
811
- return null;
812
- }
813
-
814
- // src/head/ssr.ts
815
- var import_react8 = __toESM(require("react"), 1);
816
-
817
- // src/data/provider.tsx
818
- var import_react9 = require("react");
819
- var import_jsx_runtime5 = require("react/jsx-runtime");
820
- var PageDataContext = (0, import_react9.createContext)(null);
821
- function PageDataProvider({
822
- children,
823
- data
824
- }) {
825
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PageDataContext.Provider, { value: data, children });
826
- }
827
-
828
- // src/data/hooks.ts
829
- var import_react10 = require("react");
830
- function usePageData() {
831
- const context = (0, import_react10.useContext)(PageDataContext);
832
- const [data, setData] = (0, import_react10.useState)(context);
833
- (0, import_react10.useEffect)(() => {
834
- if (typeof window !== "undefined" && window.__OLOVA_PAGE_DATA__) {
835
- setData(window.__OLOVA_PAGE_DATA__);
836
- }
837
- }, []);
838
- return data;
839
- }
840
-
841
- // src/hydration/serialization.ts
842
- function deserializeProps(props) {
843
- const result = {};
844
- for (const [key, value] of Object.entries(props)) {
845
- if (value && typeof value === "object") {
846
- if (value.__type === "Date") {
847
- result[key] = new Date(value.value);
848
- } else if (value.__type === "Map") {
849
- result[key] = new Map(value.value);
850
- } else if (value.__type === "Set") {
851
- result[key] = new Set(value.value);
852
- } else {
853
- result[key] = deserializeProps(value);
854
- }
855
- } else {
856
- result[key] = value;
857
- }
858
- }
859
- return result;
860
- }
861
- function parseFlightPayload() {
862
- if (typeof window === "undefined") return null;
863
- const flight = window.__FLIGHT__;
864
- return flight || null;
865
- }
866
- function getFlightData(key) {
867
- const payload = parseFlightPayload();
868
- if (!payload) return null;
869
- return payload.data[key] ?? null;
870
- }
871
-
872
- // src/hydration/index.ts
873
- init_scheduler();
874
- var clientScheduler = null;
875
- function getHydrationScheduler() {
876
- if (typeof window === "undefined") return null;
877
- if (!clientScheduler) {
878
- const { HydrationScheduler: HydrationScheduler2 } = (init_scheduler(), __toCommonJS(scheduler_exports));
879
- clientScheduler = new HydrationScheduler2();
880
- }
881
- return clientScheduler;
882
- }
883
- // Annotate the CommonJS export names for ESM import in node:
884
- 0 && (module.exports = {
885
- Head,
886
- HeadProvider,
887
- HydrationScheduler,
888
- Link,
889
- PageDataProvider,
890
- Router,
891
- defineConfig,
892
- deserializeProps,
893
- getFlightData,
894
- getHydrationScheduler,
895
- navigate,
896
- notFound,
897
- parseFlightPayload,
898
- permanentRedirect,
899
- redirect,
900
- replace,
901
- usePageData,
902
- useParams,
903
- usePathname,
904
- useRouter,
905
- useSearchParams
906
- });
907
- //# sourceMappingURL=index.cjs.map