olova 2.0.61 → 2.0.63

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 (80) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +42 -61
  3. package/dist/compiler.d.ts +44 -0
  4. package/dist/compiler.js +2139 -0
  5. package/dist/compiler.js.map +1 -0
  6. package/dist/core.d.ts +4 -0
  7. package/dist/core.js +859 -0
  8. package/dist/core.js.map +1 -0
  9. package/dist/global.d.ts +15 -0
  10. package/dist/global.js +226 -0
  11. package/dist/global.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +2302 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/runtime.d.ts +89 -0
  16. package/dist/runtime.js +633 -0
  17. package/dist/runtime.js.map +1 -0
  18. package/dist/signals-core-BdfWh1Yt.d.ts +43 -0
  19. package/dist/vite.d.ts +5 -0
  20. package/dist/vite.js +2302 -0
  21. package/dist/vite.js.map +1 -0
  22. package/package.json +83 -65
  23. package/dist/chunk-D7SIC5TC.js +0 -367
  24. package/dist/chunk-D7SIC5TC.js.map +0 -1
  25. package/dist/entry-server.cjs +0 -120
  26. package/dist/entry-server.cjs.map +0 -1
  27. package/dist/entry-server.js +0 -115
  28. package/dist/entry-server.js.map +0 -1
  29. package/dist/entry-worker.cjs +0 -133
  30. package/dist/entry-worker.cjs.map +0 -1
  31. package/dist/entry-worker.js +0 -127
  32. package/dist/entry-worker.js.map +0 -1
  33. package/dist/main.cjs +0 -18
  34. package/dist/main.cjs.map +0 -1
  35. package/dist/main.js +0 -16
  36. package/dist/main.js.map +0 -1
  37. package/dist/olova.cjs +0 -1680
  38. package/dist/olova.cjs.map +0 -1
  39. package/dist/olova.d.cts +0 -72
  40. package/dist/olova.d.ts +0 -72
  41. package/dist/olova.js +0 -1321
  42. package/dist/olova.js.map +0 -1
  43. package/dist/performance.cjs +0 -386
  44. package/dist/performance.cjs.map +0 -1
  45. package/dist/performance.js +0 -3
  46. package/dist/performance.js.map +0 -1
  47. package/dist/router.cjs +0 -646
  48. package/dist/router.cjs.map +0 -1
  49. package/dist/router.d.cts +0 -113
  50. package/dist/router.d.ts +0 -113
  51. package/dist/router.js +0 -632
  52. package/dist/router.js.map +0 -1
  53. package/main.tsx +0 -76
  54. package/olova.ts +0 -619
  55. package/src/entry-server.tsx +0 -165
  56. package/src/entry-worker.tsx +0 -201
  57. package/src/generator/index.ts +0 -409
  58. package/src/hydration/flight.ts +0 -320
  59. package/src/hydration/index.ts +0 -12
  60. package/src/hydration/types.ts +0 -225
  61. package/src/logger.ts +0 -182
  62. package/src/main.tsx +0 -24
  63. package/src/performance.ts +0 -488
  64. package/src/plugin/index.ts +0 -204
  65. package/src/router/ErrorBoundary.tsx +0 -145
  66. package/src/router/Link.tsx +0 -117
  67. package/src/router/OlovaRouter.tsx +0 -354
  68. package/src/router/Outlet.tsx +0 -8
  69. package/src/router/context.ts +0 -117
  70. package/src/router/index.ts +0 -29
  71. package/src/router/matching.ts +0 -63
  72. package/src/router/router.tsx +0 -23
  73. package/src/router/search-params.ts +0 -29
  74. package/src/scanner/index.ts +0 -114
  75. package/src/types/index.ts +0 -190
  76. package/src/utils/export.ts +0 -85
  77. package/src/utils/index.ts +0 -4
  78. package/src/utils/naming.ts +0 -54
  79. package/src/utils/path.ts +0 -45
  80. package/tsup.config.ts +0 -35
@@ -1,165 +0,0 @@
1
- import { StrictMode, Suspense } from 'react';
2
- import { renderToString } from 'react-dom/server';
3
- import { QueryClient, QueryClientProvider, dehydrate, HydrationBoundary } from '@tanstack/react-query';
4
- // @ts-expect-error - Virtual module resolved by vite-plugin-olova
5
- import { layouts, notFoundPages, OlovaRouter, Outlet, routes } from 'virtual:olova-app';
6
-
7
- // Wrapper to support "children" prop style layouts
8
- const wrappedLayouts = layouts.map((item: any) => ({
9
- ...item,
10
- layout: (props: any) => (
11
- <item.layout {...props}>
12
- <Outlet />
13
- </item.layout>
14
- )
15
- }));
16
-
17
- /**
18
- * Find matching route and extract params
19
- */
20
- function matchRoute(url: string): { route: any; params: Record<string, string> } | null {
21
- const pathname = url.split('?')[0];
22
-
23
- for (const route of routes) {
24
- // Exact match
25
- if (route.path === pathname) {
26
- return { route, params: {} };
27
- }
28
-
29
- // Dynamic segments
30
- if (route.path.includes(':') || route.path.includes('*')) {
31
- const pattern = route.path
32
- .replace(/\*/g, '(.*)')
33
- .replace(/:[^/]+/g, '([^/]+)');
34
- const regex = new RegExp(`^${pattern}$`);
35
- const match = pathname.match(regex);
36
-
37
- if (match) {
38
- const params: Record<string, string> = {};
39
- const paramNames = route.path.match(/:[^/]+|\*/g) || [];
40
-
41
- paramNames.forEach((name: string, i: number) => {
42
- if (name === '*') {
43
- params['*'] = match[i + 1];
44
- } else {
45
- params[name.slice(1)] = match[i + 1];
46
- }
47
- });
48
-
49
- return { route, params };
50
- }
51
- }
52
- }
53
-
54
- return null;
55
- }
56
-
57
- /**
58
- * Run loader for SSG builds
59
- * Simplified: loader just returns data directly
60
- */
61
- async function runLoader(url: string): Promise<Record<string, unknown> | null> {
62
- const match = matchRoute(url);
63
-
64
- if (!match || !match.route.loader) {
65
- return null;
66
- }
67
-
68
- try {
69
- // Simple call - loader returns data directly
70
- // Use a valid URL for Request (requires absolute URL)
71
- const baseUrl = 'http://localhost';
72
- const result = await match.route.loader({
73
- request: new Request(new URL(url, baseUrl)),
74
- params: match.params
75
- });
76
-
77
- // If it's a Response, skip
78
- if (result instanceof Response) {
79
- return null;
80
- }
81
-
82
- // Direct data return (simplified syntax)
83
- return result as Record<string, unknown>;
84
- } catch (error) {
85
- console.error(`[SSR] Loader error for ${url}:`, error);
86
- return null;
87
- }
88
- }
89
-
90
- export async function render(url: string) {
91
- // Always recreate the window mock to ensure clean state for every SSG path
92
- const globalAny = globalThis as any;
93
- globalAny.window = {
94
- location: { pathname: url, search: '', hash: '' },
95
- addEventListener: () => {},
96
- removeEventListener: () => {},
97
- history: {
98
- pushState: () => {},
99
- replaceState: () => {},
100
- location: { pathname: url, search: '', hash: '' }
101
- }
102
- };
103
- globalAny.location = globalAny.window.location;
104
- globalAny.document = {
105
- createElement: () => ({}),
106
- getElementsByTagName: () => [],
107
- getElementById: () => null,
108
- querySelector: () => null,
109
- title: '',
110
- location: globalAny.window.location
111
- };
112
-
113
- // Run loader for data-only SSR
114
- const loaderData = await runLoader(url);
115
-
116
- // Inject loader data into window for components to access
117
- if (loaderData) {
118
- globalAny.window.__LOADER_DATA__ = loaderData;
119
- }
120
-
121
- // Create QueryClient for SSR with pre-loaded data
122
- const queryClient = new QueryClient({
123
- defaultOptions: {
124
- queries: {
125
- staleTime: Infinity, // Data is fresh during SSR
126
- gcTime: Infinity,
127
- },
128
- },
129
- });
130
-
131
- // Pre-populate query client with loader data
132
- if (loaderData) {
133
- // Convert loader data to query state format
134
- // If loader returns { user: {...} }, we hydrate it
135
- Object.entries(loaderData).forEach(([key, value]) => {
136
- queryClient.setQueryData([key], value);
137
- });
138
- }
139
-
140
- // Get dehydrated state for client hydration
141
- const dehydratedState = dehydrate(queryClient);
142
-
143
- // Wrap in Suspense to handle lazy-loaded components
144
- const html = renderToString(
145
- <StrictMode>
146
- <QueryClientProvider client={queryClient}>
147
- <HydrationBoundary state={dehydratedState}>
148
- <Suspense fallback={null}>
149
- <OlovaRouter routes={routes} layouts={wrappedLayouts} notFoundPages={notFoundPages} />
150
- </Suspense>
151
- </HydrationBoundary>
152
- </QueryClientProvider>
153
- </StrictMode>
154
- );
155
-
156
- // Return both html and dehydrated state for flight serialization
157
- return {
158
- html,
159
- loaderData,
160
- queryState: dehydratedState
161
- };
162
- }
163
-
164
- export { routes };
165
-
@@ -1,201 +0,0 @@
1
- /**
2
- * Olova Cloudflare Workers Entry
3
- * Auto-generated by plugin - handles SSR for routes with loaders only
4
- */
5
-
6
- import { renderToString } from 'react-dom/server';
7
- import { StrictMode, Suspense } from 'react';
8
- import { QueryClient, QueryClientProvider, dehydrate, HydrationBoundary } from '@tanstack/react-query';
9
- // @ts-expect-error - Virtual module resolved by vite-plugin-olova
10
- import { layouts, notFoundPages, OlovaRouter, Outlet, routes } from 'virtual:olova-app';
11
-
12
- // =============================================================================
13
- // TYPES
14
- // =============================================================================
15
-
16
- interface RouteMatch {
17
- route: any;
18
- params: Record<string, string>;
19
- }
20
-
21
- // =============================================================================
22
- // ROUTE MATCHING
23
- // =============================================================================
24
-
25
- function matchRoute(pathname: string): RouteMatch | null {
26
- for (const route of routes) {
27
- // Exact match
28
- if (route.path === pathname) {
29
- return { route, params: {} };
30
- }
31
-
32
- // Dynamic segments
33
- if (route.path.includes(':') || route.path.includes('*')) {
34
- const pattern = route.path
35
- .replace(/\*/g, '(.*)')
36
- .replace(/:[^/]+/g, '([^/]+)');
37
- const regex = new RegExp(`^${pattern}$`);
38
- const match = pathname.match(regex);
39
-
40
- if (match) {
41
- const params: Record<string, string> = {};
42
- const paramNames = route.path.match(/:[^/]+|\*/g) || [];
43
-
44
- paramNames.forEach((name: string, i: number) => {
45
- const key = name === '*' ? '*' : name.slice(1);
46
- params[key] = match[i + 1];
47
- });
48
-
49
- return { route, params };
50
- }
51
- }
52
- }
53
-
54
- return null;
55
- }
56
-
57
- // =============================================================================
58
- // SSR RENDERING
59
- // =============================================================================
60
-
61
- // Wrapper to support "children" prop style layouts
62
- const wrappedLayouts = layouts.map((item: any) => ({
63
- ...item,
64
- layout: (props: any) => (
65
- <item.layout {...props}>
66
- <Outlet />
67
- </item.layout>
68
- )
69
- }));
70
-
71
- async function render(
72
- pathname: string,
73
- loaderData?: Record<string, unknown>
74
- ): Promise<{ html: string; queryState: unknown }> {
75
- // Mock browser globals
76
- const g = globalThis as any;
77
- g.window = {
78
- location: { pathname, search: '', hash: '' },
79
- addEventListener: () => {},
80
- removeEventListener: () => {},
81
- history: { pushState: () => {}, replaceState: () => {} },
82
- __LOADER_DATA__: loaderData
83
- };
84
- g.__LOADER_DATA__ = loaderData;
85
-
86
- // Create QueryClient with pre-loaded data
87
- const queryClient = new QueryClient({
88
- defaultOptions: {
89
- queries: {
90
- staleTime: Infinity,
91
- gcTime: Infinity,
92
- },
93
- },
94
- });
95
-
96
- // Pre-populate query client with loader data
97
- if (loaderData) {
98
- Object.entries(loaderData).forEach(([key, value]) => {
99
- queryClient.setQueryData([key], value);
100
- });
101
- }
102
-
103
- // Get dehydrated state for client hydration
104
- const queryState = dehydrate(queryClient);
105
-
106
- const html = renderToString(
107
- <StrictMode>
108
- <QueryClientProvider client={queryClient}>
109
- <HydrationBoundary state={queryState}>
110
- <Suspense fallback={null}>
111
- <OlovaRouter
112
- routes={routes}
113
- layouts={wrappedLayouts}
114
- notFoundPages={notFoundPages}
115
- />
116
- </Suspense>
117
- </HydrationBoundary>
118
- </QueryClientProvider>
119
- </StrictMode>
120
- );
121
-
122
- return { html, queryState };
123
- }
124
-
125
- // =============================================================================
126
- // WORKER EXPORT
127
- // =============================================================================
128
-
129
- export interface WorkerEnv {
130
- ASSETS: { fetch: (request: Request | URL) => Promise<Response> };
131
- }
132
-
133
- export default {
134
- async fetch(request: Request, env: WorkerEnv): Promise<Response> {
135
- const url = new URL(request.url);
136
- const pathname = url.pathname;
137
-
138
- // 1. Non-HTML requests → serve static assets directly
139
- if (!request.headers.get('accept')?.includes('text/html')) {
140
- return env.ASSETS.fetch(request);
141
- }
142
-
143
- // 2. Check if route has loader (needs SSR)
144
- const match = matchRoute(pathname);
145
- const needsSSR = match?.route?.loader;
146
-
147
- // 3. No SSR needed → serve pre-built static HTML
148
- if (!needsSSR) {
149
- const staticPath = pathname === '/' ? '/index.html' : `${pathname}/index.html`;
150
- try {
151
- const staticResponse = await env.ASSETS.fetch(new URL(staticPath, url));
152
- if (staticResponse.ok) {
153
- return new Response(staticResponse.body, {
154
- status: 200,
155
- headers: { 'Content-Type': 'text/html; charset=utf-8' }
156
- });
157
- }
158
- } catch {}
159
-
160
- // Fallback to 404.html
161
- try {
162
- const notFoundResponse = await env.ASSETS.fetch(new URL('/404.html', url));
163
- return new Response(notFoundResponse.body, {
164
- status: 404,
165
- headers: { 'Content-Type': 'text/html; charset=utf-8' }
166
- });
167
- } catch {}
168
-
169
- return new Response('Not Found', { status: 404 });
170
- }
171
-
172
- // 4. Route has loader → run SSR
173
- let loaderData: Record<string, unknown> | undefined;
174
-
175
- try {
176
- const result = await match.route.loader({
177
- request,
178
- params: match.params
179
- });
180
-
181
- if (!(result instanceof Response)) {
182
- loaderData = result;
183
- }
184
- } catch (error) {
185
- console.error('[Olova] Loader error:', error);
186
- }
187
-
188
- // 5. Render with loader data
189
- const { html: appHtml, queryState } = await render(pathname, loaderData);
190
-
191
- // Build full HTML document
192
- const html = `<!DOCTYPE html>${appHtml}<script>window.__QUERY_STATE__=${JSON.stringify(queryState)}</script><script type="module" src="/assets/_olova/main.js"></script>`;
193
-
194
- return new Response(html, {
195
- headers: { 'Content-Type': 'text/html; charset=utf-8' }
196
- });
197
- }
198
- };
199
-
200
- // Export for SSG builds
201
- export { render, matchRoute };