@servlyadmin/runtime-core 0.1.45 → 0.2.1

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 CHANGED
@@ -14,10 +14,88 @@ pnpm add @servlyadmin/runtime-core
14
14
 
15
15
  ## Quick Start
16
16
 
17
+ The simplest way to render a component:
18
+
19
+ ```typescript
20
+ import { mount } from '@servlyadmin/runtime-core';
21
+
22
+ // Mount a component - that's it!
23
+ const app = await mount({
24
+ componentId: 'my-component-id',
25
+ target: '#app',
26
+ props: { title: 'Hello World' }
27
+ });
28
+
29
+ // Update props
30
+ app.update({ props: { title: 'Updated!' } });
31
+
32
+ // Cleanup
33
+ app.destroy();
34
+ ```
35
+
36
+ ### With Callbacks
37
+
38
+ ```typescript
39
+ const app = await mount({
40
+ componentId: 'my-component-id',
41
+ target: '#app',
42
+ props: { title: 'Hello' },
43
+ onReady: (result) => console.log('Mounted!', result.version),
44
+ onError: (err) => console.error('Failed:', err)
45
+ });
46
+ ```
47
+
48
+ ### With Loading & Error States
49
+
50
+ ```typescript
51
+ const app = await mount({
52
+ componentId: 'my-component-id',
53
+ target: '#app',
54
+ props: { title: 'Hello' },
55
+
56
+ // Show loading indicator
57
+ loadingComponent: '<div class="skeleton animate-pulse h-32 bg-gray-200 rounded"></div>',
58
+
59
+ // Show error message on failure
60
+ errorComponent: (err) => `<div class="text-red-500">Failed to load: ${err.message}</div>`,
61
+
62
+ // Optional: delay before showing loading (avoids flash for fast loads)
63
+ loadingDelay: 200,
64
+
65
+ // Optional: minimum time to show loading (avoids flash)
66
+ minLoadingTime: 500,
67
+
68
+ // Lifecycle callbacks
69
+ onLoadStart: () => console.log('Loading...'),
70
+ onLoadEnd: () => console.log('Done!'),
71
+ onReady: (result) => console.log('Mounted!'),
72
+ onError: (err) => console.error('Failed:', err)
73
+ });
74
+ ```
75
+
76
+ ### With Version & Cache Control
77
+
78
+ ```typescript
79
+ const app = await mount({
80
+ componentId: 'my-component-id',
81
+ target: document.getElementById('app'),
82
+ props: { title: 'Hello' },
83
+ version: '^1.0.0',
84
+ fetchOptions: {
85
+ cacheStrategy: 'memory',
86
+ forceRefresh: true
87
+ }
88
+ });
89
+ ```
90
+
91
+ ## Low-Level API
92
+
93
+ For more control, use `fetchComponent` and `render` directly:
94
+
17
95
  ```typescript
18
96
  import { render, fetchComponent } from '@servlyadmin/runtime-core';
19
97
 
20
- // Fetch a component from the registry (uses Servly's default registry)
98
+ // Fetch a component from the registry
21
99
  const { data } = await fetchComponent('my-component-id');
22
100
 
23
101
  // Render to a container
@@ -83,6 +161,36 @@ const { data } = await fetchComponent('my-component', {
83
161
  });
84
162
  ```
85
163
 
164
+ ## Prefetching
165
+
166
+ Preload components for faster subsequent rendering:
167
+
168
+ ```typescript
169
+ import { prefetch, prefetchAll, prefetchOnHover, prefetchOnVisible, prefetchOnIdle } from '@servlyadmin/runtime-core';
170
+
171
+ // Prefetch a single component
172
+ await prefetch('pricing-card');
173
+
174
+ // Prefetch multiple components
175
+ await prefetchAll(['pricing-card', 'contact-form', 'navbar']);
176
+
177
+ // Prefetch with versions
178
+ await prefetchAll([
179
+ { id: 'pricing-card', version: '^1.0.0' },
180
+ { id: 'contact-form', version: 'latest' }
181
+ ]);
182
+
183
+ // Prefetch on hover (great for modals/dialogs)
184
+ const cleanup = prefetchOnHover('#open-modal-btn', 'modal-component');
185
+ // Later: cleanup() to remove listener
186
+
187
+ // Prefetch when element becomes visible (Intersection Observer)
188
+ const cleanup = prefetchOnVisible('#pricing-section', ['pricing-card', 'feature-list']);
189
+
190
+ // Prefetch when browser is idle (non-critical components)
191
+ prefetchOnIdle(['footer', 'sidebar', 'help-modal']);
192
+ ```
193
+
86
194
  ## Core Concepts
87
195
 
88
196
  ### Layout Elements
@@ -135,6 +243,65 @@ const elements = [
135
243
 
136
244
  ## API Reference
137
245
 
246
+ ### mount(options) - Recommended
247
+
248
+ The simplest way to render a component. Handles fetching, container resolution, and rendering.
249
+
250
+ ```typescript
251
+ const app = await mount({
252
+ componentId: string, // Required: Component ID to fetch
253
+ target: string | HTMLElement, // Required: CSS selector or element
254
+ props?: Record<string, any>, // Props to pass (default: {})
255
+ state?: Record<string, any>, // Initial state (default: {})
256
+ context?: Record<string, any>, // Additional context (default: {})
257
+ version?: string, // Version specifier (default: 'latest')
258
+ eventHandlers?: Record<string, Record<string, (e: Event) => void>>,
259
+ fetchOptions?: {
260
+ cacheStrategy?: 'localStorage' | 'memory' | 'none',
261
+ forceRefresh?: boolean,
262
+ apiKey?: string,
263
+ retryConfig?: RetryConfig,
264
+ },
265
+
266
+ // Loading & Error States
267
+ loadingComponent?: string | HTMLElement, // HTML to show while loading
268
+ errorComponent?: (err: Error) => string | HTMLElement, // Error display
269
+ loadingDelay?: number, // Delay before showing loading (ms)
270
+ minLoadingTime?: number, // Minimum loading display time (ms)
271
+
272
+ // Lifecycle Callbacks
273
+ onLoadStart?: () => void, // Called when loading starts
274
+ onLoadEnd?: () => void, // Called when loading ends
275
+ onReady?: (result: MountResult) => void,
276
+ onError?: (error: Error) => void,
277
+ });
278
+
279
+ // Returns MountResult
280
+ interface MountResult {
281
+ update(context: Partial<BindingContext>): void;
282
+ destroy(): void;
283
+ rootElement: HTMLElement | null;
284
+ container: HTMLElement;
285
+ data: ComponentData;
286
+ fromCache: boolean;
287
+ version: string;
288
+ }
289
+ ```
290
+
291
+ ### mountData(options)
292
+
293
+ Mount with pre-fetched data (useful for SSR or custom data sources):
294
+
295
+ ```typescript
296
+ import { mountData } from '@servlyadmin/runtime-core';
297
+
298
+ const app = mountData({
299
+ data: myComponentData, // Pre-fetched ComponentData
300
+ target: '#app',
301
+ props: { title: 'Hello' }
302
+ });
303
+ ```
304
+
138
305
  ### render(options)
139
306
 
140
307
  Renders elements to a container.
@@ -284,6 +451,53 @@ hasTemplateSyntax('{{props.name}}'); // true
284
451
  hasTemplateSyntax('static text'); // false
285
452
  ```
286
453
 
454
+ ### Prefetch API
455
+
456
+ Preload components for faster subsequent rendering.
457
+
458
+ ```typescript
459
+ import {
460
+ prefetch,
461
+ prefetchAll,
462
+ prefetchOnHover,
463
+ prefetchOnVisible,
464
+ prefetchOnIdle
465
+ } from '@servlyadmin/runtime-core';
466
+
467
+ // Prefetch single component
468
+ await prefetch('pricing-card');
469
+ await prefetch('pricing-card', '^1.0.0'); // With version
470
+
471
+ // Prefetch multiple components
472
+ const { success, failed } = await prefetchAll([
473
+ 'pricing-card',
474
+ 'contact-form',
475
+ { id: 'navbar', version: '^2.0.0' }
476
+ ]);
477
+
478
+ // Prefetch on hover (returns cleanup function)
479
+ const cleanup = prefetchOnHover(
480
+ '#open-modal-btn', // Target element
481
+ 'modal-component', // Component ID
482
+ 'latest', // Version
483
+ { delay: 100 } // Options: delay before prefetch
484
+ );
485
+ // Later: cleanup()
486
+
487
+ // Prefetch when element becomes visible
488
+ const cleanup = prefetchOnVisible(
489
+ '#pricing-section', // Target element
490
+ ['pricing-card', 'feature-list'], // Components to prefetch
491
+ { rootMargin: '100px', threshold: 0 } // IntersectionObserver options
492
+ );
493
+
494
+ // Prefetch during browser idle time
495
+ prefetchOnIdle(
496
+ ['footer', 'sidebar', 'help-modal'],
497
+ { timeout: 5000 } // Max wait time before forcing prefetch
498
+ );
499
+ ```
500
+
287
501
  ## Slots
288
502
 
289
503
  Components can define slots for content injection: