@archbase/ssr 3.0.0

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 ADDED
@@ -0,0 +1,411 @@
1
+ # @archbase/ssr
2
+
3
+ Server-Side Rendering utilities for Archbase React components with TanStack Start support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @archbase/ssr
9
+ # or
10
+ yarn add @archbase/ssr
11
+ # or
12
+ pnpm add @archbase/ssr
13
+ ```
14
+
15
+ ## Peer Dependencies
16
+
17
+ ```bash
18
+ npm install react react-dom @mantine/core @mantine/hooks @tanstack/react-query @tanstack/start
19
+ ```
20
+
21
+ ## Features
22
+
23
+ ### 🚀 **TanStack Start Integration**
24
+ - Full SSR support for TanStack Start applications
25
+ - Seamless server/client data synchronization
26
+ - Optimized hydration strategies
27
+
28
+ ### 📊 **DataSource SSR Support**
29
+ - SSR-compatible DataSource implementations
30
+ - State serialization/deserialization
31
+ - Automatic hydration from server data
32
+
33
+ ### 🎯 **SSR-Safe Components**
34
+ - Hydration-safe providers and hooks
35
+ - Client-only and server-only components
36
+ - Media query hooks that work with SSR
37
+
38
+ ### âš¡ **Performance Optimized**
39
+ - Minimal payload serialization
40
+ - Streaming-compatible
41
+ - Lazy hydration support
42
+
43
+ ## Basic Setup
44
+
45
+ ### 1. Wrap your app with ArchbaseSSRProvider
46
+
47
+ ```typescript
48
+ // app.tsx (TanStack Start)
49
+ import { ArchbaseSSRProvider, ArchbaseTanStackProvider } from '@archbase/ssr';
50
+ import { QueryClient } from '@tanstack/react-query';
51
+
52
+ function App() {
53
+ const queryClient = new QueryClient();
54
+
55
+ return (
56
+ <ArchbaseSSRProvider>
57
+ <ArchbaseTanStackProvider queryClient={queryClient}>
58
+ <YourAppContent />
59
+ </ArchbaseTanStackProvider>
60
+ </ArchbaseSSRProvider>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### 2. Use SSR-compatible DataSources
66
+
67
+ ```typescript
68
+ import { useArchbaseSSRDataSource } from '@archbase/ssr';
69
+
70
+ function UsersList() {
71
+ const {
72
+ dataSource,
73
+ isLoading,
74
+ error,
75
+ isHydrated
76
+ } = useArchbaseSSRDataSource('users', {
77
+ initialRecords: [], // Server-side initial data
78
+ autoHydrate: true,
79
+ fallbackRecords: []
80
+ });
81
+
82
+ if (!isHydrated) {
83
+ return <div>Loading...</div>; // SSR placeholder
84
+ }
85
+
86
+ return (
87
+ <div>
88
+ {dataSource.getRecords().map(user => (
89
+ <div key={user.id}>{user.name}</div>
90
+ ))}
91
+ </div>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ## Advanced Usage
97
+
98
+ ### Server-Side Data Fetching with TanStack Start
99
+
100
+ ```typescript
101
+ // routes/users.tsx
102
+ import { createFileRoute } from '@tanstack/react-router';
103
+ import { useArchbaseQuery, prepareServerQueries } from '@archbase/ssr';
104
+
105
+ export const Route = createFileRoute('/users')({
106
+ component: UsersPage,
107
+ loader: async ({ context }) => {
108
+ // Prepare queries for SSR
109
+ const queryClient = context.queryClient;
110
+
111
+ await prepareServerQueries(queryClient, [
112
+ {
113
+ key: ['users'],
114
+ fetchFn: () => fetch('/api/users').then(res => res.json())
115
+ }
116
+ ]);
117
+
118
+ return {
119
+ dehydratedState: queryClient.getQueryData(['users'])
120
+ };
121
+ }
122
+ });
123
+
124
+ function UsersPage() {
125
+ const { dataSource } = useArchbaseSSRDataSource('users');
126
+
127
+ const { data, isLoading } = useArchbaseQuery(
128
+ ['users'],
129
+ () => fetch('/api/users').then(res => res.json()),
130
+ {
131
+ dataSource,
132
+ syncWithDataSource: true,
133
+ ssr: true
134
+ }
135
+ );
136
+
137
+ return (
138
+ <div>
139
+ <h1>Users</h1>
140
+ {/* Your user list component */}
141
+ </div>
142
+ );
143
+ }
144
+ ```
145
+
146
+ ### SSR-Safe Utilities
147
+
148
+ ```typescript
149
+ import {
150
+ canUseDOM,
151
+ safeLocalStorage,
152
+ withSSRFallback,
153
+ ClientOnly,
154
+ ServerOnly
155
+ } from '@archbase/ssr';
156
+
157
+ function MyComponent() {
158
+ // Safe localStorage access
159
+ const savedData = safeLocalStorage.getItem('userData');
160
+
161
+ // Safe operation with fallback
162
+ const windowWidth = withSSRFallback(
163
+ () => window.innerWidth,
164
+ 1024 // fallback for SSR
165
+ );
166
+
167
+ return (
168
+ <div>
169
+ <ServerOnly>
170
+ <div>This only renders on server</div>
171
+ </ServerOnly>
172
+
173
+ <ClientOnly fallback={<div>Loading...</div>}>
174
+ <div>This only renders on client after hydration</div>
175
+ </ClientOnly>
176
+ </div>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ### Hydration-Safe State Management
182
+
183
+ ```typescript
184
+ import { useHydrationSafeState, useSSRSafeMediaQuery } from '@archbase/ssr';
185
+
186
+ function ResponsiveComponent() {
187
+ // Media query that works with SSR
188
+ const isMobile = useSSRSafeMediaQuery('(max-width: 768px)', false);
189
+
190
+ // State that's safe during hydration
191
+ const theme = useHydrationSafeState(
192
+ 'light', // SSR value
193
+ () => localStorage.getItem('theme') || 'light' // Client value
194
+ );
195
+
196
+ return (
197
+ <div className={`theme-${theme} ${isMobile ? 'mobile' : 'desktop'}`}>
198
+ Content
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ ### Advanced DataSource Serialization
205
+
206
+ ```typescript
207
+ import { ArchbaseSSRDataSource } from '@archbase/ssr';
208
+
209
+ // Create SSR-compatible DataSource
210
+ const userDataSource = new ArchbaseSSRDataSource('users');
211
+
212
+ // On server - serialize state
213
+ const serializedState = userDataSource.serializeState();
214
+
215
+ // Send to client and deserialize
216
+ userDataSource.deserializeState(serializedState);
217
+
218
+ // Or use minimal serialization for performance
219
+ const minimalState = userDataSource.serializeMinimalState();
220
+
221
+ // Create from server data
222
+ const hydratedDataSource = ArchbaseSSRDataSource.fromServerData(
223
+ 'users',
224
+ serverData,
225
+ { /* options */ }
226
+ );
227
+ ```
228
+
229
+ ### Multiple DataSources Management
230
+
231
+ ```typescript
232
+ import { useArchbaseSSRDataSources } from '@archbase/ssr';
233
+
234
+ function Dashboard() {
235
+ const {
236
+ sources,
237
+ getDataSource,
238
+ serializeAll,
239
+ isAnyLoading,
240
+ errors
241
+ } = useArchbaseSSRDataSources([
242
+ { name: 'users', options: { initialRecords: [] } },
243
+ { name: 'products', options: { initialRecords: [] } },
244
+ { name: 'orders', options: { initialRecords: [] } }
245
+ ]);
246
+
247
+ const userDataSource = getDataSource('users');
248
+ const productDataSource = getDataSource('products');
249
+
250
+ if (isAnyLoading) {
251
+ return <div>Loading dashboard...</div>;
252
+ }
253
+
254
+ return (
255
+ <div>
256
+ <UsersList dataSource={userDataSource?.dataSource} />
257
+ <ProductsList dataSource={productDataSource?.dataSource} />
258
+ </div>
259
+ );
260
+ }
261
+ ```
262
+
263
+ ## TanStack Start Route Example
264
+
265
+ Complete example of a TanStack Start route with Archbase SSR:
266
+
267
+ ```typescript
268
+ // routes/products/$productId.tsx
269
+ import { createFileRoute } from '@tanstack/react-router';
270
+ import {
271
+ ArchbaseSSRProvider,
272
+ useArchbaseSSRDataSource,
273
+ prepareServerQueries
274
+ } from '@archbase/ssr';
275
+
276
+ export const Route = createFileRoute('/products/$productId')({
277
+ component: ProductPage,
278
+ loader: async ({ params, context }) => {
279
+ const { productId } = params;
280
+ const queryClient = context.queryClient;
281
+
282
+ // Prefetch product data on server
283
+ await prepareServerQueries(queryClient, [
284
+ {
285
+ key: ['product', productId],
286
+ fetchFn: () => fetchProduct(productId)
287
+ }
288
+ ]);
289
+
290
+ const productData = queryClient.getQueryData(['product', productId]);
291
+
292
+ return {
293
+ productData,
294
+ dehydratedState: queryClient.getQueryData(['product', productId])
295
+ };
296
+ }
297
+ });
298
+
299
+ function ProductPage() {
300
+ const { productId } = Route.useParams();
301
+ const { productData } = Route.useLoaderData();
302
+
303
+ const { dataSource, isHydrated } = useArchbaseSSRDataSource('product', {
304
+ initialRecords: productData ? [productData] : [],
305
+ autoHydrate: true
306
+ });
307
+
308
+ if (!isHydrated) {
309
+ // SSR/hydration loading state
310
+ return <ProductSkeleton />;
311
+ }
312
+
313
+ const product = dataSource.getCurrentRecord();
314
+
315
+ return (
316
+ <div>
317
+ <h1>{product?.name}</h1>
318
+ <p>{product?.description}</p>
319
+ <ProductForm dataSource={dataSource} />
320
+ </div>
321
+ );
322
+ }
323
+
324
+ async function fetchProduct(id: string) {
325
+ const response = await fetch(`/api/products/${id}`);
326
+ return response.json();
327
+ }
328
+ ```
329
+
330
+ ## Best Practices
331
+
332
+ ### 1. **Minimize Serialized Data**
333
+ ```typescript
334
+ // Good: Only serialize essential data
335
+ const minimalState = dataSource.serializeMinimalState();
336
+
337
+ // Avoid: Serializing large datasets
338
+ const fullState = dataSource.serializeState(); // Use sparingly
339
+ ```
340
+
341
+ ### 2. **Graceful Fallbacks**
342
+ ```typescript
343
+ const { dataSource, error } = useArchbaseSSRDataSource('users', {
344
+ fallbackRecords: [], // Always provide fallbacks
345
+ autoHydrate: true
346
+ });
347
+
348
+ if (error) {
349
+ return <ErrorBoundary error={error} />;
350
+ }
351
+ ```
352
+
353
+ ### 3. **Conditional Client Features**
354
+ ```typescript
355
+ import { ClientOnly, canUseDOM } from '@archbase/ssr';
356
+
357
+ function AdvancedFeatures() {
358
+ return (
359
+ <ClientOnly fallback={<BasicView />}>
360
+ {canUseDOM() && <InteractiveChart />}
361
+ </ClientOnly>
362
+ );
363
+ }
364
+ ```
365
+
366
+ ### 4. **Performance Optimization**
367
+ ```typescript
368
+ // Lazy load heavy components
369
+ const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
370
+
371
+ function App() {
372
+ return (
373
+ <ClientOnly>
374
+ <React.Suspense fallback={<Loading />}>
375
+ <HeavyComponent />
376
+ </React.Suspense>
377
+ </ClientOnly>
378
+ );
379
+ }
380
+ ```
381
+
382
+ ## Migration from Client-Only Archbase
383
+
384
+ 1. **Wrap your app with SSR providers**
385
+ 2. **Replace DataSource with useArchbaseSSRDataSource**
386
+ 3. **Add SSR-safe utilities where needed**
387
+ 4. **Update server routes for data prefetching**
388
+
389
+ ## TypeScript Support
390
+
391
+ All components and hooks are fully typed with TypeScript. The package includes complete type definitions for optimal development experience.
392
+
393
+ ## Browser Compatibility
394
+
395
+ - **Server**: Node.js 18+
396
+ - **Client**: Modern browsers with ES2020 support
397
+ - **SSR**: Full support for all server-side environments
398
+
399
+ ## Performance
400
+
401
+ - **Bundle Size**: ~15KB gzipped
402
+ - **Runtime Overhead**: Minimal (hydration detection only)
403
+ - **Memory Usage**: Optimized serialization reduces memory footprint
404
+
405
+ ## Contributing
406
+
407
+ This package is part of the Archbase React ecosystem. See the main repository for contribution guidelines.
408
+
409
+ ## License
410
+
411
+ MIT License
Binary file
@@ -0,0 +1,122 @@
1
+ import { ArchbaseDataSource } from '@archbase/data';
2
+ /**
3
+ * Interface for serializable DataSource state
4
+ */
5
+ interface SerializableDataSourceState<T, ID> {
6
+ records: T[];
7
+ currentRecord: T | undefined;
8
+ recordIndex: number;
9
+ state: string;
10
+ hasChanges: boolean;
11
+ filter?: any;
12
+ sort?: any;
13
+ metadata?: Record<string, any>;
14
+ }
15
+ /**
16
+ * Extended DataSource with SSR capabilities
17
+ */
18
+ export declare class ArchbaseSSRDataSource<T extends Record<string, any>, ID> extends ArchbaseDataSource<T, ID> {
19
+ private _ssrData?;
20
+ private _isHydrating;
21
+ constructor(name: string, options?: any);
22
+ /**
23
+ * Serialize DataSource state for SSR
24
+ */
25
+ serializeState(): string;
26
+ /**
27
+ * Restore DataSource state from serialized data
28
+ */
29
+ deserializeState(serializedState: string): void;
30
+ /**
31
+ * Check if DataSource is currently hydrating from SSR
32
+ */
33
+ isHydrating(): boolean;
34
+ /**
35
+ * Get SSR data if available
36
+ */
37
+ getSSRData(): SerializableDataSourceState<T, ID> | undefined;
38
+ /**
39
+ * Create a minimal state object for SSR
40
+ * Only includes essential data to reduce payload size
41
+ */
42
+ serializeMinimalState(): string;
43
+ /**
44
+ * Create a DataSource instance from server data
45
+ */
46
+ static fromServerData<T extends Record<string, any>, ID>(name: string, serverData: any, options?: any): ArchbaseSSRDataSource<T, ID>;
47
+ /**
48
+ * Enhanced method to handle SSR-safe operations
49
+ */
50
+ ssrSafeOperation<R>(operation: () => R, fallback: R): R;
51
+ /**
52
+ * Override to handle SSR-safe filtering
53
+ */
54
+ applyFilter(filter: any): void;
55
+ /**
56
+ * Override to handle SSR-safe sorting
57
+ */
58
+ applySort(sort: any): void;
59
+ /**
60
+ * Get records with SSR safety
61
+ */
62
+ getRecords(): T[];
63
+ /**
64
+ * Get current record with SSR safety
65
+ */
66
+ getCurrentRecord(): T | undefined;
67
+ /**
68
+ * Check if we have data available for rendering
69
+ */
70
+ hasData(): boolean;
71
+ /**
72
+ * Create a snapshot that can be safely transmitted between server/client
73
+ */
74
+ createSnapshot(): {
75
+ records: T[];
76
+ currentIndex: number;
77
+ metadata: Record<string, any>;
78
+ timestamp: number;
79
+ };
80
+ /**
81
+ * Restore from a snapshot
82
+ */
83
+ restoreFromSnapshot(snapshot: {
84
+ records: T[];
85
+ currentIndex: number;
86
+ metadata: Record<string, any>;
87
+ timestamp: number;
88
+ }): void;
89
+ /**
90
+ * Helper method to get all metadata
91
+ */
92
+ private getAllMetadata;
93
+ /**
94
+ * Get all records from the data source
95
+ */
96
+ getAllRecords(): T[];
97
+ /**
98
+ * Get current record index
99
+ */
100
+ getRecordIndex(): number;
101
+ /**
102
+ * Check if data source has changes
103
+ */
104
+ isChanged(): boolean;
105
+ /**
106
+ * Get current filter
107
+ */
108
+ getFilter(): any;
109
+ /**
110
+ * Get current sort
111
+ */
112
+ getSort(): any;
113
+ /**
114
+ * Get record count
115
+ */
116
+ getRecordCount(): number;
117
+ /**
118
+ * Set records with proper DataSource options
119
+ */
120
+ private setRecords;
121
+ }
122
+ export {};
@@ -0,0 +1,94 @@
1
+ import { ArchbaseSSRDataSource } from '../datasource/ArchbaseSSRDataSource';
2
+ interface UseArchbaseSSRDataSourceOptions<T, ID> {
3
+ /** Initial records for SSR */
4
+ initialRecords?: T[];
5
+ /** Server data key in SSR context */
6
+ serverDataKey?: string;
7
+ /** Auto-hydrate from server data */
8
+ autoHydrate?: boolean;
9
+ /** Fallback records if server data fails */
10
+ fallbackRecords?: T[];
11
+ /** Custom serialization/deserialization */
12
+ serializer?: {
13
+ serialize: (data: any) => string;
14
+ deserialize: (data: string) => any;
15
+ };
16
+ }
17
+ /**
18
+ * SSR-compatible hook for DataSource management
19
+ */
20
+ export declare function useArchbaseSSRDataSource<T extends Record<string, any>, ID>(name: string, options?: UseArchbaseSSRDataSourceOptions<T, ID>): {
21
+ dataSource: ArchbaseSSRDataSource<T, ID>;
22
+ isLoading: boolean;
23
+ error: Error | null;
24
+ isHydrated: boolean;
25
+ serializeState: () => string;
26
+ deserializeState: (serializedState: string) => void;
27
+ getMinimalState: () => {
28
+ recordCount: number;
29
+ currentIndex: number;
30
+ hasData: boolean;
31
+ isActive: boolean;
32
+ };
33
+ refresh: (newRecords?: T[]) => Promise<void>;
34
+ records: T[];
35
+ currentRecord: T | undefined;
36
+ recordCount: number;
37
+ hasData: boolean;
38
+ };
39
+ /**
40
+ * Hook for managing multiple DataSources with SSR
41
+ */
42
+ export declare function useArchbaseSSRDataSources<T extends Record<string, any>, ID>(dataSources: Array<{
43
+ name: string;
44
+ options?: UseArchbaseSSRDataSourceOptions<T, ID>;
45
+ }>): {
46
+ sources: {
47
+ dataSource: ArchbaseSSRDataSource<T, ID>;
48
+ isLoading: boolean;
49
+ error: Error | null;
50
+ isHydrated: boolean;
51
+ serializeState: () => string;
52
+ deserializeState: (serializedState: string) => void;
53
+ getMinimalState: () => {
54
+ recordCount: number;
55
+ currentIndex: number;
56
+ hasData: boolean;
57
+ isActive: boolean;
58
+ };
59
+ refresh: (newRecords?: T[] | undefined) => Promise<void>;
60
+ records: T[];
61
+ currentRecord: T | undefined;
62
+ recordCount: number;
63
+ hasData: boolean;
64
+ name: string;
65
+ }[];
66
+ getDataSource: (name: string) => {
67
+ dataSource: ArchbaseSSRDataSource<T, ID>;
68
+ isLoading: boolean;
69
+ error: Error | null;
70
+ isHydrated: boolean;
71
+ serializeState: () => string;
72
+ deserializeState: (serializedState: string) => void;
73
+ getMinimalState: () => {
74
+ recordCount: number;
75
+ currentIndex: number;
76
+ hasData: boolean;
77
+ isActive: boolean;
78
+ };
79
+ refresh: (newRecords?: T[] | undefined) => Promise<void>;
80
+ records: T[];
81
+ currentRecord: T | undefined;
82
+ recordCount: number;
83
+ hasData: boolean;
84
+ name: string;
85
+ } | undefined;
86
+ serializeAll: () => Record<string, string>;
87
+ isAnyLoading: boolean;
88
+ errors: {
89
+ name: string;
90
+ error: Error | null;
91
+ }[];
92
+ isHydrated: boolean;
93
+ };
94
+ export {};
@@ -0,0 +1,6 @@
1
+ export { isServer, isClient, canUseDOM, safeLocalStorage, safeSessionStorage, serializeForSSR, deserializeFromSSR, createSSRId, resetSSRIdCounter, getWindow, getDocument, hasFeature, withSSRFallback, clientOnly, serverOnly, createHydrationSafeValue, getMediaQuery, type MediaQueryResult } from './utils/ArchbaseSSRUtils';
2
+ export { ArchbaseSSRProvider, useArchbaseSSR, useHydrationSafeState, ClientOnly, ServerOnly, useClientEffect, useSSRSafeMediaQuery } from './providers/ArchbaseSSRProvider';
3
+ export { ArchbaseSSRDataSource } from './datasource/ArchbaseSSRDataSource';
4
+ export { useArchbaseSSRDataSource, useArchbaseSSRDataSources } from './hooks/useArchbaseSSRDataSource';
5
+ export { ArchbaseTanStackProvider, useArchbaseQuery, serializeQueryClientState, prepareServerQueries, withArchbaseTanStack } from './tanstack/ArchbaseTanStackIntegration';
6
+ export declare const ArchbaseSSRVersion = "3.0.0";