@servlyadmin/runtime-core 0.1.7 → 0.1.9

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/dist/index.js CHANGED
@@ -1,13 +1,522 @@
1
- import {
2
- buildRegistryFromBundle,
3
- collectAllDependencies,
4
- createRegistry,
5
- detectCircularDependencies,
6
- extractDependencies,
7
- extractDependenciesFromCode
8
- } from "./chunk-CIUQK4GA.js";
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __esm = (fn, res) => function __init() {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ };
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // packages/runtime-core/src/tailwind.ts
33
+ var tailwind_exports = {};
34
+ __export(tailwind_exports, {
35
+ DEFAULT_SERVLY_TAILWIND_CONFIG: () => DEFAULT_SERVLY_TAILWIND_CONFIG,
36
+ addCustomStyles: () => addCustomStyles,
37
+ default: () => tailwind_default,
38
+ getTailwind: () => getTailwind,
39
+ initServlyTailwind: () => initServlyTailwind,
40
+ injectTailwind: () => injectTailwind,
41
+ isTailwindLoaded: () => isTailwindLoaded,
42
+ removeCustomStyles: () => removeCustomStyles,
43
+ removeTailwind: () => removeTailwind,
44
+ updateTailwindConfig: () => updateTailwindConfig
45
+ });
46
+ function injectTailwind(config = {}) {
47
+ return new Promise((resolve, reject) => {
48
+ if (tailwindInjected && tailwindScript) {
49
+ resolve();
50
+ return;
51
+ }
52
+ if (typeof document === "undefined") {
53
+ resolve();
54
+ return;
55
+ }
56
+ if (window.tailwind) {
57
+ tailwindInjected = true;
58
+ config.onReady?.();
59
+ resolve();
60
+ return;
61
+ }
62
+ const {
63
+ cdnUrl = DEFAULT_TAILWIND_CDN,
64
+ config: tailwindConfig,
65
+ plugins = [],
66
+ usePlayCdn = false,
67
+ onReady,
68
+ onError
69
+ } = config;
70
+ const script = document.createElement("script");
71
+ script.src = usePlayCdn ? `${cdnUrl}?plugins=forms,typography,aspect-ratio` : cdnUrl;
72
+ script.async = true;
73
+ script.onload = () => {
74
+ tailwindInjected = true;
75
+ tailwindScript = script;
76
+ if (tailwindConfig && window.tailwind) {
77
+ window.tailwind.config = tailwindConfig;
78
+ }
79
+ onReady?.();
80
+ resolve();
81
+ };
82
+ script.onerror = (event) => {
83
+ const error = new Error(`Failed to load Tailwind CSS from ${cdnUrl}`);
84
+ onError?.(error);
85
+ reject(error);
86
+ };
87
+ document.head.appendChild(script);
88
+ });
89
+ }
90
+ function removeTailwind() {
91
+ if (tailwindScript && tailwindScript.parentNode) {
92
+ tailwindScript.parentNode.removeChild(tailwindScript);
93
+ tailwindScript = null;
94
+ tailwindInjected = false;
95
+ delete window.tailwind;
96
+ }
97
+ }
98
+ function isTailwindLoaded() {
99
+ return tailwindInjected || !!window.tailwind;
100
+ }
101
+ function getTailwind() {
102
+ return window.tailwind;
103
+ }
104
+ function updateTailwindConfig(config) {
105
+ if (window.tailwind) {
106
+ window.tailwind.config = {
107
+ ...window.tailwind.config,
108
+ ...config
109
+ };
110
+ }
111
+ }
112
+ function addCustomStyles(css, id) {
113
+ if (typeof document === "undefined") {
114
+ throw new Error("addCustomStyles can only be used in browser environment");
115
+ }
116
+ const styleId = id || `servly-custom-styles-${Date.now()}`;
117
+ let existingStyle = document.getElementById(styleId);
118
+ if (existingStyle) {
119
+ existingStyle.textContent = css;
120
+ return existingStyle;
121
+ }
122
+ const style = document.createElement("style");
123
+ style.id = styleId;
124
+ style.textContent = css;
125
+ document.head.appendChild(style);
126
+ return style;
127
+ }
128
+ function removeCustomStyles(id) {
129
+ if (typeof document === "undefined") return;
130
+ const style = document.getElementById(id);
131
+ if (style && style.parentNode) {
132
+ style.parentNode.removeChild(style);
133
+ }
134
+ }
135
+ async function initServlyTailwind(customConfig) {
136
+ const config = customConfig ? { ...DEFAULT_SERVLY_TAILWIND_CONFIG, ...customConfig } : DEFAULT_SERVLY_TAILWIND_CONFIG;
137
+ await injectTailwind({
138
+ config,
139
+ usePlayCdn: true
140
+ });
141
+ }
142
+ var DEFAULT_TAILWIND_CDN, tailwindInjected, tailwindScript, DEFAULT_SERVLY_TAILWIND_CONFIG, tailwind_default;
143
+ var init_tailwind = __esm({
144
+ "packages/runtime-core/src/tailwind.ts"() {
145
+ DEFAULT_TAILWIND_CDN = "https://cdn.tailwindcss.com";
146
+ tailwindInjected = false;
147
+ tailwindScript = null;
148
+ DEFAULT_SERVLY_TAILWIND_CONFIG = {
149
+ theme: {
150
+ extend: {
151
+ // Add any Servly-specific theme extensions here
152
+ }
153
+ },
154
+ // Safelist common dynamic classes
155
+ safelist: [
156
+ // Spacing
157
+ { pattern: /^(p|m|gap)-/ },
158
+ // Sizing
159
+ { pattern: /^(w|h|min-w|min-h|max-w|max-h)-/ },
160
+ // Flexbox
161
+ { pattern: /^(flex|justify|items|self)-/ },
162
+ // Grid
163
+ { pattern: /^(grid|col|row)-/ },
164
+ // Colors
165
+ { pattern: /^(bg|text|border|ring)-/ },
166
+ // Typography
167
+ { pattern: /^(font|text|leading|tracking)-/ },
168
+ // Borders
169
+ { pattern: /^(rounded|border)-/ },
170
+ // Effects
171
+ { pattern: /^(shadow|opacity|blur)-/ },
172
+ // Transforms
173
+ { pattern: /^(scale|rotate|translate|skew)-/ },
174
+ // Transitions
175
+ { pattern: /^(transition|duration|ease|delay)-/ }
176
+ ]
177
+ };
178
+ tailwind_default = {
179
+ injectTailwind,
180
+ removeTailwind,
181
+ isTailwindLoaded,
182
+ getTailwind,
183
+ updateTailwindConfig,
184
+ addCustomStyles,
185
+ removeCustomStyles,
186
+ initServlyTailwind,
187
+ DEFAULT_SERVLY_TAILWIND_CONFIG
188
+ };
189
+ }
190
+ });
191
+
192
+ // packages/runtime-core/src/registry.ts
193
+ var registry_exports = {};
194
+ __export(registry_exports, {
195
+ buildRegistryFromBundle: () => buildRegistryFromBundle,
196
+ collectAllDependencies: () => collectAllDependencies,
197
+ createRegistry: () => createRegistry,
198
+ detectCircularDependencies: () => detectCircularDependencies,
199
+ extractDependencies: () => extractDependencies,
200
+ extractDependenciesFromCode: () => extractDependenciesFromCode
201
+ });
202
+ function createRegistry() {
203
+ const components = /* @__PURE__ */ new Map();
204
+ return {
205
+ get(id, version) {
206
+ if (version) {
207
+ const key = `${id}@${version}`;
208
+ if (components.has(key)) {
209
+ return components.get(key);
210
+ }
211
+ }
212
+ for (const [key, component] of components) {
213
+ if (key.startsWith(`${id}@`)) {
214
+ return component;
215
+ }
216
+ }
217
+ return components.get(id);
218
+ },
219
+ has(id, version) {
220
+ if (version) {
221
+ return components.has(`${id}@${version}`);
222
+ }
223
+ for (const key of components.keys()) {
224
+ if (key.startsWith(`${id}@`) || key === id) {
225
+ return true;
226
+ }
227
+ }
228
+ return false;
229
+ },
230
+ set(id, version, component) {
231
+ components.set(`${id}@${version}`, component);
232
+ }
233
+ };
234
+ }
235
+ function buildRegistryFromBundle(data) {
236
+ const registry = createRegistry();
237
+ registry.set(data.id, data.version, {
238
+ layout: data.layout,
239
+ propsInterface: data.propsInterface
240
+ });
241
+ if (data.bundle) {
242
+ for (const [key, component] of Object.entries(data.bundle)) {
243
+ const [id, version] = key.split("@");
244
+ if (id && version) {
245
+ registry.set(id, version, component);
246
+ }
247
+ }
248
+ }
249
+ return registry;
250
+ }
251
+ function extractDependencies(elements) {
252
+ const dependencies = [];
253
+ for (const element of elements) {
254
+ const config = element.configuration;
255
+ if (!config) continue;
256
+ if (config.componentViewRef) {
257
+ dependencies.push({
258
+ id: config.componentViewRef,
259
+ version: config.componentViewVersion,
260
+ type: "viewRef",
261
+ elementId: element.i
262
+ });
263
+ }
264
+ if (config.blueprint) {
265
+ dependencies.push({
266
+ id: config.blueprint,
267
+ version: config.blueprintVersion,
268
+ type: "blueprint",
269
+ elementId: element.i
270
+ });
271
+ }
272
+ }
273
+ return dependencies;
274
+ }
275
+ function extractDependenciesFromCode(code) {
276
+ const dependencies = [];
277
+ const pattern = /renderDynamicList\s*\(\s*\{[^}]*blueprint\s*:\s*["']([^"']+)["']/g;
278
+ let match;
279
+ while ((match = pattern.exec(code)) !== null) {
280
+ dependencies.push({
281
+ id: match[1],
282
+ type: "blueprint"
283
+ });
284
+ }
285
+ return dependencies;
286
+ }
287
+ async function collectAllDependencies(rootId, rootVersion, fetchComponent2, maxDepth = 10) {
288
+ const manifest = {};
289
+ const visited = /* @__PURE__ */ new Set();
290
+ async function collect(id, version, via, depth) {
291
+ if (depth > maxDepth) {
292
+ console.warn(`Max dependency depth (${maxDepth}) reached for ${id}`);
293
+ return;
294
+ }
295
+ const key = `${id}@${version || "latest"}`;
296
+ if (visited.has(key)) {
297
+ return;
298
+ }
299
+ visited.add(key);
300
+ try {
301
+ const component = await fetchComponent2(id, version);
302
+ if (!component) {
303
+ console.warn(`Dependency not found: ${id}@${version || "latest"}`);
304
+ return;
305
+ }
306
+ manifest[id] = {
307
+ version: version || "latest",
308
+ resolved: component.version,
309
+ type: via ? "viewRef" : "viewRef",
310
+ // Will be set by caller
311
+ via
312
+ };
313
+ const nestedDeps = extractDependencies(component.layout);
314
+ for (const dep of nestedDeps) {
315
+ await collect(dep.id, dep.version, id, depth + 1);
316
+ }
317
+ } catch (error) {
318
+ console.error(`Failed to fetch dependency ${id}:`, error);
319
+ }
320
+ }
321
+ const rootComponent = await fetchComponent2(rootId, rootVersion);
322
+ if (rootComponent) {
323
+ const rootDeps = extractDependencies(rootComponent.layout);
324
+ for (const dep of rootDeps) {
325
+ manifest[dep.id] = {
326
+ version: dep.version || "latest",
327
+ resolved: "",
328
+ // Will be filled when fetched
329
+ type: dep.type
330
+ };
331
+ await collect(dep.id, dep.version, void 0, 1);
332
+ }
333
+ }
334
+ return manifest;
335
+ }
336
+ function detectCircularDependencies(manifest) {
337
+ const graph = /* @__PURE__ */ new Map();
338
+ for (const [id, entry] of Object.entries(manifest)) {
339
+ if (entry.via) {
340
+ const deps = graph.get(entry.via) || [];
341
+ deps.push(id);
342
+ graph.set(entry.via, deps);
343
+ }
344
+ }
345
+ const visited = /* @__PURE__ */ new Set();
346
+ const stack = /* @__PURE__ */ new Set();
347
+ const path = [];
348
+ function dfs(node) {
349
+ if (stack.has(node)) {
350
+ const cycleStart = path.indexOf(node);
351
+ return [...path.slice(cycleStart), node];
352
+ }
353
+ if (visited.has(node)) {
354
+ return null;
355
+ }
356
+ visited.add(node);
357
+ stack.add(node);
358
+ path.push(node);
359
+ const neighbors = graph.get(node) || [];
360
+ for (const neighbor of neighbors) {
361
+ const cycle = dfs(neighbor);
362
+ if (cycle) return cycle;
363
+ }
364
+ stack.delete(node);
365
+ path.pop();
366
+ return null;
367
+ }
368
+ for (const node of graph.keys()) {
369
+ const cycle = dfs(node);
370
+ if (cycle) return cycle;
371
+ }
372
+ return null;
373
+ }
374
+ var init_registry = __esm({
375
+ "packages/runtime-core/src/registry.ts"() {
376
+ }
377
+ });
378
+
379
+ // packages/runtime-core/src/index.ts
380
+ var index_exports = {};
381
+ __export(index_exports, {
382
+ AnalyticsCollector: () => AnalyticsCollector,
383
+ DEFAULT_CACHE_CONFIG: () => DEFAULT_CACHE_CONFIG,
384
+ DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
385
+ DEFAULT_SERVLY_TAILWIND_CONFIG: () => DEFAULT_SERVLY_TAILWIND_CONFIG,
386
+ EVENT_HANDLERS: () => EVENT_HANDLERS,
387
+ EventSystem: () => EventSystem,
388
+ LongTaskObserver: () => LongTaskObserver,
389
+ MemorySampler: () => MemorySampler,
390
+ OverrideSystem: () => OverrideSystem,
391
+ SessionManager: () => SessionManager,
392
+ StateManager: () => StateManager,
393
+ addClass: () => addClass,
394
+ addCustomStyles: () => addCustomStyles,
395
+ analytics: () => analytics,
396
+ applyStyles: () => applyStyles,
397
+ batchFetchComponents: () => batchFetchComponents,
398
+ buildClassName: () => buildClassName,
399
+ buildElementStyles: () => buildElementStyles,
400
+ buildRegistryFromBundle: () => buildRegistryFromBundle,
401
+ bumpVersion: () => bumpVersion,
402
+ camelToKebab: () => camelToKebab,
403
+ clearAllCaches: () => clearAllCaches,
404
+ clearIconCache: () => clearIconCache,
405
+ clearLocalStorageCache: () => clearLocalStorageCache,
406
+ clearMemoryCache: () => clearMemoryCache,
407
+ clearStyles: () => clearStyles,
408
+ collectAllDependencies: () => collectAllDependencies,
409
+ collectAllViewDependencies: () => collectAllViewDependencies,
410
+ compareVersions: () => compareVersions,
411
+ configureAnalytics: () => configureAnalytics,
412
+ createIconSVG: () => createIconSVG,
413
+ createPlaceholderIcon: () => createPlaceholderIcon,
414
+ createRegistry: () => createRegistry,
415
+ createServlyRenderer: () => createServlyRenderer,
416
+ createViewsMap: () => createViewsMap,
417
+ deepMerge: () => deepMerge,
418
+ deleteValueByPath: () => deleteValueByPath,
419
+ detectCircularDependencies: () => detectCircularDependencies,
420
+ extractBindingKeys: () => extractBindingKeys,
421
+ extractDependencies: () => extractDependencies,
422
+ extractDependenciesFromCode: () => extractDependenciesFromCode,
423
+ extractIconFromReactIcons: () => extractIconFromReactIcons,
424
+ extractIconsForLayout: () => extractIconsForLayout,
425
+ extractOverrideDependencies: () => extractOverrideDependencies,
426
+ extractReferencedViewIds: () => extractReferencedViewIds,
427
+ fetchComponent: () => fetchComponent,
428
+ fetchComponentWithDependencies: () => fetchComponentWithDependencies,
429
+ findIconsInLayout: () => findIconsInLayout,
430
+ formatStyleValue: () => formatStyleValue,
431
+ formatVersion: () => formatVersion,
432
+ generateIconBundle: () => generateIconBundle,
433
+ generateTestCases: () => generateTestCases,
434
+ getAnalytics: () => getAnalytics,
435
+ getCacheKey: () => getCacheKey,
436
+ getCleanupOverrides: () => getCleanupOverrides,
437
+ getDependencyTree: () => getDependencyTree,
438
+ getEventSystem: () => getEventSystem,
439
+ getFromCache: () => getFromCache,
440
+ getIconData: () => getIconData,
441
+ getIconDataSync: () => getIconDataSync,
442
+ getIconifyCollection: () => getIconifyCollection,
443
+ getLocalStorage: () => getLocalStorage,
444
+ getLongTaskObserver: () => getLongTaskObserver,
445
+ getMemoryCacheSize: () => getMemoryCacheSize,
446
+ getMemorySampler: () => getMemorySampler,
447
+ getMountOverrides: () => getMountOverrides,
448
+ getOverrideSystem: () => getOverrideSystem,
449
+ getRegisteredIconKeys: () => getRegisteredIconKeys,
450
+ getRegistryUrl: () => getRegistryUrl,
451
+ getSessionManager: () => getSessionManager,
452
+ getSessionStorage: () => getSessionStorage,
453
+ getSupportedIconSets: () => getSupportedIconSets,
454
+ getTailwind: () => getTailwind,
455
+ getUrlInfo: () => getUrlInfo,
456
+ getValueByPath: () => getValueByPath,
457
+ goBack: () => goBack,
458
+ goForward: () => goForward,
459
+ hasClass: () => hasClass,
460
+ hasDependencyOverrides: () => hasDependencyOverrides,
461
+ hasOverrides: () => hasOverrides,
462
+ hasTemplateSyntax: () => hasTemplateSyntax,
463
+ initServlyTailwind: () => initServlyTailwind,
464
+ injectTailwind: () => injectTailwind,
465
+ invalidateCache: () => invalidateCache,
466
+ isComponentAvailable: () => isComponentAvailable,
467
+ isIconCdnEnabled: () => isIconCdnEnabled,
468
+ isIconRegistered: () => isIconRegistered,
469
+ isIconSetSupported: () => isIconSetSupported,
470
+ isTailwindLoaded: () => isTailwindLoaded,
471
+ isValidSpecifier: () => isValidSpecifier,
472
+ navigateTo: () => navigateTo,
473
+ parseVersion: () => parseVersion,
474
+ prefetchComponents: () => prefetchComponents,
475
+ preloadIcons: () => preloadIcons,
476
+ processStyles: () => processStyles,
477
+ registerIcon: () => registerIcon,
478
+ registerIcons: () => registerIcons,
479
+ removeClass: () => removeClass,
480
+ removeCustomStyles: () => removeCustomStyles,
481
+ removeLocalStorage: () => removeLocalStorage,
482
+ removeSessionStorage: () => removeSessionStorage,
483
+ removeTailwind: () => removeTailwind,
484
+ render: () => render,
485
+ renderDynamicList: () => renderDynamicList,
486
+ renderIcon: () => renderIcon,
487
+ renderInShadow: () => renderInShadow,
488
+ renderNode: () => renderNode,
489
+ resetAnalytics: () => resetAnalytics,
490
+ resetEventSystem: () => resetEventSystem,
491
+ resetLongTaskObserver: () => resetLongTaskObserver,
492
+ resetMemorySampler: () => resetMemorySampler,
493
+ resetOverrideSystem: () => resetOverrideSystem,
494
+ resetSessionManager: () => resetSessionManager,
495
+ resolveBindingPath: () => resolveBindingPath,
496
+ resolveTemplate: () => resolveTemplate,
497
+ resolveTemplateValue: () => resolveTemplateValue,
498
+ resolveTemplatesDeep: () => resolveTemplatesDeep,
499
+ resolveVersion: () => resolveVersion,
500
+ runAllTests: () => runAllTests,
501
+ runTestCase: () => runTestCase,
502
+ satisfiesVersion: () => satisfiesVersion,
503
+ setIconCdnEnabled: () => setIconCdnEnabled,
504
+ setInCache: () => setInCache,
505
+ setLocalStorage: () => setLocalStorage,
506
+ setRegistryUrl: () => setRegistryUrl,
507
+ setSessionStorage: () => setSessionStorage,
508
+ setValueByPath: () => setValueByPath,
509
+ toDomEventName: () => toDomEventName,
510
+ toReactEventName: () => toReactEventName,
511
+ toggleClass: () => toggleClass,
512
+ updateStyles: () => updateStyles,
513
+ updateTailwindConfig: () => updateTailwindConfig,
514
+ validateAssertion: () => validateAssertion,
515
+ validateProps: () => validateProps
516
+ });
517
+ module.exports = __toCommonJS(index_exports);
9
518
 
10
- // src/analyticsTypes.ts
519
+ // packages/runtime-core/src/analyticsTypes.ts
11
520
  var DEFAULT_ANALYTICS_CONFIG = {
12
521
  enabled: true,
13
522
  endpoint: "/api/v1/analytics/events",
@@ -25,7 +534,7 @@ var MAX_RETRY_ATTEMPTS = 3;
25
534
  var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
26
535
  var SDK_VERSION = "1.0.0";
27
536
 
28
- // src/sessionManager.ts
537
+ // packages/runtime-core/src/sessionManager.ts
29
538
  var SESSION_STORAGE_KEY = "servly_analytics_session";
30
539
  function generateUUID() {
31
540
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
@@ -87,8 +596,8 @@ function isSessionExpired(session) {
87
596
  return now - session.lastActivityAt > SESSION_TIMEOUT_MS;
88
597
  }
89
598
  var SessionManager = class {
599
+ session = null;
90
600
  constructor() {
91
- this.session = null;
92
601
  this.initialize();
93
602
  }
94
603
  /**
@@ -168,13 +677,15 @@ function resetSessionManager() {
168
677
  sessionManagerInstance = null;
169
678
  }
170
679
 
171
- // src/analytics.ts
680
+ // packages/runtime-core/src/analytics.ts
172
681
  var AnalyticsCollector = class {
682
+ config;
683
+ eventQueue = [];
684
+ flushTimer = null;
685
+ isEnabled;
686
+ isFlushing = false;
687
+ retryDelay = 1e3;
173
688
  constructor(config) {
174
- this.eventQueue = [];
175
- this.flushTimer = null;
176
- this.isFlushing = false;
177
- this.retryDelay = 1e3;
178
689
  this.config = { ...DEFAULT_ANALYTICS_CONFIG, ...config };
179
690
  this.isEnabled = this.config.enabled;
180
691
  this.startFlushTimer();
@@ -517,7 +1028,7 @@ var analytics = {
517
1028
  enable: () => getAnalytics().enable()
518
1029
  };
519
1030
 
520
- // src/bindings.ts
1031
+ // packages/runtime-core/src/bindings.ts
521
1032
  var BINDING_SOURCES = [
522
1033
  "props",
523
1034
  "state",
@@ -526,16 +1037,95 @@ var BINDING_SOURCES = [
526
1037
  "input",
527
1038
  "currentItem",
528
1039
  "localStore",
1040
+ "localStorage",
1041
+ "sessionStorage",
529
1042
  "config",
530
1043
  "element",
531
1044
  "self",
532
1045
  "params",
533
- "query"
1046
+ "query",
1047
+ "window",
1048
+ "bindings",
1049
+ "binding",
1050
+ "boundInputs",
1051
+ "parent"
534
1052
  ];
535
1053
  var TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g;
536
1054
  function hasTemplateSyntax(value) {
537
1055
  return typeof value === "string" && value.includes("{{") && value.includes("}}");
538
1056
  }
1057
+ function getLocalStorageValue(key) {
1058
+ if (typeof localStorage === "undefined") return void 0;
1059
+ try {
1060
+ const stored = localStorage.getItem(key);
1061
+ if (stored === null) return void 0;
1062
+ try {
1063
+ return JSON.parse(stored);
1064
+ } catch {
1065
+ return stored;
1066
+ }
1067
+ } catch {
1068
+ return void 0;
1069
+ }
1070
+ }
1071
+ function getSessionStorageValue(key) {
1072
+ if (typeof sessionStorage === "undefined") return void 0;
1073
+ try {
1074
+ const stored = sessionStorage.getItem(key);
1075
+ if (stored === null) return void 0;
1076
+ try {
1077
+ return JSON.parse(stored);
1078
+ } catch {
1079
+ return stored;
1080
+ }
1081
+ } catch {
1082
+ return void 0;
1083
+ }
1084
+ }
1085
+ function getWindowInfo() {
1086
+ if (typeof window === "undefined") return {};
1087
+ const searchParams = {};
1088
+ try {
1089
+ const urlSearchParams = new URLSearchParams(window.location.search);
1090
+ urlSearchParams.forEach((value, key) => {
1091
+ searchParams[key] = value;
1092
+ });
1093
+ } catch {
1094
+ }
1095
+ return {
1096
+ href: window.location.href,
1097
+ pathname: window.location.pathname,
1098
+ search: window.location.search,
1099
+ hash: window.location.hash,
1100
+ origin: window.location.origin,
1101
+ protocol: window.location.protocol,
1102
+ host: window.location.host,
1103
+ hostname: window.location.hostname,
1104
+ port: window.location.port,
1105
+ searchParams,
1106
+ params: searchParams,
1107
+ innerWidth: window.innerWidth,
1108
+ innerHeight: window.innerHeight,
1109
+ screenWidth: window.screen?.width,
1110
+ screenHeight: window.screen?.height
1111
+ };
1112
+ }
1113
+ function navigatePath(obj, parts) {
1114
+ let current = obj;
1115
+ for (const part of parts) {
1116
+ if (current === null || current === void 0) return void 0;
1117
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1118
+ if (arrayMatch) {
1119
+ const [, propName, indexStr] = arrayMatch;
1120
+ current = current[propName];
1121
+ if (!Array.isArray(current)) return void 0;
1122
+ current = current[parseInt(indexStr, 10)];
1123
+ } else {
1124
+ current = current[part];
1125
+ }
1126
+ }
1127
+ return current;
1128
+ }
539
1129
  function resolveBindingPath(path, context) {
540
1130
  const trimmed = path.trim();
541
1131
  const parts = trimmed.split(".");
@@ -543,17 +1133,70 @@ function resolveBindingPath(path, context) {
543
1133
  return void 0;
544
1134
  }
545
1135
  const prefix = parts[0].toLowerCase();
1136
+ if (prefix === "localstore" || prefix === "localstorage") {
1137
+ if (parts.length === 1) {
1138
+ const all = {};
1139
+ if (typeof localStorage !== "undefined") {
1140
+ for (let i = 0; i < localStorage.length; i++) {
1141
+ const key2 = localStorage.key(i);
1142
+ if (key2) {
1143
+ all[key2] = getLocalStorageValue(key2);
1144
+ }
1145
+ }
1146
+ }
1147
+ return all;
1148
+ }
1149
+ const key = parts[1];
1150
+ const value = getLocalStorageValue(key);
1151
+ if (parts.length === 2) return value;
1152
+ return navigatePath(value, parts.slice(2));
1153
+ }
1154
+ if (prefix === "sessionstorage") {
1155
+ if (parts.length === 1) {
1156
+ const all = {};
1157
+ if (typeof sessionStorage !== "undefined") {
1158
+ for (let i = 0; i < sessionStorage.length; i++) {
1159
+ const key2 = sessionStorage.key(i);
1160
+ if (key2) {
1161
+ all[key2] = getSessionStorageValue(key2);
1162
+ }
1163
+ }
1164
+ }
1165
+ return all;
1166
+ }
1167
+ const key = parts[1];
1168
+ const value = getSessionStorageValue(key);
1169
+ if (parts.length === 2) return value;
1170
+ return navigatePath(value, parts.slice(2));
1171
+ }
1172
+ if (prefix === "window" || prefix === "url") {
1173
+ const windowInfo = getWindowInfo();
1174
+ if (parts.length === 1) return windowInfo;
1175
+ return navigatePath(windowInfo, parts.slice(1));
1176
+ }
1177
+ if (prefix === "params" || prefix === "query") {
1178
+ const windowInfo = getWindowInfo();
1179
+ const params = windowInfo.searchParams || {};
1180
+ if (parts.length === 1) return params;
1181
+ return navigatePath(params, parts.slice(1));
1182
+ }
546
1183
  let source;
547
1184
  let startIndex = 0;
548
- if (prefix === "props" || prefix === "input") {
1185
+ if (prefix === "props" || prefix === "input" || prefix === "bindings" || prefix === "binding" || prefix === "boundinputs" || prefix === "parent") {
549
1186
  source = context.props;
550
1187
  startIndex = 1;
551
1188
  } else if (prefix === "state" || prefix === "appstate") {
552
1189
  source = context.state;
553
1190
  startIndex = 1;
554
- } else if (prefix === "context") {
1191
+ } else if (prefix === "context" || prefix === "config") {
555
1192
  source = context.context;
556
1193
  startIndex = 1;
1194
+ } else if (prefix === "currentitem") {
1195
+ source = context.props;
1196
+ startIndex = 0;
1197
+ } else if (prefix === "self" || prefix === "element") {
1198
+ source = context.state;
1199
+ startIndex = 1;
557
1200
  } else if (BINDING_SOURCES.includes(prefix)) {
558
1201
  source = context.props;
559
1202
  startIndex = 1;
@@ -564,41 +1207,49 @@ function resolveBindingPath(path, context) {
564
1207
  if (!source) {
565
1208
  return void 0;
566
1209
  }
567
- let value = source;
568
- for (let i = startIndex; i < parts.length; i++) {
569
- if (value === null || value === void 0) {
570
- return void 0;
571
- }
572
- value = value[parts[i]];
573
- }
574
- return value;
1210
+ return navigatePath(source, parts.slice(startIndex));
575
1211
  }
576
1212
  function resolveExpression(expression, context) {
577
1213
  const trimmed = expression.trim();
578
- const defaultMatch = trimmed.match(/^(.+?)\|\|(.+)$/);
579
- if (defaultMatch) {
580
- const mainValue = resolveExpression(defaultMatch[1].trim(), context);
581
- if (mainValue !== void 0 && mainValue !== null && mainValue !== "") {
582
- return mainValue;
583
- }
584
- const defaultVal = defaultMatch[2].trim();
585
- if (defaultVal.startsWith('"') && defaultVal.endsWith('"') || defaultVal.startsWith("'") && defaultVal.endsWith("'")) {
586
- return defaultVal.slice(1, -1);
587
- }
588
- if (!isNaN(Number(defaultVal))) {
589
- return Number(defaultVal);
1214
+ const comparisonOperators = ["===", "!==", "==", "!=", ">=", "<=", ">", "<"];
1215
+ for (const op of comparisonOperators) {
1216
+ if (trimmed.includes(op)) {
1217
+ const [left, right] = trimmed.split(op, 2);
1218
+ const leftVal = resolveExpressionValue(left.trim(), context);
1219
+ const rightVal = resolveExpressionValue(right.trim(), context);
1220
+ switch (op) {
1221
+ case "===":
1222
+ return leftVal === rightVal;
1223
+ case "!==":
1224
+ return leftVal !== rightVal;
1225
+ case "==":
1226
+ return leftVal == rightVal;
1227
+ case "!=":
1228
+ return leftVal != rightVal;
1229
+ case ">":
1230
+ return leftVal > rightVal;
1231
+ case "<":
1232
+ return leftVal < rightVal;
1233
+ case ">=":
1234
+ return leftVal >= rightVal;
1235
+ case "<=":
1236
+ return leftVal <= rightVal;
1237
+ }
590
1238
  }
591
- if (defaultVal === "true") return true;
592
- if (defaultVal === "false") return false;
593
- return resolveExpression(defaultVal, context);
594
1239
  }
595
- const ternaryMatch = trimmed.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
596
- if (ternaryMatch) {
597
- const condition = resolveExpression(ternaryMatch[1].trim(), context);
598
- if (condition) {
599
- return resolveTernaryValue(ternaryMatch[2].trim(), context);
1240
+ if (trimmed.startsWith("!") && !trimmed.startsWith("!=")) {
1241
+ const innerValue = resolveExpression(trimmed.slice(1).trim(), context);
1242
+ return !innerValue;
1243
+ }
1244
+ if (trimmed.includes("||")) {
1245
+ const parts = trimmed.split("||");
1246
+ for (let i = 0; i < parts.length; i++) {
1247
+ const part = parts[i].trim();
1248
+ const value = resolveExpressionValue(part, context);
1249
+ if (value || i === parts.length - 1) {
1250
+ return value;
1251
+ }
600
1252
  }
601
- return resolveTernaryValue(ternaryMatch[3].trim(), context);
602
1253
  }
603
1254
  if (trimmed.includes("&&")) {
604
1255
  const parts = trimmed.split("&&");
@@ -608,6 +1259,28 @@ function resolveExpression(expression, context) {
608
1259
  }
609
1260
  return resolveExpression(parts[parts.length - 1].trim(), context);
610
1261
  }
1262
+ const ternaryMatch = trimmed.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
1263
+ if (ternaryMatch) {
1264
+ const condition = resolveExpression(ternaryMatch[1].trim(), context);
1265
+ if (condition) {
1266
+ return resolveTernaryValue(ternaryMatch[2].trim(), context);
1267
+ }
1268
+ return resolveTernaryValue(ternaryMatch[3].trim(), context);
1269
+ }
1270
+ return resolveExpressionValue(trimmed, context);
1271
+ }
1272
+ function resolveExpressionValue(value, context) {
1273
+ const trimmed = value.trim();
1274
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
1275
+ return trimmed.slice(1, -1);
1276
+ }
1277
+ if (!isNaN(Number(trimmed)) && trimmed !== "") {
1278
+ return Number(trimmed);
1279
+ }
1280
+ if (trimmed === "true") return true;
1281
+ if (trimmed === "false") return false;
1282
+ if (trimmed === "null") return null;
1283
+ if (trimmed === "undefined") return void 0;
611
1284
  return resolveBindingPath(trimmed, context);
612
1285
  }
613
1286
  function resolveTernaryValue(value, context) {
@@ -639,7 +1312,7 @@ function resolveTemplate(template, context, componentId) {
639
1312
  }
640
1313
  return String(value);
641
1314
  }
642
- return template.replace(TEMPLATE_REGEX, (match, expression) => {
1315
+ return template.replace(TEMPLATE_REGEX, (_match, expression) => {
643
1316
  const value = resolveExpression(expression, context);
644
1317
  if (value === void 0 || value === null) {
645
1318
  return "";
@@ -739,7 +1412,7 @@ function extractBindingKeys(template) {
739
1412
  return Array.from(keys);
740
1413
  }
741
1414
 
742
- // src/styles.ts
1415
+ // packages/runtime-core/src/styles.ts
743
1416
  var UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
744
1417
  "animationIterationCount",
745
1418
  "borderImageOutset",
@@ -913,8 +1586,9 @@ function updateStyles(element, oldStyles, newStyles) {
913
1586
  }
914
1587
  }
915
1588
 
916
- // src/memorySampler.ts
1589
+ // packages/runtime-core/src/memorySampler.ts
917
1590
  var MemorySampler = class {
1591
+ isSupported;
918
1592
  constructor() {
919
1593
  this.isSupported = this.checkSupport();
920
1594
  }
@@ -974,12 +1648,13 @@ function resetMemorySampler() {
974
1648
  memorySamplerInstance = null;
975
1649
  }
976
1650
 
977
- // src/longTaskObserver.ts
1651
+ // packages/runtime-core/src/longTaskObserver.ts
978
1652
  var LongTaskObserver = class {
1653
+ observer = null;
1654
+ longTaskCount = 0;
1655
+ isSupported;
1656
+ isObserving = false;
979
1657
  constructor() {
980
- this.observer = null;
981
- this.longTaskCount = 0;
982
- this.isObserving = false;
983
1658
  this.isSupported = this.checkSupport();
984
1659
  }
985
1660
  /**
@@ -1021,55 +1696,1418 @@ var LongTaskObserver = class {
1021
1696
  this.isSupported = false;
1022
1697
  }
1023
1698
  }
1024
- /**
1025
- * Stop observing and return the count of long tasks
1026
- */
1027
- stop() {
1028
- if (!this.isObserving || !this.observer) {
1029
- return this.longTaskCount;
1030
- }
1031
- try {
1032
- this.observer.disconnect();
1033
- } catch {
1699
+ /**
1700
+ * Stop observing and return the count of long tasks
1701
+ */
1702
+ stop() {
1703
+ if (!this.isObserving || !this.observer) {
1704
+ return this.longTaskCount;
1705
+ }
1706
+ try {
1707
+ this.observer.disconnect();
1708
+ } catch {
1709
+ }
1710
+ this.observer = null;
1711
+ this.isObserving = false;
1712
+ return this.longTaskCount;
1713
+ }
1714
+ /**
1715
+ * Reset the long task counter
1716
+ */
1717
+ reset() {
1718
+ this.longTaskCount = 0;
1719
+ }
1720
+ /**
1721
+ * Get current count without stopping observation
1722
+ */
1723
+ getCount() {
1724
+ return this.longTaskCount;
1725
+ }
1726
+ /**
1727
+ * Check if currently observing
1728
+ */
1729
+ isActive() {
1730
+ return this.isObserving;
1731
+ }
1732
+ };
1733
+ var longTaskObserverInstance = null;
1734
+ function getLongTaskObserver() {
1735
+ if (!longTaskObserverInstance) {
1736
+ longTaskObserverInstance = new LongTaskObserver();
1737
+ }
1738
+ return longTaskObserverInstance;
1739
+ }
1740
+ function resetLongTaskObserver() {
1741
+ if (longTaskObserverInstance) {
1742
+ longTaskObserverInstance.stop();
1743
+ }
1744
+ longTaskObserverInstance = null;
1745
+ }
1746
+
1747
+ // packages/runtime-core/src/stateManager.ts
1748
+ var StateManager = class {
1749
+ state = {};
1750
+ listeners = /* @__PURE__ */ new Set();
1751
+ config;
1752
+ constructor(config = {}) {
1753
+ this.config = config;
1754
+ this.state = config.initialState || {};
1755
+ if (config.persistToLocalStorage && typeof localStorage !== "undefined") {
1756
+ this.loadFromLocalStorage();
1757
+ }
1758
+ if (config.onStateChange) {
1759
+ this.listeners.add(config.onStateChange);
1760
+ }
1761
+ }
1762
+ /**
1763
+ * Get value by path from state
1764
+ */
1765
+ get(path) {
1766
+ return getValueByPath(this.state, path);
1767
+ }
1768
+ /**
1769
+ * Get the entire state object
1770
+ */
1771
+ getState() {
1772
+ return { ...this.state };
1773
+ }
1774
+ /**
1775
+ * Set a value in state
1776
+ */
1777
+ set(key, value, elementId) {
1778
+ const previousValue = this.get(key);
1779
+ setValueByPath(this.state, key, value);
1780
+ this.notifyChange({
1781
+ key,
1782
+ value,
1783
+ previousValue,
1784
+ operation: "set",
1785
+ elementId,
1786
+ timestamp: Date.now()
1787
+ });
1788
+ }
1789
+ /**
1790
+ * Merge an object into state at the given path
1791
+ */
1792
+ merge(key, value, deep = false, elementId) {
1793
+ const previousValue = this.get(key);
1794
+ const currentValue = previousValue || {};
1795
+ const newValue = deep ? deepMerge(currentValue, value) : { ...currentValue, ...value };
1796
+ setValueByPath(this.state, key, newValue);
1797
+ this.notifyChange({
1798
+ key,
1799
+ value: newValue,
1800
+ previousValue,
1801
+ operation: "merge",
1802
+ elementId,
1803
+ timestamp: Date.now()
1804
+ });
1805
+ }
1806
+ /**
1807
+ * Delete a key from state
1808
+ */
1809
+ delete(key, elementId) {
1810
+ const previousValue = this.get(key);
1811
+ deleteValueByPath(this.state, key);
1812
+ this.notifyChange({
1813
+ key,
1814
+ value: void 0,
1815
+ previousValue,
1816
+ operation: "delete",
1817
+ elementId,
1818
+ timestamp: Date.now()
1819
+ });
1820
+ }
1821
+ /**
1822
+ * Append to an array in state
1823
+ */
1824
+ append(key, value, elementId) {
1825
+ const previousValue = this.get(key);
1826
+ const currentArray = Array.isArray(previousValue) ? [...previousValue] : [];
1827
+ currentArray.push(value);
1828
+ setValueByPath(this.state, key, currentArray);
1829
+ this.notifyChange({
1830
+ key,
1831
+ value: currentArray,
1832
+ previousValue,
1833
+ operation: "append",
1834
+ elementId,
1835
+ timestamp: Date.now()
1836
+ });
1837
+ }
1838
+ /**
1839
+ * Prepend to an array in state
1840
+ */
1841
+ prepend(key, value, elementId) {
1842
+ const previousValue = this.get(key);
1843
+ const currentArray = Array.isArray(previousValue) ? [...previousValue] : [];
1844
+ currentArray.unshift(value);
1845
+ setValueByPath(this.state, key, currentArray);
1846
+ this.notifyChange({
1847
+ key,
1848
+ value: currentArray,
1849
+ previousValue,
1850
+ operation: "prepend",
1851
+ elementId,
1852
+ timestamp: Date.now()
1853
+ });
1854
+ }
1855
+ /**
1856
+ * Toggle a boolean value in state
1857
+ */
1858
+ toggle(key, elementId) {
1859
+ const previousValue = this.get(key);
1860
+ const newValue = !previousValue;
1861
+ setValueByPath(this.state, key, newValue);
1862
+ this.notifyChange({
1863
+ key,
1864
+ value: newValue,
1865
+ previousValue,
1866
+ operation: "toggle",
1867
+ elementId,
1868
+ timestamp: Date.now()
1869
+ });
1870
+ }
1871
+ /**
1872
+ * Subscribe to state changes
1873
+ */
1874
+ subscribe(listener) {
1875
+ this.listeners.add(listener);
1876
+ return () => this.listeners.delete(listener);
1877
+ }
1878
+ /**
1879
+ * Notify all listeners of a state change
1880
+ */
1881
+ notifyChange(event) {
1882
+ if (this.config.persistToLocalStorage) {
1883
+ this.saveToLocalStorage();
1884
+ }
1885
+ for (const listener of this.listeners) {
1886
+ try {
1887
+ listener(event);
1888
+ } catch (error) {
1889
+ console.error("State change listener error:", error);
1890
+ }
1891
+ }
1892
+ }
1893
+ /**
1894
+ * Load state from localStorage
1895
+ */
1896
+ loadFromLocalStorage() {
1897
+ try {
1898
+ const prefix = this.config.localStoragePrefix || "servly_state_";
1899
+ const stored = localStorage.getItem(`${prefix}state`);
1900
+ if (stored) {
1901
+ const parsed = JSON.parse(stored);
1902
+ this.state = { ...this.state, ...parsed };
1903
+ }
1904
+ } catch (error) {
1905
+ console.warn("Failed to load state from localStorage:", error);
1906
+ }
1907
+ }
1908
+ /**
1909
+ * Save state to localStorage
1910
+ */
1911
+ saveToLocalStorage() {
1912
+ try {
1913
+ const prefix = this.config.localStoragePrefix || "servly_state_";
1914
+ localStorage.setItem(`${prefix}state`, JSON.stringify(this.state));
1915
+ } catch (error) {
1916
+ console.warn("Failed to save state to localStorage:", error);
1917
+ }
1918
+ }
1919
+ /**
1920
+ * Clear all state
1921
+ */
1922
+ clear() {
1923
+ const previousState = { ...this.state };
1924
+ this.state = {};
1925
+ if (this.config.persistToLocalStorage) {
1926
+ try {
1927
+ const prefix = this.config.localStoragePrefix || "servly_state_";
1928
+ localStorage.removeItem(`${prefix}state`);
1929
+ } catch (error) {
1930
+ console.warn("Failed to clear localStorage:", error);
1931
+ }
1932
+ }
1933
+ this.notifyChange({
1934
+ key: "",
1935
+ value: {},
1936
+ previousValue: previousState,
1937
+ operation: "delete",
1938
+ timestamp: Date.now()
1939
+ });
1940
+ }
1941
+ };
1942
+ function getValueByPath(obj, path) {
1943
+ if (!obj || !path) return void 0;
1944
+ const parts = path.split(".");
1945
+ let current = obj;
1946
+ for (const part of parts) {
1947
+ if (current === null || current === void 0) return void 0;
1948
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1949
+ if (arrayMatch) {
1950
+ const [, propName, indexStr] = arrayMatch;
1951
+ current = current[propName];
1952
+ if (!Array.isArray(current)) return void 0;
1953
+ current = current[parseInt(indexStr, 10)];
1954
+ } else {
1955
+ current = current[part];
1956
+ }
1957
+ }
1958
+ return current;
1959
+ }
1960
+ function setValueByPath(obj, path, value) {
1961
+ if (!obj || !path) return;
1962
+ const parts = path.split(".");
1963
+ let current = obj;
1964
+ for (let i = 0; i < parts.length - 1; i++) {
1965
+ const part = parts[i];
1966
+ const arrayMatch2 = part.match(/^(\w+)\[(\d+)\]$/);
1967
+ if (arrayMatch2) {
1968
+ const [, propName, indexStr] = arrayMatch2;
1969
+ if (!current[propName]) current[propName] = [];
1970
+ const index = parseInt(indexStr, 10);
1971
+ if (!current[propName][index]) current[propName][index] = {};
1972
+ current = current[propName][index];
1973
+ } else {
1974
+ if (!current[part]) current[part] = {};
1975
+ current = current[part];
1976
+ }
1977
+ }
1978
+ const lastPart = parts[parts.length - 1];
1979
+ const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
1980
+ if (arrayMatch) {
1981
+ const [, propName, indexStr] = arrayMatch;
1982
+ if (!current[propName]) current[propName] = [];
1983
+ current[propName][parseInt(indexStr, 10)] = value;
1984
+ } else {
1985
+ current[lastPart] = value;
1986
+ }
1987
+ }
1988
+ function deleteValueByPath(obj, path) {
1989
+ if (!obj || !path) return;
1990
+ const parts = path.split(".");
1991
+ let current = obj;
1992
+ for (let i = 0; i < parts.length - 1; i++) {
1993
+ const part = parts[i];
1994
+ if (current[part] === void 0) return;
1995
+ current = current[part];
1996
+ }
1997
+ const lastPart = parts[parts.length - 1];
1998
+ delete current[lastPart];
1999
+ }
2000
+ function deepMerge(target, source) {
2001
+ if (!source) return target;
2002
+ if (!target) return source;
2003
+ const result = { ...target };
2004
+ for (const key of Object.keys(source)) {
2005
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
2006
+ result[key] = deepMerge(target[key], source[key]);
2007
+ } else {
2008
+ result[key] = source[key];
2009
+ }
2010
+ }
2011
+ return result;
2012
+ }
2013
+ function addClass(currentClasses, classToAdd) {
2014
+ const classes = currentClasses.split(/\s+/).filter(Boolean);
2015
+ if (!classes.includes(classToAdd)) {
2016
+ classes.push(classToAdd);
2017
+ }
2018
+ return classes.join(" ");
2019
+ }
2020
+ function removeClass(currentClasses, classToRemove) {
2021
+ return currentClasses.split(/\s+/).filter((cls) => cls && cls !== classToRemove).join(" ");
2022
+ }
2023
+ function toggleClass(currentClasses, classToToggle) {
2024
+ const classes = currentClasses.split(/\s+/).filter(Boolean);
2025
+ const index = classes.indexOf(classToToggle);
2026
+ if (index > -1) {
2027
+ classes.splice(index, 1);
2028
+ } else {
2029
+ classes.push(classToToggle);
2030
+ }
2031
+ return classes.join(" ");
2032
+ }
2033
+ function hasClass(currentClasses, classToCheck) {
2034
+ return currentClasses.split(/\s+/).includes(classToCheck);
2035
+ }
2036
+ function getLocalStorage(key, defaultValue) {
2037
+ if (typeof localStorage === "undefined") return defaultValue;
2038
+ try {
2039
+ const stored = localStorage.getItem(key);
2040
+ if (stored === null) return defaultValue;
2041
+ return JSON.parse(stored);
2042
+ } catch {
2043
+ return localStorage.getItem(key) ?? defaultValue;
2044
+ }
2045
+ }
2046
+ function setLocalStorage(key, value) {
2047
+ if (typeof localStorage === "undefined") return;
2048
+ try {
2049
+ localStorage.setItem(key, JSON.stringify(value));
2050
+ } catch (error) {
2051
+ console.warn("Failed to set localStorage:", error);
2052
+ }
2053
+ }
2054
+ function removeLocalStorage(key) {
2055
+ if (typeof localStorage === "undefined") return;
2056
+ localStorage.removeItem(key);
2057
+ }
2058
+ function getSessionStorage(key, defaultValue) {
2059
+ if (typeof sessionStorage === "undefined") return defaultValue;
2060
+ try {
2061
+ const stored = sessionStorage.getItem(key);
2062
+ if (stored === null) return defaultValue;
2063
+ return JSON.parse(stored);
2064
+ } catch {
2065
+ return sessionStorage.getItem(key) ?? defaultValue;
2066
+ }
2067
+ }
2068
+ function setSessionStorage(key, value) {
2069
+ if (typeof sessionStorage === "undefined") return;
2070
+ try {
2071
+ sessionStorage.setItem(key, JSON.stringify(value));
2072
+ } catch (error) {
2073
+ console.warn("Failed to set sessionStorage:", error);
2074
+ }
2075
+ }
2076
+ function removeSessionStorage(key) {
2077
+ if (typeof sessionStorage === "undefined") return;
2078
+ sessionStorage.removeItem(key);
2079
+ }
2080
+ function navigateTo(url, options = {}) {
2081
+ if (typeof window === "undefined") return;
2082
+ const { replace = false, state, newTab = false } = options;
2083
+ if (newTab) {
2084
+ window.open(url, "_blank");
2085
+ return;
2086
+ }
2087
+ if (url.startsWith("http://") || url.startsWith("https://")) {
2088
+ if (replace) {
2089
+ window.location.replace(url);
2090
+ } else {
2091
+ window.location.href = url;
2092
+ }
2093
+ return;
2094
+ }
2095
+ if (url.startsWith("#")) {
2096
+ window.location.hash = url;
2097
+ return;
2098
+ }
2099
+ if (replace) {
2100
+ window.history.replaceState(state, "", url);
2101
+ } else {
2102
+ window.history.pushState(state, "", url);
2103
+ }
2104
+ window.dispatchEvent(new PopStateEvent("popstate", { state }));
2105
+ }
2106
+ function goBack() {
2107
+ if (typeof window === "undefined") return;
2108
+ window.history.back();
2109
+ }
2110
+ function goForward() {
2111
+ if (typeof window === "undefined") return;
2112
+ window.history.forward();
2113
+ }
2114
+ function getUrlInfo() {
2115
+ if (typeof window === "undefined") {
2116
+ return {
2117
+ href: "",
2118
+ pathname: "",
2119
+ search: "",
2120
+ hash: "",
2121
+ searchParams: {}
2122
+ };
2123
+ }
2124
+ const searchParams = {};
2125
+ const urlSearchParams = new URLSearchParams(window.location.search);
2126
+ urlSearchParams.forEach((value, key) => {
2127
+ searchParams[key] = value;
2128
+ });
2129
+ return {
2130
+ href: window.location.href,
2131
+ pathname: window.location.pathname,
2132
+ search: window.location.search,
2133
+ hash: window.location.hash,
2134
+ searchParams
2135
+ };
2136
+ }
2137
+
2138
+ // packages/runtime-core/src/eventSystem.ts
2139
+ var builtInPlugins = {
2140
+ /**
2141
+ * Set state value
2142
+ * Mirrors: state-setState from actions.ts
2143
+ */
2144
+ "state-setState": (action, ctx) => {
2145
+ const { stateConfig } = action.config || {};
2146
+ if (!stateConfig || !ctx.stateManager) return;
2147
+ const config = typeof stateConfig === "string" ? JSON.parse(stateConfig) : stateConfig;
2148
+ const { key, value, operation = "set" } = config;
2149
+ if (!key) return;
2150
+ switch (operation) {
2151
+ case "set":
2152
+ ctx.stateManager.set(key, value, ctx.elementId);
2153
+ break;
2154
+ case "merge":
2155
+ ctx.stateManager.merge(key, value, false, ctx.elementId);
2156
+ break;
2157
+ case "deepMerge":
2158
+ ctx.stateManager.merge(key, value, true, ctx.elementId);
2159
+ break;
2160
+ case "toggle":
2161
+ ctx.stateManager.toggle(key, ctx.elementId);
2162
+ break;
2163
+ case "append":
2164
+ ctx.stateManager.append(key, value, ctx.elementId);
2165
+ break;
2166
+ case "prepend":
2167
+ ctx.stateManager.prepend(key, value, ctx.elementId);
2168
+ break;
2169
+ case "delete":
2170
+ ctx.stateManager.delete(key, ctx.elementId);
2171
+ break;
2172
+ case "increment":
2173
+ const currentVal = ctx.stateManager.get(key) || 0;
2174
+ ctx.stateManager.set(key, currentVal + (value || 1), ctx.elementId);
2175
+ break;
2176
+ case "decrement":
2177
+ const currVal = ctx.stateManager.get(key) || 0;
2178
+ ctx.stateManager.set(key, currVal - (value || 1), ctx.elementId);
2179
+ break;
2180
+ }
2181
+ return { success: true, key, operation };
2182
+ },
2183
+ /**
2184
+ * Navigate to URL
2185
+ * Mirrors: navigateTo from actions.ts
2186
+ */
2187
+ "navigateTo": (action, ctx) => {
2188
+ const { url, replace, newTab, state } = action.config || {};
2189
+ if (!url) return;
2190
+ navigateTo(url, { replace, newTab, state });
2191
+ return { success: true, url };
2192
+ },
2193
+ /**
2194
+ * Set localStorage value
2195
+ */
2196
+ "localStorage-set": (action, ctx) => {
2197
+ const { key, value } = action.config || {};
2198
+ if (!key) return;
2199
+ setLocalStorage(key, value);
2200
+ return { success: true, key };
2201
+ },
2202
+ /**
2203
+ * Get localStorage value
2204
+ */
2205
+ "localStorage-get": (action, ctx) => {
2206
+ const { key, defaultValue } = action.config || {};
2207
+ if (!key) return defaultValue;
2208
+ return getLocalStorage(key, defaultValue);
2209
+ },
2210
+ /**
2211
+ * Remove localStorage value
2212
+ */
2213
+ "localStorage-remove": (action, ctx) => {
2214
+ const { key } = action.config || {};
2215
+ if (!key) return;
2216
+ if (typeof localStorage !== "undefined") {
2217
+ localStorage.removeItem(key);
2218
+ }
2219
+ return { success: true, key };
2220
+ },
2221
+ /**
2222
+ * Set sessionStorage value
2223
+ */
2224
+ "sessionStorage-set": (action, ctx) => {
2225
+ const { key, value } = action.config || {};
2226
+ if (!key) return;
2227
+ setSessionStorage(key, value);
2228
+ return { success: true, key };
2229
+ },
2230
+ /**
2231
+ * Get sessionStorage value
2232
+ */
2233
+ "sessionStorage-get": (action, ctx) => {
2234
+ const { key, defaultValue } = action.config || {};
2235
+ if (!key) return defaultValue;
2236
+ return getSessionStorage(key, defaultValue);
2237
+ },
2238
+ /**
2239
+ * Console log (for debugging)
2240
+ */
2241
+ "console-log": (action, ctx) => {
2242
+ const { message, data } = action.config || {};
2243
+ console.log("[Servly]", message, data);
2244
+ return { success: true };
2245
+ },
2246
+ /**
2247
+ * Show alert
2248
+ */
2249
+ "alert": (action, ctx) => {
2250
+ const { message } = action.config || {};
2251
+ if (typeof alert !== "undefined") {
2252
+ alert(message);
2253
+ }
2254
+ return { success: true };
2255
+ },
2256
+ /**
2257
+ * Copy to clipboard
2258
+ */
2259
+ "clipboard-copy": async (action, ctx) => {
2260
+ const { text } = action.config || {};
2261
+ if (!text || typeof navigator === "undefined") return { success: false };
2262
+ try {
2263
+ await navigator.clipboard.writeText(text);
2264
+ return { success: true };
2265
+ } catch (error) {
2266
+ return { success: false, error };
2267
+ }
2268
+ },
2269
+ /**
2270
+ * Scroll to element
2271
+ */
2272
+ "scrollTo": (action, ctx) => {
2273
+ const { selector, behavior = "smooth", block = "start" } = action.config || {};
2274
+ if (!selector || typeof document === "undefined") return;
2275
+ const element = document.querySelector(selector);
2276
+ if (element) {
2277
+ element.scrollIntoView({ behavior, block });
2278
+ return { success: true };
2279
+ }
2280
+ return { success: false, error: "Element not found" };
2281
+ },
2282
+ /**
2283
+ * Focus element
2284
+ */
2285
+ "focus": (action, ctx) => {
2286
+ const { selector } = action.config || {};
2287
+ if (!selector || typeof document === "undefined") return;
2288
+ const element = document.querySelector(selector);
2289
+ if (element && typeof element.focus === "function") {
2290
+ element.focus();
2291
+ return { success: true };
2292
+ }
2293
+ return { success: false, error: "Element not found" };
2294
+ },
2295
+ /**
2296
+ * Blur element
2297
+ */
2298
+ "blur": (action, ctx) => {
2299
+ const { selector } = action.config || {};
2300
+ if (!selector || typeof document === "undefined") return;
2301
+ const element = document.querySelector(selector);
2302
+ if (element && typeof element.blur === "function") {
2303
+ element.blur();
2304
+ return { success: true };
2305
+ }
2306
+ return { success: false, error: "Element not found" };
2307
+ },
2308
+ /**
2309
+ * Add class to element
2310
+ */
2311
+ "addClass": (action, ctx) => {
2312
+ const { selector, className } = action.config || {};
2313
+ if (!selector || !className || typeof document === "undefined") return;
2314
+ const element = document.querySelector(selector);
2315
+ if (element) {
2316
+ element.classList.add(className);
2317
+ return { success: true };
2318
+ }
2319
+ return { success: false, error: "Element not found" };
2320
+ },
2321
+ /**
2322
+ * Remove class from element
2323
+ */
2324
+ "removeClass": (action, ctx) => {
2325
+ const { selector, className } = action.config || {};
2326
+ if (!selector || !className || typeof document === "undefined") return;
2327
+ const element = document.querySelector(selector);
2328
+ if (element) {
2329
+ element.classList.remove(className);
2330
+ return { success: true };
2331
+ }
2332
+ return { success: false, error: "Element not found" };
2333
+ },
2334
+ /**
2335
+ * Toggle class on element
2336
+ */
2337
+ "toggleClass": (action, ctx) => {
2338
+ const { selector, className } = action.config || {};
2339
+ if (!selector || !className || typeof document === "undefined") return;
2340
+ const element = document.querySelector(selector);
2341
+ if (element) {
2342
+ element.classList.toggle(className);
2343
+ return { success: true };
2344
+ }
2345
+ return { success: false, error: "Element not found" };
2346
+ },
2347
+ /**
2348
+ * Set element attribute
2349
+ */
2350
+ "setAttribute": (action, ctx) => {
2351
+ const { selector, attribute, value } = action.config || {};
2352
+ if (!selector || !attribute || typeof document === "undefined") return;
2353
+ const element = document.querySelector(selector);
2354
+ if (element) {
2355
+ element.setAttribute(attribute, value);
2356
+ return { success: true };
2357
+ }
2358
+ return { success: false, error: "Element not found" };
2359
+ },
2360
+ /**
2361
+ * Remove element attribute
2362
+ */
2363
+ "removeAttribute": (action, ctx) => {
2364
+ const { selector, attribute } = action.config || {};
2365
+ if (!selector || !attribute || typeof document === "undefined") return;
2366
+ const element = document.querySelector(selector);
2367
+ if (element) {
2368
+ element.removeAttribute(attribute);
2369
+ return { success: true };
2370
+ }
2371
+ return { success: false, error: "Element not found" };
2372
+ },
2373
+ /**
2374
+ * Dispatch custom event
2375
+ */
2376
+ "dispatchEvent": (action, ctx) => {
2377
+ const { selector, eventName, detail } = action.config || {};
2378
+ if (!eventName || typeof document === "undefined") return;
2379
+ const target = selector ? document.querySelector(selector) : document;
2380
+ if (target) {
2381
+ const event = new CustomEvent(eventName, { detail, bubbles: true });
2382
+ target.dispatchEvent(event);
2383
+ return { success: true };
2384
+ }
2385
+ return { success: false, error: "Target not found" };
2386
+ },
2387
+ /**
2388
+ * Delay execution
2389
+ */
2390
+ "delay": async (action, ctx) => {
2391
+ const { ms = 0 } = action.config || {};
2392
+ await new Promise((resolve) => setTimeout(resolve, ms));
2393
+ return { success: true };
2394
+ },
2395
+ /**
2396
+ * Conditional execution
2397
+ */
2398
+ "condition": (action, ctx) => {
2399
+ const { condition, thenActions, elseActions } = action.config || {};
2400
+ return { condition, thenActions, elseActions };
2401
+ }
2402
+ };
2403
+ var EventSystem = class {
2404
+ config;
2405
+ pluginExecutors;
2406
+ debounceTimers = /* @__PURE__ */ new Map();
2407
+ throttleTimers = /* @__PURE__ */ new Map();
2408
+ constructor(config = {}) {
2409
+ this.config = config;
2410
+ this.pluginExecutors = {
2411
+ ...builtInPlugins,
2412
+ ...config.pluginExecutors
2413
+ };
2414
+ }
2415
+ /**
2416
+ * Register a custom plugin executor
2417
+ */
2418
+ registerPlugin(key, executor) {
2419
+ this.pluginExecutors[key] = executor;
2420
+ }
2421
+ /**
2422
+ * Create an event handler from Servly plugin format
2423
+ */
2424
+ createHandler(elementId, handlerConfig, context, options = {}) {
2425
+ return (event) => {
2426
+ if (handlerConfig.preventDefault || options.preventDefault) {
2427
+ event.preventDefault();
2428
+ }
2429
+ if (handlerConfig.stopPropagation || options.stopPropagation) {
2430
+ event.stopPropagation();
2431
+ }
2432
+ if (this.config.onEvent) {
2433
+ this.config.onEvent(event.type, elementId, event);
2434
+ }
2435
+ if (handlerConfig.plugins && handlerConfig.plugins.length > 0) {
2436
+ this.executePlugins(handlerConfig.plugins, {
2437
+ event,
2438
+ elementId,
2439
+ context,
2440
+ stateManager: this.config.stateManager
2441
+ });
2442
+ }
2443
+ };
2444
+ }
2445
+ /**
2446
+ * Execute a sequence of plugin actions
2447
+ */
2448
+ async executePlugins(plugins, eventContext) {
2449
+ const results = [];
2450
+ for (const action of plugins) {
2451
+ try {
2452
+ const executor = this.pluginExecutors[action.key];
2453
+ if (executor) {
2454
+ const result = await executor(action, eventContext);
2455
+ results.push(result);
2456
+ if (this.config.onPluginExecute) {
2457
+ this.config.onPluginExecute(action, result);
2458
+ }
2459
+ } else {
2460
+ console.warn(`[EventSystem] Unknown plugin: ${action.key}`);
2461
+ results.push({ error: `Unknown plugin: ${action.key}` });
2462
+ }
2463
+ } catch (error) {
2464
+ console.error(`[EventSystem] Plugin error (${action.key}):`, error);
2465
+ results.push({ error });
2466
+ }
2467
+ }
2468
+ return results;
2469
+ }
2470
+ /**
2471
+ * Create a debounced event handler
2472
+ */
2473
+ createDebouncedHandler(elementId, handler, delay) {
2474
+ const key = `${elementId}-debounce`;
2475
+ return (event) => {
2476
+ const existingTimer = this.debounceTimers.get(key);
2477
+ if (existingTimer) {
2478
+ clearTimeout(existingTimer);
2479
+ }
2480
+ const timer = setTimeout(() => {
2481
+ handler(event);
2482
+ this.debounceTimers.delete(key);
2483
+ }, delay);
2484
+ this.debounceTimers.set(key, timer);
2485
+ };
2486
+ }
2487
+ /**
2488
+ * Create a throttled event handler
2489
+ */
2490
+ createThrottledHandler(elementId, handler, delay) {
2491
+ const key = `${elementId}-throttle`;
2492
+ return (event) => {
2493
+ if (this.throttleTimers.get(key)) {
2494
+ return;
2495
+ }
2496
+ handler(event);
2497
+ this.throttleTimers.set(key, true);
2498
+ setTimeout(() => {
2499
+ this.throttleTimers.delete(key);
2500
+ }, delay);
2501
+ };
2502
+ }
2503
+ /**
2504
+ * Clean up all timers
2505
+ */
2506
+ destroy() {
2507
+ for (const timer of this.debounceTimers.values()) {
2508
+ clearTimeout(timer);
2509
+ }
2510
+ this.debounceTimers.clear();
2511
+ this.throttleTimers.clear();
2512
+ }
2513
+ };
2514
+ var EVENT_HANDLERS = {
2515
+ onClick: "click",
2516
+ onDoubleClick: "dblclick",
2517
+ onMouseDown: "mousedown",
2518
+ onMouseUp: "mouseup",
2519
+ onMouseEnter: "mouseenter",
2520
+ onMouseLeave: "mouseleave",
2521
+ onMouseMove: "mousemove",
2522
+ onMouseOver: "mouseover",
2523
+ onMouseOut: "mouseout",
2524
+ onKeyDown: "keydown",
2525
+ onKeyUp: "keyup",
2526
+ onKeyPress: "keypress",
2527
+ onFocus: "focus",
2528
+ onBlur: "blur",
2529
+ onChange: "change",
2530
+ onInput: "input",
2531
+ onSubmit: "submit",
2532
+ onReset: "reset",
2533
+ onScroll: "scroll",
2534
+ onWheel: "wheel",
2535
+ onDragStart: "dragstart",
2536
+ onDrag: "drag",
2537
+ onDragEnd: "dragend",
2538
+ onDragEnter: "dragenter",
2539
+ onDragLeave: "dragleave",
2540
+ onDragOver: "dragover",
2541
+ onDrop: "drop",
2542
+ onTouchStart: "touchstart",
2543
+ onTouchMove: "touchmove",
2544
+ onTouchEnd: "touchend",
2545
+ onTouchCancel: "touchcancel",
2546
+ onContextMenu: "contextmenu",
2547
+ onCopy: "copy",
2548
+ onCut: "cut",
2549
+ onPaste: "paste",
2550
+ onLoad: "load",
2551
+ onError: "error",
2552
+ onAnimationStart: "animationstart",
2553
+ onAnimationEnd: "animationend",
2554
+ onAnimationIteration: "animationiteration",
2555
+ onTransitionEnd: "transitionend"
2556
+ };
2557
+ function toDomEventName(reactEventName) {
2558
+ return EVENT_HANDLERS[reactEventName] || reactEventName.replace(/^on/, "").toLowerCase();
2559
+ }
2560
+ function toReactEventName(domEventName) {
2561
+ const entry = Object.entries(EVENT_HANDLERS).find(([, dom]) => dom === domEventName);
2562
+ return entry ? entry[0] : `on${domEventName.charAt(0).toUpperCase()}${domEventName.slice(1)}`;
2563
+ }
2564
+ var defaultEventSystem = null;
2565
+ function getEventSystem(config) {
2566
+ if (!defaultEventSystem || config) {
2567
+ defaultEventSystem = new EventSystem(config);
2568
+ }
2569
+ return defaultEventSystem;
2570
+ }
2571
+ function resetEventSystem() {
2572
+ if (defaultEventSystem) {
2573
+ defaultEventSystem.destroy();
2574
+ defaultEventSystem = null;
2575
+ }
2576
+ }
2577
+
2578
+ // packages/runtime-core/src/overrides.ts
2579
+ var OverrideSystem = class {
2580
+ config;
2581
+ elementStates = /* @__PURE__ */ new Map();
2582
+ watchIntervals = /* @__PURE__ */ new Map();
2583
+ constructor(config = {}) {
2584
+ this.config = config;
2585
+ }
2586
+ /**
2587
+ * Get overrides from an element
2588
+ */
2589
+ getOverrides(element) {
2590
+ const rawOverrides = element.configuration?._overrides_;
2591
+ if (!rawOverrides) return [];
2592
+ if (Array.isArray(rawOverrides)) {
2593
+ return rawOverrides.filter((o) => o && typeof o === "object");
2594
+ }
2595
+ if (typeof rawOverrides === "object") {
2596
+ console.warn("[OverrideSystem] Legacy object override format detected, ignoring");
2597
+ return [];
2598
+ }
2599
+ return [];
2600
+ }
2601
+ /**
2602
+ * Initialize overrides for an element (called on mount)
2603
+ */
2604
+ async initializeElement(element, context) {
2605
+ const elementId = element.i;
2606
+ const overrides = this.getOverrides(element);
2607
+ if (overrides.length === 0) return;
2608
+ const state = {
2609
+ previousValues: /* @__PURE__ */ new Map(),
2610
+ initialized: false,
2611
+ abortController: new AbortController()
2612
+ };
2613
+ this.elementStates.set(elementId, state);
2614
+ const mountOverrides = overrides.filter((o) => !o.isCleanUp);
2615
+ overrides.forEach((override, index) => {
2616
+ if (override.isCleanUp) return;
2617
+ if (!override.dependencies || override.dependencies.length === 0) return;
2618
+ const initialValues = override.dependencies.map(
2619
+ (dep) => resolveTemplate(dep, context)
2620
+ );
2621
+ state.previousValues.set(index, initialValues);
2622
+ });
2623
+ await this.processOverrides(elementId, mountOverrides, context, "onMount");
2624
+ state.initialized = true;
2625
+ }
2626
+ /**
2627
+ * Check and process dependency changes for an element
2628
+ */
2629
+ async checkDependencies(element, context) {
2630
+ const elementId = element.i;
2631
+ const overrides = this.getOverrides(element);
2632
+ const state = this.elementStates.get(elementId);
2633
+ if (!state || !state.initialized) return;
2634
+ const overridesToTrigger = [];
2635
+ overrides.forEach((override, index) => {
2636
+ if (override.isCleanUp) return;
2637
+ if (!override.dependencies || override.dependencies.length === 0) return;
2638
+ const currentValues = override.dependencies.map(
2639
+ (dep) => resolveTemplate(dep, context)
2640
+ );
2641
+ const previousValues = state.previousValues.get(index) || [];
2642
+ let hasChanged = false;
2643
+ if (previousValues.length !== currentValues.length) {
2644
+ hasChanged = true;
2645
+ } else {
2646
+ for (let i = 0; i < currentValues.length; i++) {
2647
+ if (JSON.stringify(previousValues[i]) !== JSON.stringify(currentValues[i])) {
2648
+ hasChanged = true;
2649
+ break;
2650
+ }
2651
+ }
2652
+ }
2653
+ if (hasChanged) {
2654
+ overridesToTrigger.push(override);
2655
+ state.previousValues.set(index, currentValues);
2656
+ if (this.config.onDependencyChange) {
2657
+ this.config.onDependencyChange(elementId, override.dependencies, currentValues);
2658
+ }
2659
+ }
2660
+ });
2661
+ if (overridesToTrigger.length > 0) {
2662
+ await this.processOverrides(elementId, overridesToTrigger, context, "onDependencyChange");
2663
+ }
2664
+ }
2665
+ /**
2666
+ * Cleanup overrides for an element (called on unmount)
2667
+ */
2668
+ async cleanupElement(element, context) {
2669
+ const elementId = element.i;
2670
+ const overrides = this.getOverrides(element);
2671
+ const state = this.elementStates.get(elementId);
2672
+ if (state?.abortController) {
2673
+ state.abortController.abort();
2674
+ }
2675
+ this.stopWatching(elementId);
2676
+ const cleanupOverrides = overrides.filter((o) => o.isCleanUp);
2677
+ if (cleanupOverrides.length > 0) {
2678
+ await this.processOverrides(elementId, cleanupOverrides, context, "onUnmount");
2679
+ }
2680
+ this.elementStates.delete(elementId);
2681
+ }
2682
+ /**
2683
+ * Process a list of overrides
2684
+ */
2685
+ async processOverrides(elementId, overrides, context, eventType) {
2686
+ if (!overrides || overrides.length === 0) return;
2687
+ const validOverrides = overrides.filter((o) => o.plugins && o.plugins.length > 0);
2688
+ if (validOverrides.length === 0) return;
2689
+ const results = await Promise.allSettled(
2690
+ validOverrides.map(async (override) => {
2691
+ if (this.config.onOverrideTrigger) {
2692
+ this.config.onOverrideTrigger(elementId, override, eventType);
2693
+ }
2694
+ if (this.config.eventSystem && override.plugins) {
2695
+ const eventContext = {
2696
+ event: new CustomEvent(eventType),
2697
+ elementId,
2698
+ context,
2699
+ stateManager: this.config.stateManager
2700
+ };
2701
+ return this.config.eventSystem.executePlugins(override.plugins, eventContext);
2702
+ }
2703
+ return null;
2704
+ })
2705
+ );
2706
+ results.forEach((result, index) => {
2707
+ if (result.status === "rejected") {
2708
+ console.error(`[OverrideSystem] Override ${index} failed:`, result.reason);
2709
+ }
2710
+ });
2711
+ }
2712
+ /**
2713
+ * Start watching an element for dependency changes
2714
+ */
2715
+ startWatching(element, context, intervalMs = 100) {
2716
+ const elementId = element.i;
2717
+ this.stopWatching(elementId);
2718
+ const overrides = this.getOverrides(element);
2719
+ const hasDependencies = overrides.some(
2720
+ (o) => !o.isCleanUp && o.dependencies && o.dependencies.length > 0
2721
+ );
2722
+ if (!hasDependencies) return;
2723
+ const interval = setInterval(() => {
2724
+ this.checkDependencies(element, context);
2725
+ }, intervalMs);
2726
+ this.watchIntervals.set(elementId, interval);
2727
+ }
2728
+ /**
2729
+ * Stop watching an element
2730
+ */
2731
+ stopWatching(elementId) {
2732
+ const interval = this.watchIntervals.get(elementId);
2733
+ if (interval) {
2734
+ clearInterval(interval);
2735
+ this.watchIntervals.delete(elementId);
2736
+ }
2737
+ }
2738
+ /**
2739
+ * Destroy the override system
2740
+ */
2741
+ destroy() {
2742
+ for (const interval of this.watchIntervals.values()) {
2743
+ clearInterval(interval);
2744
+ }
2745
+ this.watchIntervals.clear();
2746
+ for (const state of this.elementStates.values()) {
2747
+ if (state.abortController) {
2748
+ state.abortController.abort();
2749
+ }
2750
+ }
2751
+ this.elementStates.clear();
2752
+ }
2753
+ };
2754
+ function extractOverrideDependencies(element) {
2755
+ const overrides = element.configuration?._overrides_;
2756
+ if (!Array.isArray(overrides)) return [];
2757
+ const dependencies = [];
2758
+ for (const override of overrides) {
2759
+ if (override?.dependencies && Array.isArray(override.dependencies)) {
2760
+ dependencies.push(...override.dependencies);
2761
+ }
2762
+ }
2763
+ return [...new Set(dependencies)];
2764
+ }
2765
+ function hasOverrides(element) {
2766
+ const overrides = element.configuration?._overrides_;
2767
+ return Array.isArray(overrides) && overrides.length > 0;
2768
+ }
2769
+ function hasDependencyOverrides(element) {
2770
+ const overrides = element.configuration?._overrides_;
2771
+ if (!Array.isArray(overrides)) return false;
2772
+ return overrides.some(
2773
+ (o) => !o?.isCleanUp && o?.dependencies && o.dependencies.length > 0
2774
+ );
2775
+ }
2776
+ function getMountOverrides(element) {
2777
+ const overrides = element.configuration?._overrides_;
2778
+ if (!Array.isArray(overrides)) return [];
2779
+ return overrides.filter((o) => o && !o.isCleanUp);
2780
+ }
2781
+ function getCleanupOverrides(element) {
2782
+ const overrides = element.configuration?._overrides_;
2783
+ if (!Array.isArray(overrides)) return [];
2784
+ return overrides.filter((o) => o && o.isCleanUp);
2785
+ }
2786
+ var defaultOverrideSystem = null;
2787
+ function getOverrideSystem(config) {
2788
+ if (!defaultOverrideSystem || config) {
2789
+ defaultOverrideSystem = new OverrideSystem(config);
2790
+ }
2791
+ return defaultOverrideSystem;
2792
+ }
2793
+ function resetOverrideSystem() {
2794
+ if (defaultOverrideSystem) {
2795
+ defaultOverrideSystem.destroy();
2796
+ defaultOverrideSystem = null;
2797
+ }
2798
+ }
2799
+
2800
+ // packages/runtime-core/src/icons.ts
2801
+ var cdnEnabled = false;
2802
+ function setIconCdnEnabled(enabled) {
2803
+ cdnEnabled = enabled;
2804
+ }
2805
+ function isIconCdnEnabled() {
2806
+ return cdnEnabled;
2807
+ }
2808
+ var ICONIFY_COLLECTIONS = {
2809
+ Ai: "ant-design",
2810
+ Bi: "bi",
2811
+ Bs: "bi",
2812
+ Bx: "bx",
2813
+ Ci: "circum",
2814
+ Cg: "gg",
2815
+ Di: "devicon",
2816
+ Fi: "feather",
2817
+ Fc: "flat-color-icons",
2818
+ Fa: "fa-solid",
2819
+ Fa6: "fa6-solid",
2820
+ Gi: "game-icons",
2821
+ Go: "octicon",
2822
+ Gr: "grommet-icons",
2823
+ Hi: "heroicons-outline",
2824
+ Hi2: "heroicons",
2825
+ Im: "icomoon-free",
2826
+ Io: "ion",
2827
+ Io5: "ion",
2828
+ Lu: "lucide",
2829
+ Md: "ic",
2830
+ Pi: "ph",
2831
+ Ri: "ri",
2832
+ Rx: "radix-icons",
2833
+ Si: "simple-icons",
2834
+ Sl: "simple-line-icons",
2835
+ Tb: "tabler",
2836
+ Tfi: "themify",
2837
+ Vsc: "codicon",
2838
+ Wi: "wi"
2839
+ };
2840
+ var ICON_SET_STYLES = {
2841
+ Fi: { stroke: true },
2842
+ Lu: { stroke: true },
2843
+ Hi: { stroke: true },
2844
+ Hi2: { stroke: true },
2845
+ Tb: { stroke: true }
2846
+ };
2847
+ var iconCache = /* @__PURE__ */ new Map();
2848
+ var pendingFetches = /* @__PURE__ */ new Map();
2849
+ var registeredIcons = /* @__PURE__ */ new Map();
2850
+ function registerIcon(set, name, data) {
2851
+ const key = `${set}:${name}`;
2852
+ registeredIcons.set(key, data);
2853
+ iconCache.set(key, data);
2854
+ }
2855
+ function registerIcons(icons) {
2856
+ for (const [set, setIcons] of Object.entries(icons)) {
2857
+ for (const [name, data] of Object.entries(setIcons)) {
2858
+ registerIcon(set, name, data);
2859
+ }
2860
+ }
2861
+ }
2862
+ function isIconRegistered(icon) {
2863
+ const key = `${icon.set}:${icon.name}`;
2864
+ return registeredIcons.has(key);
2865
+ }
2866
+ function getRegisteredIconKeys() {
2867
+ return Array.from(registeredIcons.keys());
2868
+ }
2869
+ function toKebabCase(str) {
2870
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
2871
+ }
2872
+ function getIconifyName(name, set) {
2873
+ const prefixLength = set.length;
2874
+ let baseName = name;
2875
+ if (name.startsWith(set)) {
2876
+ baseName = name.slice(prefixLength);
2877
+ }
2878
+ let kebabName = toKebabCase(baseName);
2879
+ switch (set) {
2880
+ case "Md":
2881
+ if (baseName.startsWith("Outline")) {
2882
+ kebabName = toKebabCase(baseName.slice(7)) + "-outline";
2883
+ }
2884
+ kebabName = "baseline-" + kebabName;
2885
+ break;
2886
+ case "Hi2":
2887
+ if (baseName.startsWith("Outline")) {
2888
+ kebabName = toKebabCase(baseName.slice(7));
2889
+ } else if (baseName.startsWith("Solid")) {
2890
+ kebabName = toKebabCase(baseName.slice(5)) + "-solid";
2891
+ }
2892
+ break;
2893
+ case "Io":
2894
+ case "Io5":
2895
+ if (baseName.startsWith("Ios")) {
2896
+ kebabName = toKebabCase(baseName.slice(3));
2897
+ } else if (baseName.startsWith("Md")) {
2898
+ kebabName = toKebabCase(baseName.slice(2));
2899
+ }
2900
+ break;
2901
+ case "Pi":
2902
+ if (baseName.endsWith("Bold")) {
2903
+ kebabName = toKebabCase(baseName.slice(0, -4)) + "-bold";
2904
+ } else if (baseName.endsWith("Fill")) {
2905
+ kebabName = toKebabCase(baseName.slice(0, -4)) + "-fill";
2906
+ } else if (baseName.endsWith("Light")) {
2907
+ kebabName = toKebabCase(baseName.slice(0, -5)) + "-light";
2908
+ } else if (baseName.endsWith("Thin")) {
2909
+ kebabName = toKebabCase(baseName.slice(0, -4)) + "-thin";
2910
+ } else if (baseName.endsWith("Duotone")) {
2911
+ kebabName = toKebabCase(baseName.slice(0, -7)) + "-duotone";
2912
+ }
2913
+ break;
2914
+ }
2915
+ return kebabName;
2916
+ }
2917
+ async function fetchIconFromIconify(set, name) {
2918
+ if (!cdnEnabled) {
2919
+ return null;
2920
+ }
2921
+ const collection = ICONIFY_COLLECTIONS[set];
2922
+ if (!collection) {
2923
+ return null;
2924
+ }
2925
+ const iconName = getIconifyName(name, set);
2926
+ try {
2927
+ const url = `https://api.iconify.design/${collection}/${iconName}.svg`;
2928
+ const response = await fetch(url);
2929
+ if (!response.ok) {
2930
+ const altNames = [
2931
+ iconName.replace(/-outline$/, ""),
2932
+ iconName.replace(/-solid$/, ""),
2933
+ iconName.replace(/-fill$/, ""),
2934
+ iconName.replace(/^baseline-/, "")
2935
+ ].filter((alt) => alt !== iconName);
2936
+ for (const altName of altNames) {
2937
+ const altUrl = `https://api.iconify.design/${collection}/${altName}.svg`;
2938
+ const altResponse = await fetch(altUrl);
2939
+ if (altResponse.ok) {
2940
+ return parseSvgResponse(await altResponse.text());
2941
+ }
2942
+ }
2943
+ return null;
2944
+ }
2945
+ return parseSvgResponse(await response.text());
2946
+ } catch {
2947
+ return null;
2948
+ }
2949
+ }
2950
+ function parseSvgResponse(svgText) {
2951
+ try {
2952
+ const parser = new DOMParser();
2953
+ const doc = parser.parseFromString(svgText, "image/svg+xml");
2954
+ const svg = doc.querySelector("svg");
2955
+ if (!svg) {
2956
+ return null;
1034
2957
  }
1035
- this.observer = null;
1036
- this.isObserving = false;
1037
- return this.longTaskCount;
2958
+ const viewBox = svg.getAttribute("viewBox");
2959
+ const width = parseInt(svg.getAttribute("width") || "24", 10);
2960
+ const height = parseInt(svg.getAttribute("height") || "24", 10);
2961
+ const body = svg.innerHTML;
2962
+ return {
2963
+ body,
2964
+ width,
2965
+ height,
2966
+ viewBox: viewBox || `0 0 ${width} ${height}`
2967
+ };
2968
+ } catch {
2969
+ return null;
1038
2970
  }
1039
- /**
1040
- * Reset the long task counter
1041
- */
1042
- reset() {
1043
- this.longTaskCount = 0;
2971
+ }
2972
+ async function getIconData(icon) {
2973
+ const key = `${icon.set}:${icon.name}`;
2974
+ if (iconCache.has(key)) {
2975
+ return iconCache.get(key);
1044
2976
  }
1045
- /**
1046
- * Get current count without stopping observation
1047
- */
1048
- getCount() {
1049
- return this.longTaskCount;
2977
+ if (pendingFetches.has(key)) {
2978
+ return pendingFetches.get(key);
1050
2979
  }
1051
- /**
1052
- * Check if currently observing
1053
- */
1054
- isActive() {
1055
- return this.isObserving;
2980
+ if (!cdnEnabled) {
2981
+ return null;
1056
2982
  }
1057
- };
1058
- var longTaskObserverInstance = null;
1059
- function getLongTaskObserver() {
1060
- if (!longTaskObserverInstance) {
1061
- longTaskObserverInstance = new LongTaskObserver();
2983
+ const fetchPromise = fetchIconFromIconify(icon.set, icon.name);
2984
+ pendingFetches.set(key, fetchPromise);
2985
+ const data = await fetchPromise;
2986
+ pendingFetches.delete(key);
2987
+ if (data) {
2988
+ iconCache.set(key, data);
1062
2989
  }
1063
- return longTaskObserverInstance;
2990
+ return data;
1064
2991
  }
1065
- function resetLongTaskObserver() {
1066
- if (longTaskObserverInstance) {
1067
- longTaskObserverInstance.stop();
2992
+ function getIconDataSync(icon) {
2993
+ const key = `${icon.set}:${icon.name}`;
2994
+ return iconCache.get(key) || null;
2995
+ }
2996
+ function createIconSVG(icon, data, size = 24, color = "currentColor") {
2997
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
2998
+ svg.setAttribute("width", String(size));
2999
+ svg.setAttribute("height", String(size));
3000
+ svg.setAttribute("viewBox", data.viewBox || "0 0 24 24");
3001
+ svg.setAttribute("data-icon-name", icon.name);
3002
+ svg.setAttribute("data-icon-set", icon.set);
3003
+ svg.setAttribute("class", `servly-icon servly-icon-${icon.set.toLowerCase()}`);
3004
+ const style = ICON_SET_STYLES[icon.set];
3005
+ if (style?.stroke) {
3006
+ svg.setAttribute("fill", "none");
3007
+ svg.setAttribute("stroke", color);
3008
+ svg.setAttribute("stroke-width", "2");
3009
+ svg.setAttribute("stroke-linecap", "round");
3010
+ svg.setAttribute("stroke-linejoin", "round");
3011
+ } else {
3012
+ svg.setAttribute("fill", color);
3013
+ }
3014
+ svg.innerHTML = data.body;
3015
+ const paths = svg.querySelectorAll("path, circle, rect, polygon, line, polyline");
3016
+ paths.forEach((el) => {
3017
+ if (!el.getAttribute("fill") || el.getAttribute("fill") === "currentColor") {
3018
+ if (style?.stroke) {
3019
+ el.setAttribute("stroke", color);
3020
+ } else {
3021
+ el.setAttribute("fill", color);
3022
+ }
3023
+ }
3024
+ });
3025
+ return svg;
3026
+ }
3027
+ function createPlaceholderIcon(icon, size = 24, color = "currentColor") {
3028
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3029
+ svg.setAttribute("width", String(size));
3030
+ svg.setAttribute("height", String(size));
3031
+ svg.setAttribute("viewBox", "0 0 24 24");
3032
+ svg.setAttribute("fill", "none");
3033
+ svg.setAttribute("stroke", color);
3034
+ svg.setAttribute("stroke-width", "2");
3035
+ svg.setAttribute("stroke-linecap", "round");
3036
+ svg.setAttribute("stroke-linejoin", "round");
3037
+ svg.setAttribute("data-icon-name", icon.name);
3038
+ svg.setAttribute("data-icon-set", icon.set);
3039
+ svg.setAttribute("data-icon-missing", "true");
3040
+ svg.setAttribute("class", "servly-icon servly-icon-placeholder");
3041
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
3042
+ circle.setAttribute("cx", "12");
3043
+ circle.setAttribute("cy", "12");
3044
+ circle.setAttribute("r", "10");
3045
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
3046
+ path.setAttribute("d", "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3");
3047
+ const dot = document.createElementNS("http://www.w3.org/2000/svg", "line");
3048
+ dot.setAttribute("x1", "12");
3049
+ dot.setAttribute("y1", "17");
3050
+ dot.setAttribute("x2", "12.01");
3051
+ dot.setAttribute("y2", "17");
3052
+ svg.appendChild(circle);
3053
+ svg.appendChild(path);
3054
+ svg.appendChild(dot);
3055
+ return svg;
3056
+ }
3057
+ function renderIcon(container, icon, size = 24, color = "currentColor") {
3058
+ const cachedData = getIconDataSync(icon);
3059
+ if (cachedData) {
3060
+ const svg = createIconSVG(icon, cachedData, size, color);
3061
+ container.innerHTML = "";
3062
+ container.appendChild(svg);
3063
+ return;
1068
3064
  }
1069
- longTaskObserverInstance = null;
3065
+ if (!cdnEnabled) {
3066
+ const placeholder2 = createPlaceholderIcon(icon, size, color);
3067
+ container.innerHTML = "";
3068
+ container.appendChild(placeholder2);
3069
+ console.warn(
3070
+ `[Icons] Icon not bundled: ${icon.set}:${icon.name}. Use registerIcon() to bundle it, or enable CDN with setIconCdnEnabled(true).`
3071
+ );
3072
+ return;
3073
+ }
3074
+ const placeholder = createPlaceholderIcon(icon, size, color);
3075
+ placeholder.setAttribute("data-icon-loading", "true");
3076
+ placeholder.removeAttribute("data-icon-missing");
3077
+ container.innerHTML = "";
3078
+ container.appendChild(placeholder);
3079
+ getIconData(icon).then((data) => {
3080
+ if (data) {
3081
+ const svg = createIconSVG(icon, data, size, color);
3082
+ container.innerHTML = "";
3083
+ container.appendChild(svg);
3084
+ } else {
3085
+ placeholder.setAttribute("data-icon-missing", "true");
3086
+ placeholder.removeAttribute("data-icon-loading");
3087
+ }
3088
+ });
3089
+ }
3090
+ async function preloadIcons(icons) {
3091
+ await Promise.all(icons.map((icon) => getIconData(icon)));
3092
+ }
3093
+ function clearIconCache() {
3094
+ iconCache.clear();
3095
+ pendingFetches.clear();
3096
+ registeredIcons.forEach((data, key) => {
3097
+ iconCache.set(key, data);
3098
+ });
3099
+ }
3100
+ function isIconSetSupported(set) {
3101
+ return set in ICONIFY_COLLECTIONS;
3102
+ }
3103
+ function getSupportedIconSets() {
3104
+ return Object.keys(ICONIFY_COLLECTIONS);
3105
+ }
3106
+ function getIconifyCollection(set) {
3107
+ return ICONIFY_COLLECTIONS[set];
1070
3108
  }
1071
3109
 
1072
- // src/renderer.ts
3110
+ // packages/runtime-core/src/renderer.ts
1073
3111
  var COMPONENT_TO_TAG = {
1074
3112
  container: "div",
1075
3113
  text: "span",
@@ -1218,7 +3256,7 @@ function resolveFunctionBinding(binding, context) {
1218
3256
  }
1219
3257
  return void 0;
1220
3258
  }
1221
- function attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement = false) {
3259
+ function attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement = false, state) {
1222
3260
  const elementId = element.i;
1223
3261
  if (eventHandlers && eventHandlers[elementId]) {
1224
3262
  const handlers = eventHandlers[elementId];
@@ -1228,6 +3266,28 @@ function attachEventHandlers(domElement, element, eventHandlers, context, elemen
1228
3266
  domElement.addEventListener(domEventName, handler);
1229
3267
  }
1230
3268
  }
3269
+ const config = element.configuration || {};
3270
+ for (const eventPropName of Object.keys(EVENT_HANDLERS)) {
3271
+ const handlerConfig = config[eventPropName];
3272
+ if (handlerConfig && handlerConfig.plugins && handlerConfig.plugins.length > 0) {
3273
+ const domEventName = toDomEventName(eventPropName);
3274
+ if (!elementState.eventListeners.has(domEventName)) {
3275
+ if (state?.eventSystem) {
3276
+ const handler = state.eventSystem.createHandler(elementId, handlerConfig, context);
3277
+ elementState.eventListeners.set(domEventName, handler);
3278
+ domElement.addEventListener(domEventName, handler);
3279
+ } else {
3280
+ const handler = (e) => {
3281
+ if (handlerConfig.preventDefault) e.preventDefault();
3282
+ if (handlerConfig.stopPropagation) e.stopPropagation();
3283
+ console.log(`[Servly] Event ${eventPropName} triggered on ${elementId}`, handlerConfig.plugins);
3284
+ };
3285
+ elementState.eventListeners.set(domEventName, handler);
3286
+ domElement.addEventListener(domEventName, handler);
3287
+ }
3288
+ }
3289
+ }
3290
+ }
1231
3291
  const bindings = element.configuration?.bindings?.inputs;
1232
3292
  if (bindings) {
1233
3293
  for (const [propName, binding] of Object.entries(bindings)) {
@@ -1262,10 +3322,16 @@ function detachEventHandlers(elementState) {
1262
3322
  }
1263
3323
  elementState.eventListeners.clear();
1264
3324
  }
1265
- function createElement(element, context, eventHandlers, isRootElement = false) {
3325
+ function createElement(element, context, eventHandlers, isRootElement = false, state) {
1266
3326
  const tag = getElementTag(element);
1267
3327
  const domElement = document.createElement(tag);
1268
3328
  domElement.setAttribute("data-servly-id", element.i);
3329
+ const slotName = element.slotName || element.configuration?.slotName;
3330
+ if (element.componentId === "slot" || slotName) {
3331
+ const name = slotName || element.i;
3332
+ domElement.setAttribute("data-slot", name);
3333
+ domElement.setAttribute("data-servly-slot", "true");
3334
+ }
1269
3335
  const styles = buildElementStyles(element, context);
1270
3336
  applyStyles(domElement, styles);
1271
3337
  const className = buildClassName(element, context);
@@ -1288,7 +3354,7 @@ function createElement(element, context, eventHandlers, isRootElement = false) {
1288
3354
  textContent,
1289
3355
  eventListeners: /* @__PURE__ */ new Map()
1290
3356
  };
1291
- attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement);
3357
+ attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement, state);
1292
3358
  return elementState;
1293
3359
  }
1294
3360
  var globalRenderingStack = /* @__PURE__ */ new Set();
@@ -1349,8 +3415,84 @@ function renderComponentRef(element, container, context, state) {
1349
3415
  globalRenderingStack.delete(refId);
1350
3416
  }
1351
3417
  }
3418
+ function extractViewId(binding) {
3419
+ if (!binding) return null;
3420
+ if (typeof binding === "string") return binding;
3421
+ if (binding.source === "static" && typeof binding.value === "string") return binding.value;
3422
+ if (binding.source === "node" && binding.binding?.viewId) return binding.binding.viewId;
3423
+ if (binding.source === "view" && typeof binding.value === "string") return binding.value;
3424
+ if (binding.viewId) return binding.viewId;
3425
+ if (binding.type === "view" && binding.viewId) return binding.viewId;
3426
+ return null;
3427
+ }
3428
+ function resolveComponentViewInputs(bindings, context, parentInputs) {
3429
+ if (!bindings) return {};
3430
+ const resolved = {};
3431
+ for (const [key, binding] of Object.entries(bindings)) {
3432
+ if (!binding) continue;
3433
+ const source = (binding.source || "").toLowerCase();
3434
+ switch (source) {
3435
+ case "static":
3436
+ case "value":
3437
+ case "constant":
3438
+ resolved[key] = binding.value;
3439
+ break;
3440
+ case "state":
3441
+ if (binding.path && context.state) {
3442
+ resolved[key] = getValueByPath2(context.state, binding.path);
3443
+ }
3444
+ break;
3445
+ case "props":
3446
+ case "parent":
3447
+ case "input":
3448
+ if (binding.path) {
3449
+ const source2 = parentInputs || context.props;
3450
+ const resolvedValue = getValueByPath2(source2, binding.path);
3451
+ if (resolvedValue !== void 0) {
3452
+ resolved[key] = resolvedValue;
3453
+ } else {
3454
+ resolved[key] = binding.value;
3455
+ }
3456
+ } else {
3457
+ resolved[key] = binding.value;
3458
+ }
3459
+ break;
3460
+ case "node":
3461
+ if (binding.binding?.viewId) {
3462
+ resolved[key] = binding.binding.viewId;
3463
+ } else if (binding.binding) {
3464
+ resolved[key] = binding.binding;
3465
+ } else {
3466
+ resolved[key] = binding.value;
3467
+ }
3468
+ break;
3469
+ default:
3470
+ resolved[key] = binding.value;
3471
+ }
3472
+ }
3473
+ return resolved;
3474
+ }
3475
+ function getValueByPath2(obj, path) {
3476
+ if (!obj || !path) return void 0;
3477
+ const parts = path.split(".");
3478
+ let current = obj;
3479
+ for (const part of parts) {
3480
+ if (current === null || current === void 0) return void 0;
3481
+ current = current[part];
3482
+ }
3483
+ return current;
3484
+ }
1352
3485
  function renderElement(element, tree, context, eventHandlers, elementStates, state, isRootElement = false) {
1353
- const elementState = createElement(element, context, eventHandlers, isRootElement);
3486
+ if (element.isComponentView) {
3487
+ return renderComponentViewElement(element, tree, context, eventHandlers, elementStates, state, isRootElement);
3488
+ }
3489
+ if (element.componentId === "slot") {
3490
+ return renderSlotElement(element, tree, context, eventHandlers, elementStates, state, isRootElement);
3491
+ }
3492
+ if (element.componentId === "icon") {
3493
+ return renderIconElement(element, context, eventHandlers, elementStates, state, isRootElement);
3494
+ }
3495
+ const elementState = createElement(element, context, eventHandlers, isRootElement, state);
1354
3496
  elementStates.set(element.i, elementState);
1355
3497
  const config = element.configuration;
1356
3498
  if (config?.componentViewRef) {
@@ -1367,8 +3509,201 @@ function renderElement(element, tree, context, eventHandlers, elementStates, sta
1367
3509
  }
1368
3510
  return elementState.domElement;
1369
3511
  }
3512
+ function renderIconElement(element, context, eventHandlers, elementStates, state, isRootElement) {
3513
+ const config = element.configuration || {};
3514
+ const icon = config.icon;
3515
+ const iconColor = config.iconColor || "currentColor";
3516
+ const iconSize = config.iconSize || 24;
3517
+ const wrapper = document.createElement("div");
3518
+ wrapper.setAttribute("data-servly-id", element.i);
3519
+ wrapper.className = "pointer-events-none";
3520
+ const styles = buildElementStyles(element, context);
3521
+ applyStyles(wrapper, styles);
3522
+ const className = buildClassName(element, context);
3523
+ if (className) {
3524
+ wrapper.className = `pointer-events-none ${className}`;
3525
+ }
3526
+ if (state.iconRenderer && icon) {
3527
+ const iconElement = state.iconRenderer(icon, iconSize, iconColor, "");
3528
+ if (iconElement) {
3529
+ wrapper.appendChild(iconElement);
3530
+ }
3531
+ } else if (icon) {
3532
+ renderIcon(wrapper, icon, iconSize, iconColor);
3533
+ }
3534
+ const elementState = {
3535
+ element,
3536
+ domElement: wrapper,
3537
+ styles,
3538
+ className: wrapper.className,
3539
+ textContent: "",
3540
+ eventListeners: /* @__PURE__ */ new Map()
3541
+ };
3542
+ elementStates.set(element.i, elementState);
3543
+ attachEventHandlers(wrapper, element, eventHandlers, context, elementState, isRootElement, state);
3544
+ return wrapper;
3545
+ }
3546
+ function renderComponentViewElement(element, tree, context, eventHandlers, elementStates, state, isRootElement) {
3547
+ const componentViewId = `${element.componentId}-${element.i}`;
3548
+ if (globalRenderingStack.has(componentViewId)) {
3549
+ const placeholder = document.createElement("div");
3550
+ placeholder.className = "border-2 border-red-500 border-dashed p-4 bg-red-50";
3551
+ placeholder.innerHTML = `<p class="text-red-600 text-sm">Recursive component: ${element.componentId}</p>`;
3552
+ return placeholder;
3553
+ }
3554
+ let viewLayout;
3555
+ if (state.views?.has(element.componentId)) {
3556
+ const view = state.views.get(element.componentId);
3557
+ viewLayout = view?.layout;
3558
+ }
3559
+ if (!viewLayout && state.componentRegistry) {
3560
+ const component = state.componentRegistry.get(element.componentId);
3561
+ if (component) {
3562
+ viewLayout = component.layout;
3563
+ }
3564
+ }
3565
+ if (!viewLayout && state.views === void 0 && state.viewsArray) {
3566
+ const viewsArray = state.viewsArray;
3567
+ const found = viewsArray.find((v) => v.id === element.componentId);
3568
+ if (found) {
3569
+ viewLayout = found.layout;
3570
+ }
3571
+ }
3572
+ if (!viewLayout) {
3573
+ console.warn(`[Servly] Component not found: ${element.componentId}. Available in views: ${state.views ? Array.from(state.views.keys()).join(", ") : "none"}. Registry has: ${state.componentRegistry?.has(element.componentId) ? "yes" : "no"}`);
3574
+ const placeholder = document.createElement("div");
3575
+ placeholder.className = "border-2 border-yellow-500 border-dashed p-4 bg-yellow-50";
3576
+ placeholder.innerHTML = `<p class="text-yellow-600 text-sm">Component not found: ${element.componentId}</p>`;
3577
+ return placeholder;
3578
+ }
3579
+ const bindings = element.configuration?.bindings?.inputs || {};
3580
+ const resolvedInputs = resolveComponentViewInputs(bindings, context, context.props);
3581
+ const componentContext = {
3582
+ props: { ...context.props, ...resolvedInputs },
3583
+ state: context.state,
3584
+ context: context.context
3585
+ };
3586
+ globalRenderingStack.add(componentViewId);
3587
+ try {
3588
+ const wrapper = document.createElement("div");
3589
+ wrapper.id = element.i;
3590
+ wrapper.className = "contents";
3591
+ const viewTree = buildTree(viewLayout);
3592
+ const viewRoots = viewLayout.filter((el) => !el.parent || el.parent === null);
3593
+ const nestedState = {
3594
+ ...state,
3595
+ elements: viewLayout,
3596
+ context: componentContext,
3597
+ elementStates: /* @__PURE__ */ new Map(),
3598
+ rootElement: null,
3599
+ renderingStack: new Set(state.renderingStack)
3600
+ };
3601
+ nestedState.renderingStack.add(componentViewId);
3602
+ for (const root of viewRoots) {
3603
+ const rootElement = renderElement(
3604
+ root,
3605
+ viewTree,
3606
+ componentContext,
3607
+ eventHandlers,
3608
+ nestedState.elementStates,
3609
+ nestedState,
3610
+ true
3611
+ );
3612
+ wrapper.appendChild(rootElement);
3613
+ }
3614
+ const elementState = {
3615
+ element,
3616
+ domElement: wrapper,
3617
+ styles: {},
3618
+ className: "contents",
3619
+ textContent: "",
3620
+ eventListeners: /* @__PURE__ */ new Map()
3621
+ };
3622
+ elementStates.set(element.i, elementState);
3623
+ return wrapper;
3624
+ } finally {
3625
+ globalRenderingStack.delete(componentViewId);
3626
+ }
3627
+ }
3628
+ function renderSlotElement(element, tree, context, eventHandlers, elementStates, state, isRootElement) {
3629
+ const elementState = createElement(element, context, eventHandlers, isRootElement, state);
3630
+ elementStates.set(element.i, elementState);
3631
+ const bindings = element.configuration?.bindings?.inputs || {};
3632
+ let childViewId = extractViewId(bindings.child) || extractViewId(bindings.children) || extractViewId(bindings.content);
3633
+ if (!childViewId && context.props) {
3634
+ childViewId = extractViewId(context.props.child) || extractViewId(context.props.children) || extractViewId(context.props.content);
3635
+ }
3636
+ if (childViewId && typeof childViewId === "string") {
3637
+ let viewLayout;
3638
+ if (state.views?.has(childViewId)) {
3639
+ const view = state.views.get(childViewId);
3640
+ viewLayout = view?.layout;
3641
+ }
3642
+ if (!viewLayout && state.componentRegistry) {
3643
+ const component = state.componentRegistry.get(childViewId);
3644
+ if (component) {
3645
+ viewLayout = component.layout;
3646
+ }
3647
+ }
3648
+ if (viewLayout) {
3649
+ const viewTree = buildTree(viewLayout);
3650
+ const viewRoots = viewLayout.filter((el) => !el.parent || el.parent === null);
3651
+ const nestedState = {
3652
+ ...state,
3653
+ elements: viewLayout,
3654
+ elementStates: /* @__PURE__ */ new Map(),
3655
+ rootElement: null
3656
+ };
3657
+ for (const root of viewRoots) {
3658
+ const rootElement = renderElement(
3659
+ root,
3660
+ viewTree,
3661
+ context,
3662
+ eventHandlers,
3663
+ nestedState.elementStates,
3664
+ nestedState,
3665
+ false
3666
+ );
3667
+ elementState.domElement.appendChild(rootElement);
3668
+ }
3669
+ return elementState.domElement;
3670
+ }
3671
+ }
3672
+ const children = tree.get(element.i) || [];
3673
+ for (const child of children) {
3674
+ const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state, false);
3675
+ elementState.domElement.appendChild(childElement);
3676
+ }
3677
+ return elementState.domElement;
3678
+ }
1370
3679
  function render(options) {
1371
- const { container, elements, context, eventHandlers, componentRegistry, onDependencyNeeded } = options;
3680
+ const {
3681
+ container,
3682
+ elements,
3683
+ context,
3684
+ eventHandlers,
3685
+ componentRegistry,
3686
+ onDependencyNeeded,
3687
+ views: viewsInput,
3688
+ enableStateManager,
3689
+ initialState,
3690
+ onStateChange,
3691
+ pluginExecutors,
3692
+ onNavigate,
3693
+ onApiCall,
3694
+ iconRenderer
3695
+ } = options;
3696
+ let views;
3697
+ if (viewsInput instanceof Map) {
3698
+ views = viewsInput;
3699
+ } else if (Array.isArray(viewsInput)) {
3700
+ views = /* @__PURE__ */ new Map();
3701
+ for (const view of viewsInput) {
3702
+ if (view && view.id) {
3703
+ views.set(view.id, view);
3704
+ }
3705
+ }
3706
+ }
1372
3707
  const startTime = performance.now();
1373
3708
  const memorySampler = getMemorySampler();
1374
3709
  const longTaskObserver = getLongTaskObserver();
@@ -1377,6 +3712,29 @@ function render(options) {
1377
3712
  const rootElements = elements.filter((el) => !el.parent || el.parent === null);
1378
3713
  const componentId = rootElements[0]?.componentId || "unknown";
1379
3714
  const version = "latest";
3715
+ let stateManager;
3716
+ if (enableStateManager) {
3717
+ stateManager = new StateManager({
3718
+ initialState: initialState || context.state,
3719
+ onStateChange: onStateChange ? (event) => onStateChange({
3720
+ key: event.key,
3721
+ value: event.value,
3722
+ previousValue: event.previousValue
3723
+ }) : void 0
3724
+ });
3725
+ }
3726
+ const eventSystem = new EventSystem({
3727
+ stateManager,
3728
+ pluginExecutors,
3729
+ onNavigate,
3730
+ onApiCall
3731
+ });
3732
+ const overrideSystem = new OverrideSystem({
3733
+ eventSystem,
3734
+ stateManager
3735
+ });
3736
+ const hasAnyOverrides = elements.some((el) => hasOverrides(el));
3737
+ const hasAnyDependencyOverrides = elements.some((el) => hasDependencyOverrides(el));
1380
3738
  try {
1381
3739
  const tree = buildTree(elements);
1382
3740
  const state = {
@@ -1388,7 +3746,13 @@ function render(options) {
1388
3746
  rootElement: null,
1389
3747
  componentRegistry,
1390
3748
  onDependencyNeeded,
1391
- renderingStack: /* @__PURE__ */ new Set()
3749
+ renderingStack: /* @__PURE__ */ new Set(),
3750
+ views,
3751
+ eventSystem,
3752
+ stateManager,
3753
+ overrideSystem,
3754
+ enableOverrides: hasAnyOverrides,
3755
+ iconRenderer
1392
3756
  };
1393
3757
  container.innerHTML = "";
1394
3758
  if (rootElements.length === 0) {
@@ -1426,6 +3790,16 @@ function render(options) {
1426
3790
  state.rootElement = wrapper;
1427
3791
  container.appendChild(wrapper);
1428
3792
  }
3793
+ if (hasAnyOverrides && overrideSystem) {
3794
+ for (const element of elements) {
3795
+ if (hasOverrides(element)) {
3796
+ overrideSystem.initializeElement(element, context);
3797
+ if (hasDependencyOverrides(element)) {
3798
+ overrideSystem.startWatching(element, context);
3799
+ }
3800
+ }
3801
+ }
3802
+ }
1429
3803
  const duration = performance.now() - startTime;
1430
3804
  const memoryAfter = memorySampler.sample();
1431
3805
  const longTasks = longTaskObserver.stop();
@@ -1444,6 +3818,7 @@ function render(options) {
1444
3818
  };
1445
3819
  } catch (error) {
1446
3820
  longTaskObserver.stop();
3821
+ overrideSystem.destroy();
1447
3822
  analytics.trackError(componentId, version, error, {
1448
3823
  errorType: "render"
1449
3824
  });
@@ -1474,12 +3849,23 @@ function update(state, newContext) {
1474
3849
  }
1475
3850
  }
1476
3851
  function destroy(state) {
3852
+ if (state.overrideSystem && state.enableOverrides) {
3853
+ for (const element of state.elements) {
3854
+ if (hasOverrides(element)) {
3855
+ state.overrideSystem.cleanupElement(element, state.context);
3856
+ }
3857
+ }
3858
+ state.overrideSystem.destroy();
3859
+ }
1477
3860
  for (const elementState of state.elementStates.values()) {
1478
3861
  detachEventHandlers(elementState);
1479
3862
  if (elementState.nestedRender) {
1480
3863
  elementState.nestedRender.destroy();
1481
3864
  }
1482
3865
  }
3866
+ if (state.eventSystem) {
3867
+ state.eventSystem.destroy();
3868
+ }
1483
3869
  state.elementStates.clear();
1484
3870
  if (state.rootElement && state.rootElement.parentNode) {
1485
3871
  state.rootElement.parentNode.removeChild(state.rootElement);
@@ -1546,8 +3932,202 @@ function renderDynamicList(options) {
1546
3932
  }
1547
3933
  return results;
1548
3934
  }
3935
+ function renderNode(options) {
3936
+ const {
3937
+ container,
3938
+ elements,
3939
+ nodeId,
3940
+ context,
3941
+ includeChildren = true,
3942
+ eventHandlers,
3943
+ componentRegistry,
3944
+ views
3945
+ } = options;
3946
+ const targetNode = elements.find((el) => el.i === nodeId);
3947
+ if (!targetNode) {
3948
+ console.error(`renderNode: Node not found: ${nodeId}`);
3949
+ return null;
3950
+ }
3951
+ if (!includeChildren) {
3952
+ const singleElementLayout = [targetNode];
3953
+ return render({
3954
+ container,
3955
+ elements: singleElementLayout,
3956
+ context,
3957
+ eventHandlers,
3958
+ componentRegistry,
3959
+ views
3960
+ });
3961
+ }
3962
+ const descendantIds = /* @__PURE__ */ new Set();
3963
+ const collectDescendants = (parentId) => {
3964
+ for (const el of elements) {
3965
+ if (el.parent === parentId) {
3966
+ descendantIds.add(el.i);
3967
+ collectDescendants(el.i);
3968
+ }
3969
+ }
3970
+ };
3971
+ descendantIds.add(nodeId);
3972
+ collectDescendants(nodeId);
3973
+ const nodeElements = elements.filter((el) => descendantIds.has(el.i));
3974
+ const rootedElements = nodeElements.map(
3975
+ (el) => el.i === nodeId ? { ...el, parent: null } : el
3976
+ );
3977
+ return render({
3978
+ container,
3979
+ elements: rootedElements,
3980
+ context,
3981
+ eventHandlers,
3982
+ componentRegistry,
3983
+ views
3984
+ });
3985
+ }
3986
+ function renderInShadow(options) {
3987
+ const { container, mode = "open", styles, injectTailwind: shouldInjectTailwind, ...renderOptions } = options;
3988
+ const shadowRoot = container.attachShadow({ mode });
3989
+ const innerContainer = document.createElement("div");
3990
+ innerContainer.className = "servly-shadow-container";
3991
+ shadowRoot.appendChild(innerContainer);
3992
+ if (styles) {
3993
+ const styleEl = document.createElement("style");
3994
+ styleEl.textContent = styles;
3995
+ shadowRoot.insertBefore(styleEl, innerContainer);
3996
+ }
3997
+ if (shouldInjectTailwind) {
3998
+ const tailwindLink = document.createElement("link");
3999
+ tailwindLink.rel = "stylesheet";
4000
+ tailwindLink.href = "https://cdn.tailwindcss.com";
4001
+ shadowRoot.insertBefore(tailwindLink, innerContainer);
4002
+ }
4003
+ const result = render({
4004
+ ...renderOptions,
4005
+ container: innerContainer
4006
+ });
4007
+ return {
4008
+ ...result,
4009
+ shadowRoot
4010
+ };
4011
+ }
4012
+ async function createServlyRenderer(options) {
4013
+ const {
4014
+ container: containerOption,
4015
+ injectTailwind: shouldInjectTailwind = true,
4016
+ tailwindConfig,
4017
+ initialState,
4018
+ onStateChange,
4019
+ onNavigate
4020
+ } = options;
4021
+ let container;
4022
+ if (typeof containerOption === "string") {
4023
+ const el = document.querySelector(containerOption);
4024
+ if (!el) {
4025
+ throw new Error(`Container not found: ${containerOption}`);
4026
+ }
4027
+ container = el;
4028
+ } else {
4029
+ container = containerOption;
4030
+ }
4031
+ if (shouldInjectTailwind) {
4032
+ const { initServlyTailwind: initServlyTailwind2 } = await Promise.resolve().then(() => (init_tailwind(), tailwind_exports));
4033
+ await initServlyTailwind2(tailwindConfig);
4034
+ }
4035
+ const activeRenders = [];
4036
+ return {
4037
+ render: (elements, context = { props: {} }) => {
4038
+ const result = render({
4039
+ container,
4040
+ elements,
4041
+ context,
4042
+ enableStateManager: true,
4043
+ initialState,
4044
+ onStateChange,
4045
+ onNavigate
4046
+ });
4047
+ activeRenders.push(result);
4048
+ return result;
4049
+ },
4050
+ renderNode: (elements, nodeId, context = { props: {} }) => {
4051
+ const result = renderNode({
4052
+ container,
4053
+ elements,
4054
+ nodeId,
4055
+ context
4056
+ });
4057
+ if (result) {
4058
+ activeRenders.push(result);
4059
+ }
4060
+ return result;
4061
+ },
4062
+ renderDynamicList,
4063
+ destroy: () => {
4064
+ for (const result of activeRenders) {
4065
+ result.destroy();
4066
+ }
4067
+ activeRenders.length = 0;
4068
+ container.innerHTML = "";
4069
+ }
4070
+ };
4071
+ }
4072
+ function createViewsMap(views) {
4073
+ const viewsMap = /* @__PURE__ */ new Map();
4074
+ for (const view of views) {
4075
+ const id = view.id || view._id;
4076
+ if (id && view.layout) {
4077
+ viewsMap.set(id, {
4078
+ id,
4079
+ layout: view.layout,
4080
+ props: view.props
4081
+ });
4082
+ }
4083
+ }
4084
+ return viewsMap;
4085
+ }
4086
+ function extractReferencedViewIds(elements) {
4087
+ const viewIds = /* @__PURE__ */ new Set();
4088
+ for (const element of elements) {
4089
+ if (element.isComponentView && element.componentId) {
4090
+ viewIds.add(element.componentId);
4091
+ }
4092
+ if (element.configuration?.componentViewRef) {
4093
+ viewIds.add(element.configuration.componentViewRef);
4094
+ }
4095
+ const bindings = element.configuration?.bindings?.inputs;
4096
+ if (bindings) {
4097
+ for (const binding of Object.values(bindings)) {
4098
+ if (binding && typeof binding === "object") {
4099
+ if (binding.source === "node" && binding.binding?.viewId) {
4100
+ viewIds.add(binding.binding.viewId);
4101
+ }
4102
+ if (binding.viewId) {
4103
+ viewIds.add(binding.viewId);
4104
+ }
4105
+ }
4106
+ }
4107
+ }
4108
+ }
4109
+ return Array.from(viewIds);
4110
+ }
4111
+ function collectAllViewDependencies(views, startViewId) {
4112
+ const collected = /* @__PURE__ */ new Set();
4113
+ const toProcess = [startViewId];
4114
+ while (toProcess.length > 0) {
4115
+ const viewId = toProcess.pop();
4116
+ if (collected.has(viewId)) continue;
4117
+ collected.add(viewId);
4118
+ const view = views.get(viewId);
4119
+ if (!view) continue;
4120
+ const referencedIds = extractReferencedViewIds(view.layout);
4121
+ for (const refId of referencedIds) {
4122
+ if (!collected.has(refId)) {
4123
+ toProcess.push(refId);
4124
+ }
4125
+ }
4126
+ }
4127
+ return collected;
4128
+ }
1549
4129
 
1550
- // src/cache.ts
4130
+ // packages/runtime-core/src/cache.ts
1551
4131
  var DEFAULT_CACHE_CONFIG = {
1552
4132
  maxEntries: 50,
1553
4133
  ttl: 5 * 60 * 1e3,
@@ -1791,7 +4371,8 @@ function invalidateCache(id, version, config = DEFAULT_CACHE_CONFIG) {
1791
4371
  }
1792
4372
  }
1793
4373
 
1794
- // src/fetcher.ts
4374
+ // packages/runtime-core/src/fetcher.ts
4375
+ init_registry();
1795
4376
  var DEFAULT_RETRY_CONFIG = {
1796
4377
  maxRetries: 3,
1797
4378
  initialDelay: 1e3,
@@ -1819,6 +4400,18 @@ function calculateBackoffDelay(retryCount, config) {
1819
4400
  function sleep(ms) {
1820
4401
  return new Promise((resolve) => setTimeout(resolve, ms));
1821
4402
  }
4403
+ function buildViewsMap(views) {
4404
+ if (!views || views.length === 0) return void 0;
4405
+ const viewsMap = /* @__PURE__ */ new Map();
4406
+ for (const view of views) {
4407
+ viewsMap.set(view.id, {
4408
+ id: view.id,
4409
+ layout: view.layout,
4410
+ props: view.props
4411
+ });
4412
+ }
4413
+ return viewsMap;
4414
+ }
1822
4415
  async function resolveVersionFromApi(id, specifier, apiKey) {
1823
4416
  if (/^\d+\.\d+\.\d+$/.test(specifier)) {
1824
4417
  return specifier;
@@ -1848,7 +4441,7 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
1848
4441
  return "latest";
1849
4442
  }
1850
4443
  }
1851
- async function fetchFromRegistry(id, version, apiKey, includeBundle) {
4444
+ async function fetchFromRegistry(id, version, apiKey, includeBundle, includeViews) {
1852
4445
  const baseUrl = getRegistryUrl();
1853
4446
  const headers = {
1854
4447
  "Content-Type": "application/json"
@@ -1867,6 +4460,9 @@ async function fetchFromRegistry(id, version, apiKey, includeBundle) {
1867
4460
  if (includeBundle) {
1868
4461
  url += (url.includes("?") ? "&" : "?") + "bundle=true";
1869
4462
  }
4463
+ if (includeViews) {
4464
+ url += (url.includes("?") ? "&" : "?") + "includeViews=true";
4465
+ }
1870
4466
  const response = await fetch(url, { headers });
1871
4467
  if (!response.ok) {
1872
4468
  if (response.status === 404) {
@@ -1900,7 +4496,9 @@ async function fetchComponent(id, options = {}) {
1900
4496
  forceRefresh = false,
1901
4497
  signal,
1902
4498
  bundleStrategy = "eager",
1903
- includeBundle = true
4499
+ includeBundle = true,
4500
+ includeViews = true
4501
+ // Default to true - fetch all views needed
1904
4502
  } = options;
1905
4503
  const fullRetryConfig = {
1906
4504
  ...DEFAULT_RETRY_CONFIG,
@@ -1914,6 +4512,7 @@ async function fetchComponent(id, options = {}) {
1914
4512
  if (cached.bundle) {
1915
4513
  registry = buildRegistryFromBundle(cached);
1916
4514
  }
4515
+ const views = buildViewsMap(cached.views);
1917
4516
  const duration = performance.now() - startTime;
1918
4517
  analytics.trackFetch(id, cached.version, duration, true, {
1919
4518
  cacheHit: true,
@@ -1923,7 +4522,8 @@ async function fetchComponent(id, options = {}) {
1923
4522
  data: cached,
1924
4523
  fromCache: true,
1925
4524
  version: cached.version,
1926
- registry
4525
+ registry,
4526
+ views
1927
4527
  };
1928
4528
  }
1929
4529
  }
@@ -1935,11 +4535,13 @@ async function fetchComponent(id, options = {}) {
1935
4535
  if (cached.bundle) {
1936
4536
  registry = buildRegistryFromBundle(cached);
1937
4537
  }
4538
+ const views = buildViewsMap(cached.views);
1938
4539
  return {
1939
4540
  data: cached,
1940
4541
  fromCache: true,
1941
4542
  version: resolvedVersion,
1942
- registry
4543
+ registry,
4544
+ views
1943
4545
  };
1944
4546
  }
1945
4547
  }
@@ -1950,7 +4552,7 @@ async function fetchComponent(id, options = {}) {
1950
4552
  throw new Error("Fetch aborted");
1951
4553
  }
1952
4554
  try {
1953
- const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle);
4555
+ const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle, includeViews);
1954
4556
  setInCache(id, resolvedVersion, data, cacheStrategy, cacheConfig);
1955
4557
  if (version !== resolvedVersion) {
1956
4558
  setInCache(id, version, data, cacheStrategy, cacheConfig);
@@ -1965,6 +4567,7 @@ async function fetchComponent(id, options = {}) {
1965
4567
  version: entry.resolved || entry.version
1966
4568
  }));
1967
4569
  }
4570
+ const views = buildViewsMap(data.views);
1968
4571
  const duration = performance.now() - startTime;
1969
4572
  analytics.trackFetch(id, resolvedVersion, duration, false, {
1970
4573
  cacheHit: false,
@@ -1976,7 +4579,8 @@ async function fetchComponent(id, options = {}) {
1976
4579
  fromCache: false,
1977
4580
  version: resolvedVersion,
1978
4581
  registry,
1979
- pendingDependencies
4582
+ pendingDependencies,
4583
+ views
1980
4584
  };
1981
4585
  } catch (error) {
1982
4586
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -1998,7 +4602,7 @@ async function fetchComponent(id, options = {}) {
1998
4602
  async function fetchComponentWithDependencies(id, options = {}) {
1999
4603
  const result = await fetchComponent(id, { ...options, includeBundle: true });
2000
4604
  if (result.pendingDependencies && result.pendingDependencies.length > 0) {
2001
- const { createRegistry: createRegistry2 } = await import("./registry-GCCVK65D.js");
4605
+ const { createRegistry: createRegistry2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
2002
4606
  const registry = result.registry || createRegistry2();
2003
4607
  await Promise.all(
2004
4608
  result.pendingDependencies.map(async (dep) => {
@@ -2104,7 +4708,7 @@ async function getDependencyTree(id, options = {}) {
2104
4708
  return data.data;
2105
4709
  }
2106
4710
 
2107
- // src/version.ts
4711
+ // packages/runtime-core/src/version.ts
2108
4712
  function parseVersion(version) {
2109
4713
  const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
2110
4714
  if (!match) return null;
@@ -2216,7 +4820,7 @@ function formatVersion(version) {
2216
4820
  return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
2217
4821
  }
2218
4822
 
2219
- // src/testRunner.ts
4823
+ // packages/runtime-core/src/testRunner.ts
2220
4824
  function runTestCase(elements, testCase, container) {
2221
4825
  const startTime = performance.now();
2222
4826
  const assertionResults = [];
@@ -2347,13 +4951,13 @@ function validateAssertion(container, assertion) {
2347
4951
  message: `Element "${assertion.selector}" not found`
2348
4952
  };
2349
4953
  }
2350
- const hasClass = elements[0].classList.contains(assertion.expected);
4954
+ const hasClass2 = elements[0].classList.contains(assertion.expected);
2351
4955
  return {
2352
4956
  assertion,
2353
- passed: hasClass,
4957
+ passed: hasClass2,
2354
4958
  actual: Array.from(elements[0].classList),
2355
4959
  expected: assertion.expected,
2356
- message: hasClass ? `Element has class "${assertion.expected}"` : `Element does not have class "${assertion.expected}"`
4960
+ message: hasClass2 ? `Element has class "${assertion.expected}"` : `Element does not have class "${assertion.expected}"`
2357
4961
  };
2358
4962
  case "style":
2359
4963
  if (elements.length === 0) {
@@ -2470,13 +5074,161 @@ function getSampleValue(def) {
2470
5074
  return def.defaultValue;
2471
5075
  }
2472
5076
  }
2473
- export {
5077
+
5078
+ // packages/runtime-core/src/index.ts
5079
+ init_registry();
5080
+ init_tailwind();
5081
+
5082
+ // packages/runtime-core/src/iconExtractor.ts
5083
+ var REACT_ICONS_PACKAGES = {
5084
+ Ai: "react-icons/ai",
5085
+ Bi: "react-icons/bi",
5086
+ Bs: "react-icons/bs",
5087
+ Cg: "react-icons/cg",
5088
+ Di: "react-icons/di",
5089
+ Fa: "react-icons/fa",
5090
+ Fa6: "react-icons/fa6",
5091
+ Fc: "react-icons/fc",
5092
+ Fi: "react-icons/fi",
5093
+ Gi: "react-icons/gi",
5094
+ Go: "react-icons/go",
5095
+ Gr: "react-icons/gr",
5096
+ Hi: "react-icons/hi",
5097
+ Hi2: "react-icons/hi2",
5098
+ Im: "react-icons/im",
5099
+ Io: "react-icons/io",
5100
+ Io5: "react-icons/io5",
5101
+ Lu: "react-icons/lu",
5102
+ Md: "react-icons/md",
5103
+ Pi: "react-icons/pi",
5104
+ Ri: "react-icons/ri",
5105
+ Rx: "react-icons/rx",
5106
+ Si: "react-icons/si",
5107
+ Sl: "react-icons/sl",
5108
+ Tb: "react-icons/tb",
5109
+ Tfi: "react-icons/tfi",
5110
+ Vsc: "react-icons/vsc",
5111
+ Wi: "react-icons/wi"
5112
+ };
5113
+ async function extractIconFromReactIcons(iconName, iconSet) {
5114
+ const packagePath = REACT_ICONS_PACKAGES[iconSet];
5115
+ if (!packagePath) {
5116
+ console.warn(`Unknown icon set: ${iconSet}`);
5117
+ return null;
5118
+ }
5119
+ try {
5120
+ const iconModule = await import(packagePath);
5121
+ const IconComponent = iconModule[iconName];
5122
+ if (!IconComponent) {
5123
+ console.warn(`Icon not found: ${iconName} in ${packagePath}`);
5124
+ return null;
5125
+ }
5126
+ const React = await import("react");
5127
+ const { renderToStaticMarkup } = await import("react-dom/server");
5128
+ const svgString = renderToStaticMarkup(React.createElement(IconComponent, { size: 24 }));
5129
+ return parseSvgString(svgString);
5130
+ } catch (error) {
5131
+ console.error(`Failed to extract icon ${iconSet}:${iconName}:`, error);
5132
+ return null;
5133
+ }
5134
+ }
5135
+ function parseSvgString(svgString) {
5136
+ const viewBoxMatch = svgString.match(/viewBox="([^"]+)"/);
5137
+ const viewBox = viewBoxMatch ? viewBoxMatch[1] : "0 0 24 24";
5138
+ const widthMatch = svgString.match(/width="(\d+)"/);
5139
+ const heightMatch = svgString.match(/height="(\d+)"/);
5140
+ const width = widthMatch ? parseInt(widthMatch[1], 10) : 24;
5141
+ const height = heightMatch ? parseInt(heightMatch[1], 10) : 24;
5142
+ const bodyMatch = svgString.match(/<svg[^>]*>([\s\S]*)<\/svg>/);
5143
+ const body = bodyMatch ? bodyMatch[1].trim() : "";
5144
+ if (!body) {
5145
+ return null;
5146
+ }
5147
+ return {
5148
+ body,
5149
+ viewBox,
5150
+ width,
5151
+ height
5152
+ };
5153
+ }
5154
+ function findIconsInLayout(elements) {
5155
+ const icons = [];
5156
+ const seen = /* @__PURE__ */ new Set();
5157
+ for (const element of elements) {
5158
+ if (element.componentId === "icon" && element.configuration?.icon) {
5159
+ const icon = element.configuration.icon;
5160
+ const key = `${icon.set}:${icon.name}`;
5161
+ if (!seen.has(key)) {
5162
+ seen.add(key);
5163
+ icons.push({
5164
+ name: icon.name,
5165
+ set: icon.set,
5166
+ setName: icon.setName
5167
+ });
5168
+ }
5169
+ }
5170
+ }
5171
+ return icons;
5172
+ }
5173
+ async function extractIconsForLayout(elements) {
5174
+ const icons = findIconsInLayout(elements);
5175
+ const result = {};
5176
+ for (const icon of icons) {
5177
+ const data = await extractIconFromReactIcons(icon.name, icon.set);
5178
+ if (data) {
5179
+ if (!result[icon.set]) {
5180
+ result[icon.set] = {};
5181
+ }
5182
+ result[icon.set][icon.name] = data;
5183
+ }
5184
+ }
5185
+ return result;
5186
+ }
5187
+ function generateIconBundle(icons) {
5188
+ const lines = [
5189
+ "// Auto-generated icon bundle",
5190
+ "// Do not edit manually",
5191
+ "",
5192
+ "import { registerIcons, type IconData } from '@servlyadmin/runtime-core';",
5193
+ "",
5194
+ "const BUNDLED_ICONS: Record<string, Record<string, IconData>> = {"
5195
+ ];
5196
+ for (const [set, setIcons] of Object.entries(icons)) {
5197
+ lines.push(` ${set}: {`);
5198
+ for (const [name, data] of Object.entries(setIcons)) {
5199
+ const escapedBody = data.body.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
5200
+ lines.push(` ${name}: {`);
5201
+ lines.push(` body: '${escapedBody}',`);
5202
+ lines.push(` viewBox: '${data.viewBox}',`);
5203
+ if (data.width) lines.push(` width: ${data.width},`);
5204
+ if (data.height) lines.push(` height: ${data.height},`);
5205
+ lines.push(` },`);
5206
+ }
5207
+ lines.push(` },`);
5208
+ }
5209
+ lines.push("};");
5210
+ lines.push("");
5211
+ lines.push("// Register all bundled icons");
5212
+ lines.push("registerIcons(BUNDLED_ICONS);");
5213
+ lines.push("");
5214
+ lines.push("export { BUNDLED_ICONS };");
5215
+ return lines.join("\n");
5216
+ }
5217
+ // Annotate the CommonJS export names for ESM import in node:
5218
+ 0 && (module.exports = {
2474
5219
  AnalyticsCollector,
2475
5220
  DEFAULT_CACHE_CONFIG,
2476
5221
  DEFAULT_RETRY_CONFIG,
5222
+ DEFAULT_SERVLY_TAILWIND_CONFIG,
5223
+ EVENT_HANDLERS,
5224
+ EventSystem,
2477
5225
  LongTaskObserver,
2478
5226
  MemorySampler,
5227
+ OverrideSystem,
2479
5228
  SessionManager,
5229
+ StateManager,
5230
+ addClass,
5231
+ addCustomStyles,
2480
5232
  analytics,
2481
5233
  applyStyles,
2482
5234
  batchFetchComponents,
@@ -2486,43 +5238,96 @@ export {
2486
5238
  bumpVersion,
2487
5239
  camelToKebab,
2488
5240
  clearAllCaches,
5241
+ clearIconCache,
2489
5242
  clearLocalStorageCache,
2490
5243
  clearMemoryCache,
2491
5244
  clearStyles,
2492
5245
  collectAllDependencies,
5246
+ collectAllViewDependencies,
2493
5247
  compareVersions,
2494
5248
  configureAnalytics,
5249
+ createIconSVG,
5250
+ createPlaceholderIcon,
2495
5251
  createRegistry,
5252
+ createServlyRenderer,
5253
+ createViewsMap,
5254
+ deepMerge,
5255
+ deleteValueByPath,
2496
5256
  detectCircularDependencies,
2497
5257
  extractBindingKeys,
2498
5258
  extractDependencies,
2499
5259
  extractDependenciesFromCode,
5260
+ extractIconFromReactIcons,
5261
+ extractIconsForLayout,
5262
+ extractOverrideDependencies,
5263
+ extractReferencedViewIds,
2500
5264
  fetchComponent,
2501
5265
  fetchComponentWithDependencies,
5266
+ findIconsInLayout,
2502
5267
  formatStyleValue,
2503
5268
  formatVersion,
5269
+ generateIconBundle,
2504
5270
  generateTestCases,
2505
5271
  getAnalytics,
2506
5272
  getCacheKey,
5273
+ getCleanupOverrides,
2507
5274
  getDependencyTree,
5275
+ getEventSystem,
2508
5276
  getFromCache,
5277
+ getIconData,
5278
+ getIconDataSync,
5279
+ getIconifyCollection,
5280
+ getLocalStorage,
2509
5281
  getLongTaskObserver,
2510
5282
  getMemoryCacheSize,
2511
5283
  getMemorySampler,
5284
+ getMountOverrides,
5285
+ getOverrideSystem,
5286
+ getRegisteredIconKeys,
2512
5287
  getRegistryUrl,
2513
5288
  getSessionManager,
5289
+ getSessionStorage,
5290
+ getSupportedIconSets,
5291
+ getTailwind,
5292
+ getUrlInfo,
5293
+ getValueByPath,
5294
+ goBack,
5295
+ goForward,
5296
+ hasClass,
5297
+ hasDependencyOverrides,
5298
+ hasOverrides,
2514
5299
  hasTemplateSyntax,
5300
+ initServlyTailwind,
5301
+ injectTailwind,
2515
5302
  invalidateCache,
2516
5303
  isComponentAvailable,
5304
+ isIconCdnEnabled,
5305
+ isIconRegistered,
5306
+ isIconSetSupported,
5307
+ isTailwindLoaded,
2517
5308
  isValidSpecifier,
5309
+ navigateTo,
2518
5310
  parseVersion,
2519
5311
  prefetchComponents,
5312
+ preloadIcons,
2520
5313
  processStyles,
5314
+ registerIcon,
5315
+ registerIcons,
5316
+ removeClass,
5317
+ removeCustomStyles,
5318
+ removeLocalStorage,
5319
+ removeSessionStorage,
5320
+ removeTailwind,
2521
5321
  render,
2522
5322
  renderDynamicList,
5323
+ renderIcon,
5324
+ renderInShadow,
5325
+ renderNode,
2523
5326
  resetAnalytics,
5327
+ resetEventSystem,
2524
5328
  resetLongTaskObserver,
2525
5329
  resetMemorySampler,
5330
+ resetOverrideSystem,
2526
5331
  resetSessionManager,
2527
5332
  resolveBindingPath,
2528
5333
  resolveTemplate,
@@ -2532,9 +5337,17 @@ export {
2532
5337
  runAllTests,
2533
5338
  runTestCase,
2534
5339
  satisfiesVersion,
5340
+ setIconCdnEnabled,
2535
5341
  setInCache,
5342
+ setLocalStorage,
2536
5343
  setRegistryUrl,
5344
+ setSessionStorage,
5345
+ setValueByPath,
5346
+ toDomEventName,
5347
+ toReactEventName,
5348
+ toggleClass,
2537
5349
  updateStyles,
5350
+ updateTailwindConfig,
2538
5351
  validateAssertion,
2539
5352
  validateProps
2540
- };
5353
+ });