@servlyadmin/runtime-core 0.1.46 → 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 +115 -0
- package/dist/index.cjs +520 -5
- package/dist/index.js +514 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -45,6 +45,34 @@ const app = await mount({
|
|
|
45
45
|
});
|
|
46
46
|
```
|
|
47
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
|
+
|
|
48
76
|
### With Version & Cache Control
|
|
49
77
|
|
|
50
78
|
```typescript
|
|
@@ -133,6 +161,36 @@ const { data } = await fetchComponent('my-component', {
|
|
|
133
161
|
});
|
|
134
162
|
```
|
|
135
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
|
+
|
|
136
194
|
## Core Concepts
|
|
137
195
|
|
|
138
196
|
### Layout Elements
|
|
@@ -204,6 +262,16 @@ const app = await mount({
|
|
|
204
262
|
apiKey?: string,
|
|
205
263
|
retryConfig?: RetryConfig,
|
|
206
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
|
|
207
275
|
onReady?: (result: MountResult) => void,
|
|
208
276
|
onError?: (error: Error) => void,
|
|
209
277
|
});
|
|
@@ -383,6 +451,53 @@ hasTemplateSyntax('{{props.name}}'); // true
|
|
|
383
451
|
hasTemplateSyntax('static text'); // false
|
|
384
452
|
```
|
|
385
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
|
+
|
|
386
501
|
## Slots
|
|
387
502
|
|
|
388
503
|
Components can define slots for content injection:
|
package/dist/index.cjs
CHANGED
|
@@ -720,6 +720,7 @@ __export(index_exports, {
|
|
|
720
720
|
extractDependenciesFromCode: () => extractDependenciesFromCode,
|
|
721
721
|
extractOverrideDependencies: () => extractOverrideDependencies,
|
|
722
722
|
extractReferencedViewIds: () => extractReferencedViewIds,
|
|
723
|
+
extractSlotBindings: () => extractSlotBindings,
|
|
723
724
|
fetchComponent: () => fetchComponent,
|
|
724
725
|
fetchComponentWithDependencies: () => fetchComponentWithDependencies,
|
|
725
726
|
formatStyleValue: () => formatStyleValue,
|
|
@@ -777,7 +778,12 @@ __export(index_exports, {
|
|
|
777
778
|
mountData: () => mountData,
|
|
778
779
|
navigateTo: () => navigateTo,
|
|
779
780
|
parseVersion: () => parseVersion,
|
|
781
|
+
prefetch: () => prefetch,
|
|
782
|
+
prefetchAll: () => prefetchAll,
|
|
780
783
|
prefetchComponents: () => prefetchComponents,
|
|
784
|
+
prefetchOnHover: () => prefetchOnHover,
|
|
785
|
+
prefetchOnIdle: () => prefetchOnIdle,
|
|
786
|
+
prefetchOnVisible: () => prefetchOnVisible,
|
|
781
787
|
preloadIcons: () => preloadIcons,
|
|
782
788
|
preloadTailwind: () => preloadTailwind,
|
|
783
789
|
preventFOUC: () => preventFOUC,
|
|
@@ -1519,8 +1525,96 @@ function resolveBindingPath(path, context) {
|
|
|
1519
1525
|
}
|
|
1520
1526
|
return navigatePath(source, parts.slice(startIndex));
|
|
1521
1527
|
}
|
|
1528
|
+
var FUNCTION_CALL_REGEX = /^(\w+)\s*\((.*)\)$/s;
|
|
1529
|
+
function executeGlobalFunction(funcName, argsStr, context) {
|
|
1530
|
+
if (context.functionMap?.[funcName]) {
|
|
1531
|
+
try {
|
|
1532
|
+
const args = parseFunctionArguments(argsStr, context);
|
|
1533
|
+
return context.functionMap[funcName](...args);
|
|
1534
|
+
} catch (error) {
|
|
1535
|
+
console.error(`[GlobalFunction] Error executing ${funcName}:`, error);
|
|
1536
|
+
return void 0;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
const func = context.globalFunctions?.find((f) => f.name === funcName);
|
|
1540
|
+
if (!func) {
|
|
1541
|
+
console.warn(`[GlobalFunction] Function not found: ${funcName}`);
|
|
1542
|
+
return void 0;
|
|
1543
|
+
}
|
|
1544
|
+
try {
|
|
1545
|
+
const args = parseFunctionArguments(argsStr, context);
|
|
1546
|
+
const paramNames = func.parameters.map((p) => p.name);
|
|
1547
|
+
const fnBody = `
|
|
1548
|
+
const state = __ctx__.state || {};
|
|
1549
|
+
const props = __ctx__.props || {};
|
|
1550
|
+
const config = __ctx__.configs || __ctx__.context || {};
|
|
1551
|
+
${func.code}
|
|
1552
|
+
`;
|
|
1553
|
+
const fn = new Function("__ctx__", ...paramNames, fnBody);
|
|
1554
|
+
return fn(context, ...args);
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
console.error(`[GlobalFunction] Error executing ${funcName}:`, error);
|
|
1557
|
+
return void 0;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
function parseFunctionArguments(argsStr, context) {
|
|
1561
|
+
if (!argsStr.trim()) return [];
|
|
1562
|
+
const args = [];
|
|
1563
|
+
let current = "";
|
|
1564
|
+
let depth = 0;
|
|
1565
|
+
let inString = false;
|
|
1566
|
+
let stringChar = "";
|
|
1567
|
+
for (let i = 0; i < argsStr.length; i++) {
|
|
1568
|
+
const char = argsStr[i];
|
|
1569
|
+
const prevChar = argsStr[i - 1];
|
|
1570
|
+
if ((char === '"' || char === "'") && prevChar !== "\\") {
|
|
1571
|
+
if (!inString) {
|
|
1572
|
+
inString = true;
|
|
1573
|
+
stringChar = char;
|
|
1574
|
+
} else if (char === stringChar) {
|
|
1575
|
+
inString = false;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
if (!inString) {
|
|
1579
|
+
if (char === "(" || char === "[" || char === "{") depth++;
|
|
1580
|
+
if (char === ")" || char === "]" || char === "}") depth--;
|
|
1581
|
+
}
|
|
1582
|
+
if (char === "," && depth === 0 && !inString) {
|
|
1583
|
+
args.push(parseArgumentValue(current.trim(), context));
|
|
1584
|
+
current = "";
|
|
1585
|
+
} else {
|
|
1586
|
+
current += char;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
if (current.trim()) {
|
|
1590
|
+
args.push(parseArgumentValue(current.trim(), context));
|
|
1591
|
+
}
|
|
1592
|
+
return args;
|
|
1593
|
+
}
|
|
1594
|
+
function parseArgumentValue(value, context) {
|
|
1595
|
+
const trimmed = value.trim();
|
|
1596
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
1597
|
+
return trimmed.slice(1, -1);
|
|
1598
|
+
}
|
|
1599
|
+
if (!isNaN(Number(trimmed)) && trimmed !== "") {
|
|
1600
|
+
return Number(trimmed);
|
|
1601
|
+
}
|
|
1602
|
+
if (trimmed === "true") return true;
|
|
1603
|
+
if (trimmed === "false") return false;
|
|
1604
|
+
if (trimmed === "null") return null;
|
|
1605
|
+
if (trimmed === "undefined") return void 0;
|
|
1606
|
+
if (trimmed.startsWith("{{") && trimmed.endsWith("}}")) {
|
|
1607
|
+
return resolveTemplateValue(trimmed, context);
|
|
1608
|
+
}
|
|
1609
|
+
return resolveBindingPath(trimmed, context);
|
|
1610
|
+
}
|
|
1522
1611
|
function resolveExpression(expression, context) {
|
|
1523
1612
|
const trimmed = expression.trim();
|
|
1613
|
+
const funcMatch = trimmed.match(FUNCTION_CALL_REGEX);
|
|
1614
|
+
if (funcMatch) {
|
|
1615
|
+
const [, funcName, argsStr] = funcMatch;
|
|
1616
|
+
return executeGlobalFunction(funcName, argsStr, context);
|
|
1617
|
+
}
|
|
1524
1618
|
const comparisonOperators = ["===", "!==", "==", "!=", ">=", "<=", ">", "<"];
|
|
1525
1619
|
for (const op of comparisonOperators) {
|
|
1526
1620
|
if (trimmed.includes(op)) {
|
|
@@ -2437,6 +2531,203 @@ function getUrlInfo() {
|
|
|
2437
2531
|
}
|
|
2438
2532
|
|
|
2439
2533
|
// src/eventSystem.ts
|
|
2534
|
+
function createShortcuts(ctx) {
|
|
2535
|
+
return {
|
|
2536
|
+
/**
|
|
2537
|
+
* Render a dynamic list of items using a blueprint view
|
|
2538
|
+
* This is a simplified version for runtime - full implementation requires DOM access
|
|
2539
|
+
*/
|
|
2540
|
+
renderDynamicList: (options) => {
|
|
2541
|
+
const { blueprint, targetContainer, data = [], clearExisting = true, itemProps = {} } = options;
|
|
2542
|
+
const container = typeof document !== "undefined" ? document.querySelector(targetContainer) || document.querySelector(`[data-servly-id="${targetContainer.replace("#", "")}"]`) : null;
|
|
2543
|
+
if (!container) {
|
|
2544
|
+
console.warn(`[shortcuts.renderDynamicList] Container not found: ${targetContainer}`);
|
|
2545
|
+
return { success: false, error: "Container not found" };
|
|
2546
|
+
}
|
|
2547
|
+
if (clearExisting) {
|
|
2548
|
+
container.innerHTML = "";
|
|
2549
|
+
}
|
|
2550
|
+
const event = new CustomEvent("servly:renderDynamicList", {
|
|
2551
|
+
bubbles: true,
|
|
2552
|
+
detail: {
|
|
2553
|
+
blueprint,
|
|
2554
|
+
targetContainer,
|
|
2555
|
+
data,
|
|
2556
|
+
itemProps,
|
|
2557
|
+
container
|
|
2558
|
+
}
|
|
2559
|
+
});
|
|
2560
|
+
container.dispatchEvent(event);
|
|
2561
|
+
return { success: true, itemCount: data.length };
|
|
2562
|
+
},
|
|
2563
|
+
/**
|
|
2564
|
+
* Quick list rendering helper
|
|
2565
|
+
*/
|
|
2566
|
+
renderList: (blueprint, targetContainer, data, options = {}) => {
|
|
2567
|
+
return createShortcuts(ctx).renderDynamicList({
|
|
2568
|
+
blueprint,
|
|
2569
|
+
targetContainer,
|
|
2570
|
+
data,
|
|
2571
|
+
...options
|
|
2572
|
+
});
|
|
2573
|
+
},
|
|
2574
|
+
/**
|
|
2575
|
+
* Set state value
|
|
2576
|
+
*/
|
|
2577
|
+
setState: (key, value) => {
|
|
2578
|
+
if (ctx.stateManager) {
|
|
2579
|
+
ctx.stateManager.set(key, value, ctx.elementId);
|
|
2580
|
+
return { success: true };
|
|
2581
|
+
}
|
|
2582
|
+
return { success: false, error: "No state manager" };
|
|
2583
|
+
},
|
|
2584
|
+
/**
|
|
2585
|
+
* Get state value
|
|
2586
|
+
*/
|
|
2587
|
+
getState: (key) => {
|
|
2588
|
+
if (ctx.stateManager) {
|
|
2589
|
+
return ctx.stateManager.get(key);
|
|
2590
|
+
}
|
|
2591
|
+
return void 0;
|
|
2592
|
+
},
|
|
2593
|
+
/**
|
|
2594
|
+
* Toggle state value
|
|
2595
|
+
*/
|
|
2596
|
+
toggleState: (key) => {
|
|
2597
|
+
if (ctx.stateManager) {
|
|
2598
|
+
ctx.stateManager.toggle(key, ctx.elementId);
|
|
2599
|
+
return { success: true };
|
|
2600
|
+
}
|
|
2601
|
+
return { success: false, error: "No state manager" };
|
|
2602
|
+
},
|
|
2603
|
+
/**
|
|
2604
|
+
* Navigate to URL
|
|
2605
|
+
*/
|
|
2606
|
+
navigateTo: (url, options) => {
|
|
2607
|
+
navigateTo(url, options);
|
|
2608
|
+
return { success: true };
|
|
2609
|
+
},
|
|
2610
|
+
/**
|
|
2611
|
+
* Log to console (for debugging)
|
|
2612
|
+
*/
|
|
2613
|
+
log: (...args) => {
|
|
2614
|
+
console.log("[Servly]", ...args);
|
|
2615
|
+
},
|
|
2616
|
+
/**
|
|
2617
|
+
* Show alert
|
|
2618
|
+
*/
|
|
2619
|
+
alert: (message) => {
|
|
2620
|
+
if (typeof alert !== "undefined") {
|
|
2621
|
+
alert(message);
|
|
2622
|
+
}
|
|
2623
|
+
},
|
|
2624
|
+
/**
|
|
2625
|
+
* Get element by ID
|
|
2626
|
+
*/
|
|
2627
|
+
getElement: (id) => {
|
|
2628
|
+
if (typeof document === "undefined") return null;
|
|
2629
|
+
return document.querySelector(`[data-servly-id="${id}"]`) || document.getElementById(id);
|
|
2630
|
+
},
|
|
2631
|
+
/**
|
|
2632
|
+
* Set element text content
|
|
2633
|
+
*/
|
|
2634
|
+
setText: (selector, text) => {
|
|
2635
|
+
if (typeof document === "undefined") return { success: false };
|
|
2636
|
+
const el = document.querySelector(selector) || document.querySelector(`[data-servly-id="${selector.replace("#", "")}"]`);
|
|
2637
|
+
if (el) {
|
|
2638
|
+
el.textContent = text;
|
|
2639
|
+
return { success: true };
|
|
2640
|
+
}
|
|
2641
|
+
return { success: false, error: "Element not found" };
|
|
2642
|
+
},
|
|
2643
|
+
/**
|
|
2644
|
+
* Add class to element
|
|
2645
|
+
*/
|
|
2646
|
+
addClass: (selector, className) => {
|
|
2647
|
+
if (typeof document === "undefined") return { success: false };
|
|
2648
|
+
const el = document.querySelector(selector);
|
|
2649
|
+
if (el) {
|
|
2650
|
+
el.classList.add(className);
|
|
2651
|
+
return { success: true };
|
|
2652
|
+
}
|
|
2653
|
+
return { success: false, error: "Element not found" };
|
|
2654
|
+
},
|
|
2655
|
+
/**
|
|
2656
|
+
* Remove class from element
|
|
2657
|
+
*/
|
|
2658
|
+
removeClass: (selector, className) => {
|
|
2659
|
+
if (typeof document === "undefined") return { success: false };
|
|
2660
|
+
const el = document.querySelector(selector);
|
|
2661
|
+
if (el) {
|
|
2662
|
+
el.classList.remove(className);
|
|
2663
|
+
return { success: true };
|
|
2664
|
+
}
|
|
2665
|
+
return { success: false, error: "Element not found" };
|
|
2666
|
+
},
|
|
2667
|
+
/**
|
|
2668
|
+
* Toggle class on element
|
|
2669
|
+
*/
|
|
2670
|
+
toggleClass: (selector, className) => {
|
|
2671
|
+
if (typeof document === "undefined") return { success: false };
|
|
2672
|
+
const el = document.querySelector(selector);
|
|
2673
|
+
if (el) {
|
|
2674
|
+
el.classList.toggle(className);
|
|
2675
|
+
return { success: true };
|
|
2676
|
+
}
|
|
2677
|
+
return { success: false, error: "Element not found" };
|
|
2678
|
+
},
|
|
2679
|
+
/**
|
|
2680
|
+
* Local storage helpers
|
|
2681
|
+
*/
|
|
2682
|
+
localStorage: {
|
|
2683
|
+
get: (key, defaultValue) => getLocalStorage(key, defaultValue),
|
|
2684
|
+
set: (key, value) => setLocalStorage(key, value),
|
|
2685
|
+
remove: (key) => {
|
|
2686
|
+
if (typeof localStorage !== "undefined") {
|
|
2687
|
+
localStorage.removeItem(key);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
},
|
|
2691
|
+
/**
|
|
2692
|
+
* Session storage helpers
|
|
2693
|
+
*/
|
|
2694
|
+
sessionStorage: {
|
|
2695
|
+
get: (key, defaultValue) => getSessionStorage(key, defaultValue),
|
|
2696
|
+
set: (key, value) => setSessionStorage(key, value),
|
|
2697
|
+
remove: (key) => {
|
|
2698
|
+
if (typeof sessionStorage !== "undefined") {
|
|
2699
|
+
sessionStorage.removeItem(key);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2703
|
+
/**
|
|
2704
|
+
* Clipboard helpers
|
|
2705
|
+
*/
|
|
2706
|
+
clipboard: {
|
|
2707
|
+
copy: async (text) => {
|
|
2708
|
+
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
2709
|
+
try {
|
|
2710
|
+
await navigator.clipboard.writeText(text);
|
|
2711
|
+
return { success: true };
|
|
2712
|
+
} catch (error) {
|
|
2713
|
+
return { success: false, error };
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
return { success: false, error: "Clipboard not available" };
|
|
2717
|
+
}
|
|
2718
|
+
},
|
|
2719
|
+
/**
|
|
2720
|
+
* Delay execution
|
|
2721
|
+
*/
|
|
2722
|
+
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
2723
|
+
/**
|
|
2724
|
+
* Current context accessors
|
|
2725
|
+
*/
|
|
2726
|
+
currentItem: ctx.currentItem,
|
|
2727
|
+
currentIndex: ctx.currentIndex,
|
|
2728
|
+
elementId: ctx.elementId
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2440
2731
|
var builtInPlugins = {
|
|
2441
2732
|
/**
|
|
2442
2733
|
* Set state value
|
|
@@ -2563,6 +2854,7 @@ var builtInPlugins = {
|
|
|
2563
2854
|
const code = action.code || action.config?.code;
|
|
2564
2855
|
if (!code) return { success: false, error: "No code provided" };
|
|
2565
2856
|
try {
|
|
2857
|
+
const shortcuts = createShortcuts(ctx);
|
|
2566
2858
|
const fn = new Function(
|
|
2567
2859
|
"event",
|
|
2568
2860
|
"props",
|
|
@@ -2571,6 +2863,7 @@ var builtInPlugins = {
|
|
|
2571
2863
|
"stateManager",
|
|
2572
2864
|
"currentItem",
|
|
2573
2865
|
"currentIndex",
|
|
2866
|
+
"shortcuts",
|
|
2574
2867
|
code
|
|
2575
2868
|
);
|
|
2576
2869
|
const result = fn(
|
|
@@ -2580,7 +2873,8 @@ var builtInPlugins = {
|
|
|
2580
2873
|
ctx.context.context || {},
|
|
2581
2874
|
ctx.stateManager,
|
|
2582
2875
|
ctx.currentItem,
|
|
2583
|
-
ctx.currentIndex
|
|
2876
|
+
ctx.currentIndex,
|
|
2877
|
+
shortcuts
|
|
2584
2878
|
);
|
|
2585
2879
|
return { success: true, result };
|
|
2586
2880
|
} catch (error) {
|
|
@@ -5296,18 +5590,78 @@ async function mount(options) {
|
|
|
5296
5590
|
eventHandlers,
|
|
5297
5591
|
fetchOptions = {},
|
|
5298
5592
|
onReady,
|
|
5299
|
-
onError
|
|
5593
|
+
onError,
|
|
5594
|
+
onLoadStart,
|
|
5595
|
+
onLoadEnd,
|
|
5596
|
+
loadingComponent,
|
|
5597
|
+
errorComponent,
|
|
5598
|
+
loadingDelay = 0,
|
|
5599
|
+
minLoadingTime = 0
|
|
5300
5600
|
} = options;
|
|
5601
|
+
const container = typeof target === "string" ? document.querySelector(target) : target;
|
|
5602
|
+
if (!container) {
|
|
5603
|
+
const err = new Error(`Target container not found: ${target}`);
|
|
5604
|
+
onError?.(err);
|
|
5605
|
+
throw err;
|
|
5606
|
+
}
|
|
5607
|
+
let loadingElement = null;
|
|
5608
|
+
let loadingTimeout = null;
|
|
5609
|
+
let loadingStartTime = null;
|
|
5610
|
+
const showLoading = () => {
|
|
5611
|
+
if (loadingComponent) {
|
|
5612
|
+
loadingStartTime = Date.now();
|
|
5613
|
+
if (typeof loadingComponent === "string") {
|
|
5614
|
+
loadingElement = document.createElement("div");
|
|
5615
|
+
loadingElement.innerHTML = loadingComponent;
|
|
5616
|
+
loadingElement = loadingElement.firstElementChild || loadingElement;
|
|
5617
|
+
} else {
|
|
5618
|
+
loadingElement = loadingComponent.cloneNode(true);
|
|
5619
|
+
}
|
|
5620
|
+
container.appendChild(loadingElement);
|
|
5621
|
+
}
|
|
5622
|
+
};
|
|
5623
|
+
const removeLoading = async () => {
|
|
5624
|
+
if (loadingTimeout) {
|
|
5625
|
+
clearTimeout(loadingTimeout);
|
|
5626
|
+
loadingTimeout = null;
|
|
5627
|
+
}
|
|
5628
|
+
if (loadingStartTime && minLoadingTime > 0) {
|
|
5629
|
+
const elapsed = Date.now() - loadingStartTime;
|
|
5630
|
+
if (elapsed < minLoadingTime) {
|
|
5631
|
+
await new Promise((resolve) => setTimeout(resolve, minLoadingTime - elapsed));
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
if (loadingElement && loadingElement.parentNode) {
|
|
5635
|
+
loadingElement.parentNode.removeChild(loadingElement);
|
|
5636
|
+
loadingElement = null;
|
|
5637
|
+
}
|
|
5638
|
+
};
|
|
5639
|
+
const showError = (err) => {
|
|
5640
|
+
if (errorComponent) {
|
|
5641
|
+
const errorContent = errorComponent(err);
|
|
5642
|
+
if (typeof errorContent === "string") {
|
|
5643
|
+
container.innerHTML = errorContent;
|
|
5644
|
+
} else {
|
|
5645
|
+
container.innerHTML = "";
|
|
5646
|
+
container.appendChild(errorContent);
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
};
|
|
5301
5650
|
try {
|
|
5302
|
-
|
|
5303
|
-
if (
|
|
5304
|
-
|
|
5651
|
+
onLoadStart?.();
|
|
5652
|
+
if (loadingComponent) {
|
|
5653
|
+
if (loadingDelay > 0) {
|
|
5654
|
+
loadingTimeout = setTimeout(showLoading, loadingDelay);
|
|
5655
|
+
} else {
|
|
5656
|
+
showLoading();
|
|
5657
|
+
}
|
|
5305
5658
|
}
|
|
5306
5659
|
const fetchResult = await fetchComponentWithDependencies(componentId, {
|
|
5307
5660
|
...fetchOptions,
|
|
5308
5661
|
version
|
|
5309
5662
|
});
|
|
5310
5663
|
const { data, fromCache, version: resolvedVersion, registry, views } = fetchResult;
|
|
5664
|
+
await removeLoading();
|
|
5311
5665
|
const bindingContext = {
|
|
5312
5666
|
props,
|
|
5313
5667
|
state,
|
|
@@ -5338,10 +5692,14 @@ async function mount(options) {
|
|
|
5338
5692
|
fromCache,
|
|
5339
5693
|
version: resolvedVersion
|
|
5340
5694
|
};
|
|
5695
|
+
onLoadEnd?.();
|
|
5341
5696
|
onReady?.(result);
|
|
5342
5697
|
return result;
|
|
5343
5698
|
} catch (error) {
|
|
5344
5699
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
5700
|
+
await removeLoading();
|
|
5701
|
+
showError(err);
|
|
5702
|
+
onLoadEnd?.();
|
|
5345
5703
|
onError?.(err);
|
|
5346
5704
|
throw err;
|
|
5347
5705
|
}
|
|
@@ -5387,6 +5745,108 @@ function mountData(options) {
|
|
|
5387
5745
|
};
|
|
5388
5746
|
}
|
|
5389
5747
|
|
|
5748
|
+
// src/prefetch.ts
|
|
5749
|
+
async function prefetch(componentId, version = "latest", options = {}) {
|
|
5750
|
+
const { includeDependencies = true, ...fetchOptions } = options;
|
|
5751
|
+
try {
|
|
5752
|
+
if (includeDependencies) {
|
|
5753
|
+
await fetchComponentWithDependencies(componentId, { ...fetchOptions, version });
|
|
5754
|
+
} else {
|
|
5755
|
+
await fetchComponent(componentId, { ...fetchOptions, version });
|
|
5756
|
+
}
|
|
5757
|
+
return true;
|
|
5758
|
+
} catch (error) {
|
|
5759
|
+
console.warn(`[Servly] Failed to prefetch ${componentId}:`, error);
|
|
5760
|
+
return false;
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
async function prefetchAll(components, options = {}) {
|
|
5764
|
+
const results = await Promise.allSettled(
|
|
5765
|
+
components.map((comp) => {
|
|
5766
|
+
const id = typeof comp === "string" ? comp : comp.id;
|
|
5767
|
+
const version = typeof comp === "string" ? "latest" : comp.version || "latest";
|
|
5768
|
+
return prefetch(id, version, options).then((success2) => ({ id, success: success2 }));
|
|
5769
|
+
})
|
|
5770
|
+
);
|
|
5771
|
+
const success = [];
|
|
5772
|
+
const failed = [];
|
|
5773
|
+
for (const result of results) {
|
|
5774
|
+
if (result.status === "fulfilled" && result.value.success) {
|
|
5775
|
+
success.push(result.value.id);
|
|
5776
|
+
} else {
|
|
5777
|
+
const id = result.status === "fulfilled" ? result.value.id : "unknown";
|
|
5778
|
+
failed.push(id);
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
return { success, failed };
|
|
5782
|
+
}
|
|
5783
|
+
function prefetchOnVisible(target, components, options = {}) {
|
|
5784
|
+
const { rootMargin = "100px", threshold = 0, ...prefetchOptions } = options;
|
|
5785
|
+
const element = typeof target === "string" ? document.querySelector(target) : target;
|
|
5786
|
+
if (!element) {
|
|
5787
|
+
console.warn(`[Servly] prefetchOnVisible: Target not found: ${target}`);
|
|
5788
|
+
return () => {
|
|
5789
|
+
};
|
|
5790
|
+
}
|
|
5791
|
+
let hasPrefetched = false;
|
|
5792
|
+
const observer = new IntersectionObserver(
|
|
5793
|
+
(entries) => {
|
|
5794
|
+
for (const entry of entries) {
|
|
5795
|
+
if (entry.isIntersecting && !hasPrefetched) {
|
|
5796
|
+
hasPrefetched = true;
|
|
5797
|
+
prefetchAll(components, prefetchOptions);
|
|
5798
|
+
observer.disconnect();
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
},
|
|
5802
|
+
{ rootMargin, threshold }
|
|
5803
|
+
);
|
|
5804
|
+
observer.observe(element);
|
|
5805
|
+
return () => observer.disconnect();
|
|
5806
|
+
}
|
|
5807
|
+
function prefetchOnIdle(components, options = {}) {
|
|
5808
|
+
const { timeout = 5e3, ...prefetchOptions } = options;
|
|
5809
|
+
const callback = () => {
|
|
5810
|
+
prefetchAll(components, prefetchOptions);
|
|
5811
|
+
};
|
|
5812
|
+
if ("requestIdleCallback" in window) {
|
|
5813
|
+
window.requestIdleCallback(callback, { timeout });
|
|
5814
|
+
} else {
|
|
5815
|
+
setTimeout(callback, 1);
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
function prefetchOnHover(target, componentId, version = "latest", options = {}) {
|
|
5819
|
+
const { delay = 100, ...prefetchOptions } = options;
|
|
5820
|
+
const element = typeof target === "string" ? document.querySelector(target) : target;
|
|
5821
|
+
if (!element) {
|
|
5822
|
+
console.warn(`[Servly] prefetchOnHover: Target not found: ${target}`);
|
|
5823
|
+
return () => {
|
|
5824
|
+
};
|
|
5825
|
+
}
|
|
5826
|
+
let timeoutId = null;
|
|
5827
|
+
let hasPrefetched = false;
|
|
5828
|
+
const handleMouseEnter = () => {
|
|
5829
|
+
if (hasPrefetched) return;
|
|
5830
|
+
timeoutId = setTimeout(() => {
|
|
5831
|
+
hasPrefetched = true;
|
|
5832
|
+
prefetch(componentId, version, prefetchOptions);
|
|
5833
|
+
}, delay);
|
|
5834
|
+
};
|
|
5835
|
+
const handleMouseLeave = () => {
|
|
5836
|
+
if (timeoutId) {
|
|
5837
|
+
clearTimeout(timeoutId);
|
|
5838
|
+
timeoutId = null;
|
|
5839
|
+
}
|
|
5840
|
+
};
|
|
5841
|
+
element.addEventListener("mouseenter", handleMouseEnter);
|
|
5842
|
+
element.addEventListener("mouseleave", handleMouseLeave);
|
|
5843
|
+
return () => {
|
|
5844
|
+
element.removeEventListener("mouseenter", handleMouseEnter);
|
|
5845
|
+
element.removeEventListener("mouseleave", handleMouseLeave);
|
|
5846
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
|
|
5390
5850
|
// src/version.ts
|
|
5391
5851
|
function parseVersion(version) {
|
|
5392
5852
|
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
@@ -5757,6 +6217,55 @@ function getSampleValue(def) {
|
|
|
5757
6217
|
// src/index.ts
|
|
5758
6218
|
init_registry();
|
|
5759
6219
|
init_tailwind();
|
|
6220
|
+
|
|
6221
|
+
// src/slotBindings.ts
|
|
6222
|
+
function extractSlotBindings(layout) {
|
|
6223
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
6224
|
+
const elementMap = /* @__PURE__ */ new Map();
|
|
6225
|
+
for (const element of layout) {
|
|
6226
|
+
elementMap.set(element.i, element);
|
|
6227
|
+
}
|
|
6228
|
+
const traceToProps = (element, propPath, visited = /* @__PURE__ */ new Set()) => {
|
|
6229
|
+
if (visited.has(element.i)) return null;
|
|
6230
|
+
visited.add(element.i);
|
|
6231
|
+
const inputs = element.configuration?.bindings?.inputs || {};
|
|
6232
|
+
const binding = inputs[propPath];
|
|
6233
|
+
if (!binding) return null;
|
|
6234
|
+
if (binding.source === "props" && binding.path) {
|
|
6235
|
+
return binding.path;
|
|
6236
|
+
}
|
|
6237
|
+
if (binding.source === "parent" && binding.path && element.parent) {
|
|
6238
|
+
const parentElement = elementMap.get(element.parent);
|
|
6239
|
+
if (parentElement) {
|
|
6240
|
+
return traceToProps(parentElement, binding.path, visited);
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
6243
|
+
return null;
|
|
6244
|
+
};
|
|
6245
|
+
for (const element of layout) {
|
|
6246
|
+
if (element.componentId !== "slot") continue;
|
|
6247
|
+
const inputs = element.configuration?.bindings?.inputs || {};
|
|
6248
|
+
const slotId = element.configuration?.slotName || element.i;
|
|
6249
|
+
for (const [targetProp, binding] of Object.entries(inputs)) {
|
|
6250
|
+
if (["child", "children", "content"].includes(targetProp)) {
|
|
6251
|
+
const b = binding;
|
|
6252
|
+
if (!b) continue;
|
|
6253
|
+
if (b.source === "props" && b.path) {
|
|
6254
|
+
bindings.set(b.path, slotId);
|
|
6255
|
+
} else if (b.source === "parent" && b.path && element.parent) {
|
|
6256
|
+
const parentElement = elementMap.get(element.parent);
|
|
6257
|
+
if (parentElement) {
|
|
6258
|
+
const originalProp = traceToProps(parentElement, b.path);
|
|
6259
|
+
if (originalProp) {
|
|
6260
|
+
bindings.set(originalProp, slotId);
|
|
6261
|
+
}
|
|
6262
|
+
}
|
|
6263
|
+
}
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6267
|
+
return bindings;
|
|
6268
|
+
}
|
|
5760
6269
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5761
6270
|
0 && (module.exports = {
|
|
5762
6271
|
AnalyticsCollector,
|
|
@@ -5806,6 +6315,7 @@ init_tailwind();
|
|
|
5806
6315
|
extractDependenciesFromCode,
|
|
5807
6316
|
extractOverrideDependencies,
|
|
5808
6317
|
extractReferencedViewIds,
|
|
6318
|
+
extractSlotBindings,
|
|
5809
6319
|
fetchComponent,
|
|
5810
6320
|
fetchComponentWithDependencies,
|
|
5811
6321
|
formatStyleValue,
|
|
@@ -5863,7 +6373,12 @@ init_tailwind();
|
|
|
5863
6373
|
mountData,
|
|
5864
6374
|
navigateTo,
|
|
5865
6375
|
parseVersion,
|
|
6376
|
+
prefetch,
|
|
6377
|
+
prefetchAll,
|
|
5866
6378
|
prefetchComponents,
|
|
6379
|
+
prefetchOnHover,
|
|
6380
|
+
prefetchOnIdle,
|
|
6381
|
+
prefetchOnVisible,
|
|
5867
6382
|
preloadIcons,
|
|
5868
6383
|
preloadTailwind,
|
|
5869
6384
|
preventFOUC,
|
package/dist/index.js
CHANGED
|
@@ -718,8 +718,96 @@ function resolveBindingPath(path, context) {
|
|
|
718
718
|
}
|
|
719
719
|
return navigatePath(source, parts.slice(startIndex));
|
|
720
720
|
}
|
|
721
|
+
var FUNCTION_CALL_REGEX = /^(\w+)\s*\((.*)\)$/s;
|
|
722
|
+
function executeGlobalFunction(funcName, argsStr, context) {
|
|
723
|
+
if (context.functionMap?.[funcName]) {
|
|
724
|
+
try {
|
|
725
|
+
const args = parseFunctionArguments(argsStr, context);
|
|
726
|
+
return context.functionMap[funcName](...args);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
console.error(`[GlobalFunction] Error executing ${funcName}:`, error);
|
|
729
|
+
return void 0;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const func = context.globalFunctions?.find((f) => f.name === funcName);
|
|
733
|
+
if (!func) {
|
|
734
|
+
console.warn(`[GlobalFunction] Function not found: ${funcName}`);
|
|
735
|
+
return void 0;
|
|
736
|
+
}
|
|
737
|
+
try {
|
|
738
|
+
const args = parseFunctionArguments(argsStr, context);
|
|
739
|
+
const paramNames = func.parameters.map((p) => p.name);
|
|
740
|
+
const fnBody = `
|
|
741
|
+
const state = __ctx__.state || {};
|
|
742
|
+
const props = __ctx__.props || {};
|
|
743
|
+
const config = __ctx__.configs || __ctx__.context || {};
|
|
744
|
+
${func.code}
|
|
745
|
+
`;
|
|
746
|
+
const fn = new Function("__ctx__", ...paramNames, fnBody);
|
|
747
|
+
return fn(context, ...args);
|
|
748
|
+
} catch (error) {
|
|
749
|
+
console.error(`[GlobalFunction] Error executing ${funcName}:`, error);
|
|
750
|
+
return void 0;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
function parseFunctionArguments(argsStr, context) {
|
|
754
|
+
if (!argsStr.trim()) return [];
|
|
755
|
+
const args = [];
|
|
756
|
+
let current = "";
|
|
757
|
+
let depth = 0;
|
|
758
|
+
let inString = false;
|
|
759
|
+
let stringChar = "";
|
|
760
|
+
for (let i = 0; i < argsStr.length; i++) {
|
|
761
|
+
const char = argsStr[i];
|
|
762
|
+
const prevChar = argsStr[i - 1];
|
|
763
|
+
if ((char === '"' || char === "'") && prevChar !== "\\") {
|
|
764
|
+
if (!inString) {
|
|
765
|
+
inString = true;
|
|
766
|
+
stringChar = char;
|
|
767
|
+
} else if (char === stringChar) {
|
|
768
|
+
inString = false;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (!inString) {
|
|
772
|
+
if (char === "(" || char === "[" || char === "{") depth++;
|
|
773
|
+
if (char === ")" || char === "]" || char === "}") depth--;
|
|
774
|
+
}
|
|
775
|
+
if (char === "," && depth === 0 && !inString) {
|
|
776
|
+
args.push(parseArgumentValue(current.trim(), context));
|
|
777
|
+
current = "";
|
|
778
|
+
} else {
|
|
779
|
+
current += char;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (current.trim()) {
|
|
783
|
+
args.push(parseArgumentValue(current.trim(), context));
|
|
784
|
+
}
|
|
785
|
+
return args;
|
|
786
|
+
}
|
|
787
|
+
function parseArgumentValue(value, context) {
|
|
788
|
+
const trimmed = value.trim();
|
|
789
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
790
|
+
return trimmed.slice(1, -1);
|
|
791
|
+
}
|
|
792
|
+
if (!isNaN(Number(trimmed)) && trimmed !== "") {
|
|
793
|
+
return Number(trimmed);
|
|
794
|
+
}
|
|
795
|
+
if (trimmed === "true") return true;
|
|
796
|
+
if (trimmed === "false") return false;
|
|
797
|
+
if (trimmed === "null") return null;
|
|
798
|
+
if (trimmed === "undefined") return void 0;
|
|
799
|
+
if (trimmed.startsWith("{{") && trimmed.endsWith("}}")) {
|
|
800
|
+
return resolveTemplateValue(trimmed, context);
|
|
801
|
+
}
|
|
802
|
+
return resolveBindingPath(trimmed, context);
|
|
803
|
+
}
|
|
721
804
|
function resolveExpression(expression, context) {
|
|
722
805
|
const trimmed = expression.trim();
|
|
806
|
+
const funcMatch = trimmed.match(FUNCTION_CALL_REGEX);
|
|
807
|
+
if (funcMatch) {
|
|
808
|
+
const [, funcName, argsStr] = funcMatch;
|
|
809
|
+
return executeGlobalFunction(funcName, argsStr, context);
|
|
810
|
+
}
|
|
723
811
|
const comparisonOperators = ["===", "!==", "==", "!=", ">=", "<=", ">", "<"];
|
|
724
812
|
for (const op of comparisonOperators) {
|
|
725
813
|
if (trimmed.includes(op)) {
|
|
@@ -1636,6 +1724,203 @@ function getUrlInfo() {
|
|
|
1636
1724
|
}
|
|
1637
1725
|
|
|
1638
1726
|
// src/eventSystem.ts
|
|
1727
|
+
function createShortcuts(ctx) {
|
|
1728
|
+
return {
|
|
1729
|
+
/**
|
|
1730
|
+
* Render a dynamic list of items using a blueprint view
|
|
1731
|
+
* This is a simplified version for runtime - full implementation requires DOM access
|
|
1732
|
+
*/
|
|
1733
|
+
renderDynamicList: (options) => {
|
|
1734
|
+
const { blueprint, targetContainer, data = [], clearExisting = true, itemProps = {} } = options;
|
|
1735
|
+
const container = typeof document !== "undefined" ? document.querySelector(targetContainer) || document.querySelector(`[data-servly-id="${targetContainer.replace("#", "")}"]`) : null;
|
|
1736
|
+
if (!container) {
|
|
1737
|
+
console.warn(`[shortcuts.renderDynamicList] Container not found: ${targetContainer}`);
|
|
1738
|
+
return { success: false, error: "Container not found" };
|
|
1739
|
+
}
|
|
1740
|
+
if (clearExisting) {
|
|
1741
|
+
container.innerHTML = "";
|
|
1742
|
+
}
|
|
1743
|
+
const event = new CustomEvent("servly:renderDynamicList", {
|
|
1744
|
+
bubbles: true,
|
|
1745
|
+
detail: {
|
|
1746
|
+
blueprint,
|
|
1747
|
+
targetContainer,
|
|
1748
|
+
data,
|
|
1749
|
+
itemProps,
|
|
1750
|
+
container
|
|
1751
|
+
}
|
|
1752
|
+
});
|
|
1753
|
+
container.dispatchEvent(event);
|
|
1754
|
+
return { success: true, itemCount: data.length };
|
|
1755
|
+
},
|
|
1756
|
+
/**
|
|
1757
|
+
* Quick list rendering helper
|
|
1758
|
+
*/
|
|
1759
|
+
renderList: (blueprint, targetContainer, data, options = {}) => {
|
|
1760
|
+
return createShortcuts(ctx).renderDynamicList({
|
|
1761
|
+
blueprint,
|
|
1762
|
+
targetContainer,
|
|
1763
|
+
data,
|
|
1764
|
+
...options
|
|
1765
|
+
});
|
|
1766
|
+
},
|
|
1767
|
+
/**
|
|
1768
|
+
* Set state value
|
|
1769
|
+
*/
|
|
1770
|
+
setState: (key, value) => {
|
|
1771
|
+
if (ctx.stateManager) {
|
|
1772
|
+
ctx.stateManager.set(key, value, ctx.elementId);
|
|
1773
|
+
return { success: true };
|
|
1774
|
+
}
|
|
1775
|
+
return { success: false, error: "No state manager" };
|
|
1776
|
+
},
|
|
1777
|
+
/**
|
|
1778
|
+
* Get state value
|
|
1779
|
+
*/
|
|
1780
|
+
getState: (key) => {
|
|
1781
|
+
if (ctx.stateManager) {
|
|
1782
|
+
return ctx.stateManager.get(key);
|
|
1783
|
+
}
|
|
1784
|
+
return void 0;
|
|
1785
|
+
},
|
|
1786
|
+
/**
|
|
1787
|
+
* Toggle state value
|
|
1788
|
+
*/
|
|
1789
|
+
toggleState: (key) => {
|
|
1790
|
+
if (ctx.stateManager) {
|
|
1791
|
+
ctx.stateManager.toggle(key, ctx.elementId);
|
|
1792
|
+
return { success: true };
|
|
1793
|
+
}
|
|
1794
|
+
return { success: false, error: "No state manager" };
|
|
1795
|
+
},
|
|
1796
|
+
/**
|
|
1797
|
+
* Navigate to URL
|
|
1798
|
+
*/
|
|
1799
|
+
navigateTo: (url, options) => {
|
|
1800
|
+
navigateTo(url, options);
|
|
1801
|
+
return { success: true };
|
|
1802
|
+
},
|
|
1803
|
+
/**
|
|
1804
|
+
* Log to console (for debugging)
|
|
1805
|
+
*/
|
|
1806
|
+
log: (...args) => {
|
|
1807
|
+
console.log("[Servly]", ...args);
|
|
1808
|
+
},
|
|
1809
|
+
/**
|
|
1810
|
+
* Show alert
|
|
1811
|
+
*/
|
|
1812
|
+
alert: (message) => {
|
|
1813
|
+
if (typeof alert !== "undefined") {
|
|
1814
|
+
alert(message);
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
/**
|
|
1818
|
+
* Get element by ID
|
|
1819
|
+
*/
|
|
1820
|
+
getElement: (id) => {
|
|
1821
|
+
if (typeof document === "undefined") return null;
|
|
1822
|
+
return document.querySelector(`[data-servly-id="${id}"]`) || document.getElementById(id);
|
|
1823
|
+
},
|
|
1824
|
+
/**
|
|
1825
|
+
* Set element text content
|
|
1826
|
+
*/
|
|
1827
|
+
setText: (selector, text) => {
|
|
1828
|
+
if (typeof document === "undefined") return { success: false };
|
|
1829
|
+
const el = document.querySelector(selector) || document.querySelector(`[data-servly-id="${selector.replace("#", "")}"]`);
|
|
1830
|
+
if (el) {
|
|
1831
|
+
el.textContent = text;
|
|
1832
|
+
return { success: true };
|
|
1833
|
+
}
|
|
1834
|
+
return { success: false, error: "Element not found" };
|
|
1835
|
+
},
|
|
1836
|
+
/**
|
|
1837
|
+
* Add class to element
|
|
1838
|
+
*/
|
|
1839
|
+
addClass: (selector, className) => {
|
|
1840
|
+
if (typeof document === "undefined") return { success: false };
|
|
1841
|
+
const el = document.querySelector(selector);
|
|
1842
|
+
if (el) {
|
|
1843
|
+
el.classList.add(className);
|
|
1844
|
+
return { success: true };
|
|
1845
|
+
}
|
|
1846
|
+
return { success: false, error: "Element not found" };
|
|
1847
|
+
},
|
|
1848
|
+
/**
|
|
1849
|
+
* Remove class from element
|
|
1850
|
+
*/
|
|
1851
|
+
removeClass: (selector, className) => {
|
|
1852
|
+
if (typeof document === "undefined") return { success: false };
|
|
1853
|
+
const el = document.querySelector(selector);
|
|
1854
|
+
if (el) {
|
|
1855
|
+
el.classList.remove(className);
|
|
1856
|
+
return { success: true };
|
|
1857
|
+
}
|
|
1858
|
+
return { success: false, error: "Element not found" };
|
|
1859
|
+
},
|
|
1860
|
+
/**
|
|
1861
|
+
* Toggle class on element
|
|
1862
|
+
*/
|
|
1863
|
+
toggleClass: (selector, className) => {
|
|
1864
|
+
if (typeof document === "undefined") return { success: false };
|
|
1865
|
+
const el = document.querySelector(selector);
|
|
1866
|
+
if (el) {
|
|
1867
|
+
el.classList.toggle(className);
|
|
1868
|
+
return { success: true };
|
|
1869
|
+
}
|
|
1870
|
+
return { success: false, error: "Element not found" };
|
|
1871
|
+
},
|
|
1872
|
+
/**
|
|
1873
|
+
* Local storage helpers
|
|
1874
|
+
*/
|
|
1875
|
+
localStorage: {
|
|
1876
|
+
get: (key, defaultValue) => getLocalStorage(key, defaultValue),
|
|
1877
|
+
set: (key, value) => setLocalStorage(key, value),
|
|
1878
|
+
remove: (key) => {
|
|
1879
|
+
if (typeof localStorage !== "undefined") {
|
|
1880
|
+
localStorage.removeItem(key);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
},
|
|
1884
|
+
/**
|
|
1885
|
+
* Session storage helpers
|
|
1886
|
+
*/
|
|
1887
|
+
sessionStorage: {
|
|
1888
|
+
get: (key, defaultValue) => getSessionStorage(key, defaultValue),
|
|
1889
|
+
set: (key, value) => setSessionStorage(key, value),
|
|
1890
|
+
remove: (key) => {
|
|
1891
|
+
if (typeof sessionStorage !== "undefined") {
|
|
1892
|
+
sessionStorage.removeItem(key);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1896
|
+
/**
|
|
1897
|
+
* Clipboard helpers
|
|
1898
|
+
*/
|
|
1899
|
+
clipboard: {
|
|
1900
|
+
copy: async (text) => {
|
|
1901
|
+
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
1902
|
+
try {
|
|
1903
|
+
await navigator.clipboard.writeText(text);
|
|
1904
|
+
return { success: true };
|
|
1905
|
+
} catch (error) {
|
|
1906
|
+
return { success: false, error };
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
return { success: false, error: "Clipboard not available" };
|
|
1910
|
+
}
|
|
1911
|
+
},
|
|
1912
|
+
/**
|
|
1913
|
+
* Delay execution
|
|
1914
|
+
*/
|
|
1915
|
+
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
1916
|
+
/**
|
|
1917
|
+
* Current context accessors
|
|
1918
|
+
*/
|
|
1919
|
+
currentItem: ctx.currentItem,
|
|
1920
|
+
currentIndex: ctx.currentIndex,
|
|
1921
|
+
elementId: ctx.elementId
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1639
1924
|
var builtInPlugins = {
|
|
1640
1925
|
/**
|
|
1641
1926
|
* Set state value
|
|
@@ -1762,6 +2047,7 @@ var builtInPlugins = {
|
|
|
1762
2047
|
const code = action.code || action.config?.code;
|
|
1763
2048
|
if (!code) return { success: false, error: "No code provided" };
|
|
1764
2049
|
try {
|
|
2050
|
+
const shortcuts = createShortcuts(ctx);
|
|
1765
2051
|
const fn = new Function(
|
|
1766
2052
|
"event",
|
|
1767
2053
|
"props",
|
|
@@ -1770,6 +2056,7 @@ var builtInPlugins = {
|
|
|
1770
2056
|
"stateManager",
|
|
1771
2057
|
"currentItem",
|
|
1772
2058
|
"currentIndex",
|
|
2059
|
+
"shortcuts",
|
|
1773
2060
|
code
|
|
1774
2061
|
);
|
|
1775
2062
|
const result = fn(
|
|
@@ -1779,7 +2066,8 @@ var builtInPlugins = {
|
|
|
1779
2066
|
ctx.context.context || {},
|
|
1780
2067
|
ctx.stateManager,
|
|
1781
2068
|
ctx.currentItem,
|
|
1782
|
-
ctx.currentIndex
|
|
2069
|
+
ctx.currentIndex,
|
|
2070
|
+
shortcuts
|
|
1783
2071
|
);
|
|
1784
2072
|
return { success: true, result };
|
|
1785
2073
|
} catch (error) {
|
|
@@ -4491,18 +4779,78 @@ async function mount(options) {
|
|
|
4491
4779
|
eventHandlers,
|
|
4492
4780
|
fetchOptions = {},
|
|
4493
4781
|
onReady,
|
|
4494
|
-
onError
|
|
4782
|
+
onError,
|
|
4783
|
+
onLoadStart,
|
|
4784
|
+
onLoadEnd,
|
|
4785
|
+
loadingComponent,
|
|
4786
|
+
errorComponent,
|
|
4787
|
+
loadingDelay = 0,
|
|
4788
|
+
minLoadingTime = 0
|
|
4495
4789
|
} = options;
|
|
4790
|
+
const container = typeof target === "string" ? document.querySelector(target) : target;
|
|
4791
|
+
if (!container) {
|
|
4792
|
+
const err = new Error(`Target container not found: ${target}`);
|
|
4793
|
+
onError?.(err);
|
|
4794
|
+
throw err;
|
|
4795
|
+
}
|
|
4796
|
+
let loadingElement = null;
|
|
4797
|
+
let loadingTimeout = null;
|
|
4798
|
+
let loadingStartTime = null;
|
|
4799
|
+
const showLoading = () => {
|
|
4800
|
+
if (loadingComponent) {
|
|
4801
|
+
loadingStartTime = Date.now();
|
|
4802
|
+
if (typeof loadingComponent === "string") {
|
|
4803
|
+
loadingElement = document.createElement("div");
|
|
4804
|
+
loadingElement.innerHTML = loadingComponent;
|
|
4805
|
+
loadingElement = loadingElement.firstElementChild || loadingElement;
|
|
4806
|
+
} else {
|
|
4807
|
+
loadingElement = loadingComponent.cloneNode(true);
|
|
4808
|
+
}
|
|
4809
|
+
container.appendChild(loadingElement);
|
|
4810
|
+
}
|
|
4811
|
+
};
|
|
4812
|
+
const removeLoading = async () => {
|
|
4813
|
+
if (loadingTimeout) {
|
|
4814
|
+
clearTimeout(loadingTimeout);
|
|
4815
|
+
loadingTimeout = null;
|
|
4816
|
+
}
|
|
4817
|
+
if (loadingStartTime && minLoadingTime > 0) {
|
|
4818
|
+
const elapsed = Date.now() - loadingStartTime;
|
|
4819
|
+
if (elapsed < minLoadingTime) {
|
|
4820
|
+
await new Promise((resolve) => setTimeout(resolve, minLoadingTime - elapsed));
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
if (loadingElement && loadingElement.parentNode) {
|
|
4824
|
+
loadingElement.parentNode.removeChild(loadingElement);
|
|
4825
|
+
loadingElement = null;
|
|
4826
|
+
}
|
|
4827
|
+
};
|
|
4828
|
+
const showError = (err) => {
|
|
4829
|
+
if (errorComponent) {
|
|
4830
|
+
const errorContent = errorComponent(err);
|
|
4831
|
+
if (typeof errorContent === "string") {
|
|
4832
|
+
container.innerHTML = errorContent;
|
|
4833
|
+
} else {
|
|
4834
|
+
container.innerHTML = "";
|
|
4835
|
+
container.appendChild(errorContent);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
};
|
|
4496
4839
|
try {
|
|
4497
|
-
|
|
4498
|
-
if (
|
|
4499
|
-
|
|
4840
|
+
onLoadStart?.();
|
|
4841
|
+
if (loadingComponent) {
|
|
4842
|
+
if (loadingDelay > 0) {
|
|
4843
|
+
loadingTimeout = setTimeout(showLoading, loadingDelay);
|
|
4844
|
+
} else {
|
|
4845
|
+
showLoading();
|
|
4846
|
+
}
|
|
4500
4847
|
}
|
|
4501
4848
|
const fetchResult = await fetchComponentWithDependencies(componentId, {
|
|
4502
4849
|
...fetchOptions,
|
|
4503
4850
|
version
|
|
4504
4851
|
});
|
|
4505
4852
|
const { data, fromCache, version: resolvedVersion, registry, views } = fetchResult;
|
|
4853
|
+
await removeLoading();
|
|
4506
4854
|
const bindingContext = {
|
|
4507
4855
|
props,
|
|
4508
4856
|
state,
|
|
@@ -4533,10 +4881,14 @@ async function mount(options) {
|
|
|
4533
4881
|
fromCache,
|
|
4534
4882
|
version: resolvedVersion
|
|
4535
4883
|
};
|
|
4884
|
+
onLoadEnd?.();
|
|
4536
4885
|
onReady?.(result);
|
|
4537
4886
|
return result;
|
|
4538
4887
|
} catch (error) {
|
|
4539
4888
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
4889
|
+
await removeLoading();
|
|
4890
|
+
showError(err);
|
|
4891
|
+
onLoadEnd?.();
|
|
4540
4892
|
onError?.(err);
|
|
4541
4893
|
throw err;
|
|
4542
4894
|
}
|
|
@@ -4582,6 +4934,108 @@ function mountData(options) {
|
|
|
4582
4934
|
};
|
|
4583
4935
|
}
|
|
4584
4936
|
|
|
4937
|
+
// src/prefetch.ts
|
|
4938
|
+
async function prefetch(componentId, version = "latest", options = {}) {
|
|
4939
|
+
const { includeDependencies = true, ...fetchOptions } = options;
|
|
4940
|
+
try {
|
|
4941
|
+
if (includeDependencies) {
|
|
4942
|
+
await fetchComponentWithDependencies(componentId, { ...fetchOptions, version });
|
|
4943
|
+
} else {
|
|
4944
|
+
await fetchComponent(componentId, { ...fetchOptions, version });
|
|
4945
|
+
}
|
|
4946
|
+
return true;
|
|
4947
|
+
} catch (error) {
|
|
4948
|
+
console.warn(`[Servly] Failed to prefetch ${componentId}:`, error);
|
|
4949
|
+
return false;
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
async function prefetchAll(components, options = {}) {
|
|
4953
|
+
const results = await Promise.allSettled(
|
|
4954
|
+
components.map((comp) => {
|
|
4955
|
+
const id = typeof comp === "string" ? comp : comp.id;
|
|
4956
|
+
const version = typeof comp === "string" ? "latest" : comp.version || "latest";
|
|
4957
|
+
return prefetch(id, version, options).then((success2) => ({ id, success: success2 }));
|
|
4958
|
+
})
|
|
4959
|
+
);
|
|
4960
|
+
const success = [];
|
|
4961
|
+
const failed = [];
|
|
4962
|
+
for (const result of results) {
|
|
4963
|
+
if (result.status === "fulfilled" && result.value.success) {
|
|
4964
|
+
success.push(result.value.id);
|
|
4965
|
+
} else {
|
|
4966
|
+
const id = result.status === "fulfilled" ? result.value.id : "unknown";
|
|
4967
|
+
failed.push(id);
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
return { success, failed };
|
|
4971
|
+
}
|
|
4972
|
+
function prefetchOnVisible(target, components, options = {}) {
|
|
4973
|
+
const { rootMargin = "100px", threshold = 0, ...prefetchOptions } = options;
|
|
4974
|
+
const element = typeof target === "string" ? document.querySelector(target) : target;
|
|
4975
|
+
if (!element) {
|
|
4976
|
+
console.warn(`[Servly] prefetchOnVisible: Target not found: ${target}`);
|
|
4977
|
+
return () => {
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
let hasPrefetched = false;
|
|
4981
|
+
const observer = new IntersectionObserver(
|
|
4982
|
+
(entries) => {
|
|
4983
|
+
for (const entry of entries) {
|
|
4984
|
+
if (entry.isIntersecting && !hasPrefetched) {
|
|
4985
|
+
hasPrefetched = true;
|
|
4986
|
+
prefetchAll(components, prefetchOptions);
|
|
4987
|
+
observer.disconnect();
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
},
|
|
4991
|
+
{ rootMargin, threshold }
|
|
4992
|
+
);
|
|
4993
|
+
observer.observe(element);
|
|
4994
|
+
return () => observer.disconnect();
|
|
4995
|
+
}
|
|
4996
|
+
function prefetchOnIdle(components, options = {}) {
|
|
4997
|
+
const { timeout = 5e3, ...prefetchOptions } = options;
|
|
4998
|
+
const callback = () => {
|
|
4999
|
+
prefetchAll(components, prefetchOptions);
|
|
5000
|
+
};
|
|
5001
|
+
if ("requestIdleCallback" in window) {
|
|
5002
|
+
window.requestIdleCallback(callback, { timeout });
|
|
5003
|
+
} else {
|
|
5004
|
+
setTimeout(callback, 1);
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
function prefetchOnHover(target, componentId, version = "latest", options = {}) {
|
|
5008
|
+
const { delay = 100, ...prefetchOptions } = options;
|
|
5009
|
+
const element = typeof target === "string" ? document.querySelector(target) : target;
|
|
5010
|
+
if (!element) {
|
|
5011
|
+
console.warn(`[Servly] prefetchOnHover: Target not found: ${target}`);
|
|
5012
|
+
return () => {
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
let timeoutId = null;
|
|
5016
|
+
let hasPrefetched = false;
|
|
5017
|
+
const handleMouseEnter = () => {
|
|
5018
|
+
if (hasPrefetched) return;
|
|
5019
|
+
timeoutId = setTimeout(() => {
|
|
5020
|
+
hasPrefetched = true;
|
|
5021
|
+
prefetch(componentId, version, prefetchOptions);
|
|
5022
|
+
}, delay);
|
|
5023
|
+
};
|
|
5024
|
+
const handleMouseLeave = () => {
|
|
5025
|
+
if (timeoutId) {
|
|
5026
|
+
clearTimeout(timeoutId);
|
|
5027
|
+
timeoutId = null;
|
|
5028
|
+
}
|
|
5029
|
+
};
|
|
5030
|
+
element.addEventListener("mouseenter", handleMouseEnter);
|
|
5031
|
+
element.addEventListener("mouseleave", handleMouseLeave);
|
|
5032
|
+
return () => {
|
|
5033
|
+
element.removeEventListener("mouseenter", handleMouseEnter);
|
|
5034
|
+
element.removeEventListener("mouseleave", handleMouseLeave);
|
|
5035
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
5036
|
+
};
|
|
5037
|
+
}
|
|
5038
|
+
|
|
4585
5039
|
// src/version.ts
|
|
4586
5040
|
function parseVersion(version) {
|
|
4587
5041
|
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
@@ -4948,6 +5402,55 @@ function getSampleValue(def) {
|
|
|
4948
5402
|
return def.defaultValue;
|
|
4949
5403
|
}
|
|
4950
5404
|
}
|
|
5405
|
+
|
|
5406
|
+
// src/slotBindings.ts
|
|
5407
|
+
function extractSlotBindings(layout) {
|
|
5408
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
5409
|
+
const elementMap = /* @__PURE__ */ new Map();
|
|
5410
|
+
for (const element of layout) {
|
|
5411
|
+
elementMap.set(element.i, element);
|
|
5412
|
+
}
|
|
5413
|
+
const traceToProps = (element, propPath, visited = /* @__PURE__ */ new Set()) => {
|
|
5414
|
+
if (visited.has(element.i)) return null;
|
|
5415
|
+
visited.add(element.i);
|
|
5416
|
+
const inputs = element.configuration?.bindings?.inputs || {};
|
|
5417
|
+
const binding = inputs[propPath];
|
|
5418
|
+
if (!binding) return null;
|
|
5419
|
+
if (binding.source === "props" && binding.path) {
|
|
5420
|
+
return binding.path;
|
|
5421
|
+
}
|
|
5422
|
+
if (binding.source === "parent" && binding.path && element.parent) {
|
|
5423
|
+
const parentElement = elementMap.get(element.parent);
|
|
5424
|
+
if (parentElement) {
|
|
5425
|
+
return traceToProps(parentElement, binding.path, visited);
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
return null;
|
|
5429
|
+
};
|
|
5430
|
+
for (const element of layout) {
|
|
5431
|
+
if (element.componentId !== "slot") continue;
|
|
5432
|
+
const inputs = element.configuration?.bindings?.inputs || {};
|
|
5433
|
+
const slotId = element.configuration?.slotName || element.i;
|
|
5434
|
+
for (const [targetProp, binding] of Object.entries(inputs)) {
|
|
5435
|
+
if (["child", "children", "content"].includes(targetProp)) {
|
|
5436
|
+
const b = binding;
|
|
5437
|
+
if (!b) continue;
|
|
5438
|
+
if (b.source === "props" && b.path) {
|
|
5439
|
+
bindings.set(b.path, slotId);
|
|
5440
|
+
} else if (b.source === "parent" && b.path && element.parent) {
|
|
5441
|
+
const parentElement = elementMap.get(element.parent);
|
|
5442
|
+
if (parentElement) {
|
|
5443
|
+
const originalProp = traceToProps(parentElement, b.path);
|
|
5444
|
+
if (originalProp) {
|
|
5445
|
+
bindings.set(originalProp, slotId);
|
|
5446
|
+
}
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
}
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
return bindings;
|
|
5453
|
+
}
|
|
4951
5454
|
export {
|
|
4952
5455
|
AnalyticsCollector,
|
|
4953
5456
|
DEFAULT_CACHE_CONFIG,
|
|
@@ -4996,6 +5499,7 @@ export {
|
|
|
4996
5499
|
extractDependenciesFromCode,
|
|
4997
5500
|
extractOverrideDependencies,
|
|
4998
5501
|
extractReferencedViewIds,
|
|
5502
|
+
extractSlotBindings,
|
|
4999
5503
|
fetchComponent,
|
|
5000
5504
|
fetchComponentWithDependencies,
|
|
5001
5505
|
formatStyleValue,
|
|
@@ -5053,7 +5557,12 @@ export {
|
|
|
5053
5557
|
mountData,
|
|
5054
5558
|
navigateTo,
|
|
5055
5559
|
parseVersion,
|
|
5560
|
+
prefetch,
|
|
5561
|
+
prefetchAll,
|
|
5056
5562
|
prefetchComponents,
|
|
5563
|
+
prefetchOnHover,
|
|
5564
|
+
prefetchOnIdle,
|
|
5565
|
+
prefetchOnVisible,
|
|
5057
5566
|
preloadIcons,
|
|
5058
5567
|
preloadTailwind,
|
|
5059
5568
|
preventFOUC,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servlyadmin/runtime-core",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Framework-agnostic core renderer for Servly components",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Framework-agnostic core renderer for Servly components with prefetching and loading states",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|