@servlyadmin/runtime-core 0.1.9 → 0.1.11

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.mjs DELETED
@@ -1,4849 +0,0 @@
1
- import {
2
- DEFAULT_SERVLY_TAILWIND_CONFIG,
3
- addCustomStyles,
4
- getTailwind,
5
- initServlyTailwind,
6
- injectTailwind,
7
- isTailwindLoaded,
8
- removeCustomStyles,
9
- removeTailwind,
10
- updateTailwindConfig
11
- } from "./chunk-RKUT63EF.mjs";
12
- import {
13
- buildRegistryFromBundle,
14
- collectAllDependencies,
15
- createRegistry,
16
- detectCircularDependencies,
17
- extractDependencies,
18
- extractDependenciesFromCode
19
- } from "./chunk-EQFZFPI7.mjs";
20
-
21
- // packages/runtime-core/src/analyticsTypes.ts
22
- var DEFAULT_ANALYTICS_CONFIG = {
23
- enabled: true,
24
- endpoint: "/api/v1/analytics/events",
25
- batchSize: 50,
26
- flushInterval: 3e4,
27
- // 30 seconds
28
- sampleRate: 1,
29
- environment: "production",
30
- debug: false
31
- };
32
- var MAX_ERROR_MESSAGE_LENGTH = 1e3;
33
- var MAX_STACK_TRACE_LENGTH = 500;
34
- var MAX_QUEUE_SIZE = 500;
35
- var MAX_RETRY_ATTEMPTS = 3;
36
- var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
37
- var SDK_VERSION = "1.0.0";
38
-
39
- // packages/runtime-core/src/sessionManager.ts
40
- var SESSION_STORAGE_KEY = "servly_analytics_session";
41
- function generateUUID() {
42
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
43
- return crypto.randomUUID();
44
- }
45
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
46
- const r = Math.random() * 16 | 0;
47
- const v = c === "x" ? r : r & 3 | 8;
48
- return v.toString(16);
49
- });
50
- }
51
- function isSessionStorageAvailable() {
52
- try {
53
- if (typeof sessionStorage === "undefined") {
54
- return false;
55
- }
56
- const testKey = "__servly_test__";
57
- sessionStorage.setItem(testKey, "test");
58
- sessionStorage.removeItem(testKey);
59
- return true;
60
- } catch {
61
- return false;
62
- }
63
- }
64
- function loadSession() {
65
- if (!isSessionStorageAvailable()) {
66
- return null;
67
- }
68
- try {
69
- const stored = sessionStorage.getItem(SESSION_STORAGE_KEY);
70
- if (!stored) {
71
- return null;
72
- }
73
- return JSON.parse(stored);
74
- } catch {
75
- return null;
76
- }
77
- }
78
- function saveSession(session) {
79
- if (!isSessionStorageAvailable()) {
80
- return;
81
- }
82
- try {
83
- sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(session));
84
- } catch {
85
- }
86
- }
87
- function clearSession() {
88
- if (!isSessionStorageAvailable()) {
89
- return;
90
- }
91
- try {
92
- sessionStorage.removeItem(SESSION_STORAGE_KEY);
93
- } catch {
94
- }
95
- }
96
- function isSessionExpired(session) {
97
- const now = Date.now();
98
- return now - session.lastActivityAt > SESSION_TIMEOUT_MS;
99
- }
100
- var SessionManager = class {
101
- session = null;
102
- constructor() {
103
- this.initialize();
104
- }
105
- /**
106
- * Initialize session manager
107
- */
108
- initialize() {
109
- const stored = loadSession();
110
- if (stored && !isSessionExpired(stored)) {
111
- this.session = stored;
112
- this.touch();
113
- } else {
114
- this.createNewSession();
115
- }
116
- }
117
- /**
118
- * Create a new session
119
- */
120
- createNewSession() {
121
- const now = Date.now();
122
- this.session = {
123
- id: generateUUID(),
124
- createdAt: now,
125
- lastActivityAt: now
126
- };
127
- saveSession(this.session);
128
- }
129
- /**
130
- * Get current session ID
131
- * Creates new session if expired
132
- */
133
- getSessionId() {
134
- if (!this.session || isSessionExpired(this.session)) {
135
- this.createNewSession();
136
- }
137
- return this.session.id;
138
- }
139
- /**
140
- * Update last activity timestamp
141
- */
142
- touch() {
143
- if (this.session) {
144
- this.session.lastActivityAt = Date.now();
145
- saveSession(this.session);
146
- }
147
- }
148
- /**
149
- * Force create a new session
150
- */
151
- rotate() {
152
- this.createNewSession();
153
- }
154
- /**
155
- * Clear current session
156
- */
157
- clear() {
158
- this.session = null;
159
- clearSession();
160
- }
161
- /**
162
- * Get session info (for debugging)
163
- */
164
- getSessionInfo() {
165
- return this.session ? { ...this.session } : null;
166
- }
167
- };
168
- var sessionManagerInstance = null;
169
- function getSessionManager() {
170
- if (!sessionManagerInstance) {
171
- sessionManagerInstance = new SessionManager();
172
- }
173
- return sessionManagerInstance;
174
- }
175
- function resetSessionManager() {
176
- if (sessionManagerInstance) {
177
- sessionManagerInstance.clear();
178
- }
179
- sessionManagerInstance = null;
180
- }
181
-
182
- // packages/runtime-core/src/analytics.ts
183
- var AnalyticsCollector = class {
184
- config;
185
- eventQueue = [];
186
- flushTimer = null;
187
- isEnabled;
188
- isFlushing = false;
189
- retryDelay = 1e3;
190
- constructor(config) {
191
- this.config = { ...DEFAULT_ANALYTICS_CONFIG, ...config };
192
- this.isEnabled = this.config.enabled;
193
- this.startFlushTimer();
194
- }
195
- // ============================================
196
- // Configuration
197
- // ============================================
198
- /**
199
- * Configure analytics
200
- */
201
- configure(config) {
202
- this.config = { ...this.config, ...config };
203
- this.isEnabled = this.config.enabled;
204
- this.stopFlushTimer();
205
- if (this.isEnabled) {
206
- this.startFlushTimer();
207
- }
208
- }
209
- /**
210
- * Disable analytics collection
211
- */
212
- disable() {
213
- this.isEnabled = false;
214
- this.stopFlushTimer();
215
- this.eventQueue = [];
216
- }
217
- /**
218
- * Enable analytics collection
219
- */
220
- enable() {
221
- this.isEnabled = true;
222
- this.startFlushTimer();
223
- }
224
- /**
225
- * Destroy and cleanup
226
- */
227
- destroy() {
228
- this.disable();
229
- }
230
- // ============================================
231
- // Event Tracking
232
- // ============================================
233
- /**
234
- * Track component render
235
- */
236
- trackRender(componentId, version, duration, metadata) {
237
- if (!this.shouldTrack()) {
238
- return;
239
- }
240
- const event = {
241
- type: "render",
242
- componentId,
243
- version,
244
- timestamp: Date.now(),
245
- sessionId: getSessionManager().getSessionId(),
246
- appId: this.config.appId,
247
- duration: Math.round(duration),
248
- metadata
249
- };
250
- this.queueEvent(event);
251
- }
252
- /**
253
- * Track component fetch
254
- */
255
- trackFetch(componentId, version, duration, fromCache, metadata) {
256
- if (!this.shouldTrack()) {
257
- return;
258
- }
259
- const event = {
260
- type: "fetch",
261
- componentId,
262
- version,
263
- timestamp: Date.now(),
264
- sessionId: getSessionManager().getSessionId(),
265
- appId: this.config.appId,
266
- duration: Math.round(duration),
267
- metadata: {
268
- cacheHit: fromCache,
269
- fetchDuration: Math.round(duration),
270
- ...metadata
271
- }
272
- };
273
- this.queueEvent(event);
274
- }
275
- /**
276
- * Track error
277
- */
278
- trackError(componentId, version, error, context) {
279
- if (!this.shouldTrack()) {
280
- return;
281
- }
282
- const errorMessage = this.truncateString(
283
- error.message || "Unknown error",
284
- MAX_ERROR_MESSAGE_LENGTH
285
- );
286
- const stackTrace = error.stack ? this.truncateString(error.stack, MAX_STACK_TRACE_LENGTH) : void 0;
287
- const event = {
288
- type: "error",
289
- componentId,
290
- version,
291
- timestamp: Date.now(),
292
- sessionId: getSessionManager().getSessionId(),
293
- appId: this.config.appId,
294
- metadata: {
295
- errorType: context?.errorType || "render",
296
- errorMessage,
297
- stackTrace,
298
- bindingPath: context?.bindingPath,
299
- elementId: context?.elementId
300
- }
301
- };
302
- this.queueEvent(event);
303
- }
304
- // ============================================
305
- // Queue Management
306
- // ============================================
307
- /**
308
- * Check if event should be tracked (sampling + enabled)
309
- */
310
- shouldTrack() {
311
- if (!this.isEnabled) {
312
- return false;
313
- }
314
- if (this.config.sampleRate < 1) {
315
- return Math.random() < this.config.sampleRate;
316
- }
317
- return true;
318
- }
319
- /**
320
- * Add event to queue
321
- */
322
- queueEvent(event) {
323
- if (this.eventQueue.length >= MAX_QUEUE_SIZE) {
324
- this.eventQueue.shift();
325
- this.log("Queue full, dropping oldest event");
326
- }
327
- this.eventQueue.push({
328
- event,
329
- retryCount: 0,
330
- addedAt: Date.now()
331
- });
332
- if (this.eventQueue.length >= this.config.batchSize) {
333
- this.flush();
334
- }
335
- }
336
- /**
337
- * Get current queue size
338
- */
339
- getQueueSize() {
340
- return this.eventQueue.length;
341
- }
342
- // ============================================
343
- // Flush Logic
344
- // ============================================
345
- /**
346
- * Start the flush timer
347
- */
348
- startFlushTimer() {
349
- if (this.flushTimer) {
350
- return;
351
- }
352
- this.flushTimer = setInterval(() => {
353
- this.flush();
354
- }, this.config.flushInterval);
355
- }
356
- /**
357
- * Stop the flush timer
358
- */
359
- stopFlushTimer() {
360
- if (this.flushTimer) {
361
- clearInterval(this.flushTimer);
362
- this.flushTimer = null;
363
- }
364
- }
365
- /**
366
- * Flush events to server
367
- */
368
- async flush() {
369
- if (this.isFlushing || this.eventQueue.length === 0) {
370
- return;
371
- }
372
- this.isFlushing = true;
373
- if (typeof requestIdleCallback !== "undefined") {
374
- requestIdleCallback(
375
- () => {
376
- this.doFlush();
377
- },
378
- { timeout: 5e3 }
379
- );
380
- } else {
381
- setTimeout(() => {
382
- this.doFlush();
383
- }, 0);
384
- }
385
- }
386
- /**
387
- * Perform the actual flush
388
- */
389
- async doFlush() {
390
- const eventsToSend = this.eventQueue.splice(0, this.config.batchSize);
391
- if (eventsToSend.length === 0) {
392
- this.isFlushing = false;
393
- return;
394
- }
395
- const request = {
396
- events: eventsToSend.map((q) => q.event),
397
- clientInfo: {
398
- sdkVersion: SDK_VERSION,
399
- environment: this.config.environment
400
- }
401
- };
402
- try {
403
- const response = await this.sendEvents(request);
404
- if (response.success) {
405
- this.log(`Flushed ${response.accepted} events`);
406
- this.retryDelay = 1e3;
407
- } else {
408
- this.handleFailedEvents(eventsToSend, response);
409
- }
410
- } catch (error) {
411
- this.handleNetworkError(eventsToSend, error);
412
- } finally {
413
- this.isFlushing = false;
414
- }
415
- }
416
- /**
417
- * Send events to the API
418
- */
419
- async sendEvents(request) {
420
- const headers = {
421
- "Content-Type": "application/json"
422
- };
423
- if (this.config.apiKey) {
424
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
425
- }
426
- const response = await fetch(this.config.endpoint, {
427
- method: "POST",
428
- headers,
429
- body: JSON.stringify(request)
430
- });
431
- if (response.status === 429) {
432
- const retryAfter = parseInt(response.headers.get("Retry-After") || "60", 10);
433
- return {
434
- success: false,
435
- accepted: 0,
436
- rejected: request.events.length,
437
- retryAfter
438
- };
439
- }
440
- if (!response.ok) {
441
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
442
- }
443
- return await response.json();
444
- }
445
- /**
446
- * Handle failed events (partial success)
447
- */
448
- handleFailedEvents(events, response) {
449
- const eventsToRetry = events.filter((e) => e.retryCount < MAX_RETRY_ATTEMPTS);
450
- eventsToRetry.forEach((e) => {
451
- e.retryCount++;
452
- this.eventQueue.unshift(e);
453
- });
454
- const dropped = events.length - eventsToRetry.length;
455
- if (dropped > 0) {
456
- this.log(`Dropped ${dropped} events after max retries`);
457
- }
458
- if (response.retryAfter) {
459
- this.scheduleRetry(response.retryAfter * 1e3);
460
- }
461
- }
462
- /**
463
- * Handle network error
464
- */
465
- handleNetworkError(events, error) {
466
- this.log(`Network error: ${error}`);
467
- const eventsToRetry = events.filter((e) => e.retryCount < MAX_RETRY_ATTEMPTS);
468
- eventsToRetry.forEach((e) => {
469
- e.retryCount++;
470
- this.eventQueue.unshift(e);
471
- });
472
- this.scheduleRetry(this.retryDelay);
473
- this.retryDelay = Math.min(this.retryDelay * 2, 3e4);
474
- }
475
- /**
476
- * Schedule a retry flush
477
- */
478
- scheduleRetry(delayMs) {
479
- setTimeout(() => {
480
- this.flush();
481
- }, delayMs);
482
- }
483
- // ============================================
484
- // Utilities
485
- // ============================================
486
- /**
487
- * Truncate string to max length
488
- */
489
- truncateString(str, maxLength) {
490
- if (str.length <= maxLength) {
491
- return str;
492
- }
493
- return str.substring(0, maxLength - 3) + "...";
494
- }
495
- /**
496
- * Log debug message
497
- */
498
- log(message) {
499
- if (this.config.debug) {
500
- console.log(`[Analytics] ${message}`);
501
- }
502
- }
503
- };
504
- var analyticsInstance = null;
505
- function getAnalytics() {
506
- if (!analyticsInstance) {
507
- analyticsInstance = new AnalyticsCollector();
508
- }
509
- return analyticsInstance;
510
- }
511
- function configureAnalytics(config) {
512
- getAnalytics().configure(config);
513
- }
514
- function resetAnalytics() {
515
- if (analyticsInstance) {
516
- analyticsInstance.destroy();
517
- }
518
- analyticsInstance = null;
519
- }
520
- var analytics = {
521
- get instance() {
522
- return getAnalytics();
523
- },
524
- configure: configureAnalytics,
525
- trackRender: (componentId, version, duration, metadata) => getAnalytics().trackRender(componentId, version, duration, metadata),
526
- trackFetch: (componentId, version, duration, fromCache, metadata) => getAnalytics().trackFetch(componentId, version, duration, fromCache, metadata),
527
- trackError: (componentId, version, error, context) => getAnalytics().trackError(componentId, version, error, context),
528
- flush: () => getAnalytics().flush(),
529
- disable: () => getAnalytics().disable(),
530
- enable: () => getAnalytics().enable()
531
- };
532
-
533
- // packages/runtime-core/src/bindings.ts
534
- var BINDING_SOURCES = [
535
- "props",
536
- "state",
537
- "appState",
538
- "context",
539
- "input",
540
- "currentItem",
541
- "localStore",
542
- "localStorage",
543
- "sessionStorage",
544
- "config",
545
- "element",
546
- "self",
547
- "params",
548
- "query",
549
- "window",
550
- "bindings",
551
- "binding",
552
- "boundInputs",
553
- "parent"
554
- ];
555
- var TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g;
556
- function hasTemplateSyntax(value) {
557
- return typeof value === "string" && value.includes("{{") && value.includes("}}");
558
- }
559
- function getLocalStorageValue(key) {
560
- if (typeof localStorage === "undefined") return void 0;
561
- try {
562
- const stored = localStorage.getItem(key);
563
- if (stored === null) return void 0;
564
- try {
565
- return JSON.parse(stored);
566
- } catch {
567
- return stored;
568
- }
569
- } catch {
570
- return void 0;
571
- }
572
- }
573
- function getSessionStorageValue(key) {
574
- if (typeof sessionStorage === "undefined") return void 0;
575
- try {
576
- const stored = sessionStorage.getItem(key);
577
- if (stored === null) return void 0;
578
- try {
579
- return JSON.parse(stored);
580
- } catch {
581
- return stored;
582
- }
583
- } catch {
584
- return void 0;
585
- }
586
- }
587
- function getWindowInfo() {
588
- if (typeof window === "undefined") return {};
589
- const searchParams = {};
590
- try {
591
- const urlSearchParams = new URLSearchParams(window.location.search);
592
- urlSearchParams.forEach((value, key) => {
593
- searchParams[key] = value;
594
- });
595
- } catch {
596
- }
597
- return {
598
- href: window.location.href,
599
- pathname: window.location.pathname,
600
- search: window.location.search,
601
- hash: window.location.hash,
602
- origin: window.location.origin,
603
- protocol: window.location.protocol,
604
- host: window.location.host,
605
- hostname: window.location.hostname,
606
- port: window.location.port,
607
- searchParams,
608
- params: searchParams,
609
- innerWidth: window.innerWidth,
610
- innerHeight: window.innerHeight,
611
- screenWidth: window.screen?.width,
612
- screenHeight: window.screen?.height
613
- };
614
- }
615
- function navigatePath(obj, parts) {
616
- let current = obj;
617
- for (const part of parts) {
618
- if (current === null || current === void 0) return void 0;
619
- const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
620
- if (arrayMatch) {
621
- const [, propName, indexStr] = arrayMatch;
622
- current = current[propName];
623
- if (!Array.isArray(current)) return void 0;
624
- current = current[parseInt(indexStr, 10)];
625
- } else {
626
- current = current[part];
627
- }
628
- }
629
- return current;
630
- }
631
- function resolveBindingPath(path, context) {
632
- const trimmed = path.trim();
633
- const parts = trimmed.split(".");
634
- if (parts.length === 0) {
635
- return void 0;
636
- }
637
- const prefix = parts[0].toLowerCase();
638
- if (prefix === "localstore" || prefix === "localstorage") {
639
- if (parts.length === 1) {
640
- const all = {};
641
- if (typeof localStorage !== "undefined") {
642
- for (let i = 0; i < localStorage.length; i++) {
643
- const key2 = localStorage.key(i);
644
- if (key2) {
645
- all[key2] = getLocalStorageValue(key2);
646
- }
647
- }
648
- }
649
- return all;
650
- }
651
- const key = parts[1];
652
- const value = getLocalStorageValue(key);
653
- if (parts.length === 2) return value;
654
- return navigatePath(value, parts.slice(2));
655
- }
656
- if (prefix === "sessionstorage") {
657
- if (parts.length === 1) {
658
- const all = {};
659
- if (typeof sessionStorage !== "undefined") {
660
- for (let i = 0; i < sessionStorage.length; i++) {
661
- const key2 = sessionStorage.key(i);
662
- if (key2) {
663
- all[key2] = getSessionStorageValue(key2);
664
- }
665
- }
666
- }
667
- return all;
668
- }
669
- const key = parts[1];
670
- const value = getSessionStorageValue(key);
671
- if (parts.length === 2) return value;
672
- return navigatePath(value, parts.slice(2));
673
- }
674
- if (prefix === "window" || prefix === "url") {
675
- const windowInfo = getWindowInfo();
676
- if (parts.length === 1) return windowInfo;
677
- return navigatePath(windowInfo, parts.slice(1));
678
- }
679
- if (prefix === "params" || prefix === "query") {
680
- const windowInfo = getWindowInfo();
681
- const params = windowInfo.searchParams || {};
682
- if (parts.length === 1) return params;
683
- return navigatePath(params, parts.slice(1));
684
- }
685
- let source;
686
- let startIndex = 0;
687
- if (prefix === "props" || prefix === "input" || prefix === "bindings" || prefix === "binding" || prefix === "boundinputs" || prefix === "parent") {
688
- source = context.props;
689
- startIndex = 1;
690
- } else if (prefix === "state" || prefix === "appstate") {
691
- source = context.state;
692
- startIndex = 1;
693
- } else if (prefix === "context" || prefix === "config") {
694
- source = context.context;
695
- startIndex = 1;
696
- } else if (prefix === "currentitem") {
697
- source = context.props;
698
- startIndex = 0;
699
- } else if (prefix === "self" || prefix === "element") {
700
- source = context.state;
701
- startIndex = 1;
702
- } else if (BINDING_SOURCES.includes(prefix)) {
703
- source = context.props;
704
- startIndex = 1;
705
- } else {
706
- source = context.props;
707
- startIndex = 0;
708
- }
709
- if (!source) {
710
- return void 0;
711
- }
712
- return navigatePath(source, parts.slice(startIndex));
713
- }
714
- function resolveExpression(expression, context) {
715
- const trimmed = expression.trim();
716
- const comparisonOperators = ["===", "!==", "==", "!=", ">=", "<=", ">", "<"];
717
- for (const op of comparisonOperators) {
718
- if (trimmed.includes(op)) {
719
- const [left, right] = trimmed.split(op, 2);
720
- const leftVal = resolveExpressionValue(left.trim(), context);
721
- const rightVal = resolveExpressionValue(right.trim(), context);
722
- switch (op) {
723
- case "===":
724
- return leftVal === rightVal;
725
- case "!==":
726
- return leftVal !== rightVal;
727
- case "==":
728
- return leftVal == rightVal;
729
- case "!=":
730
- return leftVal != rightVal;
731
- case ">":
732
- return leftVal > rightVal;
733
- case "<":
734
- return leftVal < rightVal;
735
- case ">=":
736
- return leftVal >= rightVal;
737
- case "<=":
738
- return leftVal <= rightVal;
739
- }
740
- }
741
- }
742
- if (trimmed.startsWith("!") && !trimmed.startsWith("!=")) {
743
- const innerValue = resolveExpression(trimmed.slice(1).trim(), context);
744
- return !innerValue;
745
- }
746
- if (trimmed.includes("||")) {
747
- const parts = trimmed.split("||");
748
- for (let i = 0; i < parts.length; i++) {
749
- const part = parts[i].trim();
750
- const value = resolveExpressionValue(part, context);
751
- if (value || i === parts.length - 1) {
752
- return value;
753
- }
754
- }
755
- }
756
- if (trimmed.includes("&&")) {
757
- const parts = trimmed.split("&&");
758
- for (const part of parts) {
759
- const value = resolveExpression(part.trim(), context);
760
- if (!value) return value;
761
- }
762
- return resolveExpression(parts[parts.length - 1].trim(), context);
763
- }
764
- const ternaryMatch = trimmed.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
765
- if (ternaryMatch) {
766
- const condition = resolveExpression(ternaryMatch[1].trim(), context);
767
- if (condition) {
768
- return resolveTernaryValue(ternaryMatch[2].trim(), context);
769
- }
770
- return resolveTernaryValue(ternaryMatch[3].trim(), context);
771
- }
772
- return resolveExpressionValue(trimmed, context);
773
- }
774
- function resolveExpressionValue(value, context) {
775
- const trimmed = value.trim();
776
- if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
777
- return trimmed.slice(1, -1);
778
- }
779
- if (!isNaN(Number(trimmed)) && trimmed !== "") {
780
- return Number(trimmed);
781
- }
782
- if (trimmed === "true") return true;
783
- if (trimmed === "false") return false;
784
- if (trimmed === "null") return null;
785
- if (trimmed === "undefined") return void 0;
786
- return resolveBindingPath(trimmed, context);
787
- }
788
- function resolveTernaryValue(value, context) {
789
- if (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"')) {
790
- return value.slice(1, -1);
791
- }
792
- if (!isNaN(Number(value))) {
793
- return Number(value);
794
- }
795
- if (value === "true") return true;
796
- if (value === "false") return false;
797
- if (value === "null") return null;
798
- if (value === "undefined") return void 0;
799
- return resolveExpression(value, context);
800
- }
801
- function resolveTemplate(template, context, componentId) {
802
- if (!template || typeof template !== "string") {
803
- return template;
804
- }
805
- if (!hasTemplateSyntax(template)) {
806
- return template;
807
- }
808
- try {
809
- const singleMatch = template.match(/^\{\{([^}]+)\}\}$/);
810
- if (singleMatch) {
811
- const value = resolveExpression(singleMatch[1], context);
812
- if (value === void 0 || value === null) {
813
- return "";
814
- }
815
- return String(value);
816
- }
817
- return template.replace(TEMPLATE_REGEX, (_match, expression) => {
818
- const value = resolveExpression(expression, context);
819
- if (value === void 0 || value === null) {
820
- return "";
821
- }
822
- if (typeof value === "object") {
823
- return JSON.stringify(value);
824
- }
825
- return String(value);
826
- });
827
- } catch (error) {
828
- if (componentId) {
829
- analytics.trackError(componentId, "latest", error, {
830
- errorType: "binding",
831
- bindingPath: template
832
- });
833
- }
834
- return "";
835
- }
836
- }
837
- function resolveTemplateValue(template, context, componentId) {
838
- if (!template || typeof template !== "string") {
839
- return template;
840
- }
841
- if (!hasTemplateSyntax(template)) {
842
- return template;
843
- }
844
- try {
845
- const singleMatch = template.match(/^\{\{([^}]+)\}\}$/);
846
- if (singleMatch) {
847
- return resolveExpression(singleMatch[1], context);
848
- }
849
- return resolveTemplate(template, context, componentId);
850
- } catch (error) {
851
- if (componentId) {
852
- analytics.trackError(componentId, "latest", error, {
853
- errorType: "binding",
854
- bindingPath: template
855
- });
856
- }
857
- return void 0;
858
- }
859
- }
860
- function isPlainObject(value) {
861
- return Object.prototype.toString.call(value) === "[object Object]";
862
- }
863
- function resolveTemplatesDeep(input, context) {
864
- if (typeof input === "string") {
865
- return resolveTemplateValue(input, context);
866
- }
867
- if (Array.isArray(input)) {
868
- let changed = false;
869
- const result = input.map((item) => {
870
- const resolved = resolveTemplatesDeep(item, context);
871
- if (resolved !== item) {
872
- changed = true;
873
- }
874
- return resolved;
875
- });
876
- return changed ? result : input;
877
- }
878
- if (input && typeof input === "object") {
879
- if (!isPlainObject(input)) {
880
- return input;
881
- }
882
- let mutable = null;
883
- const original = input;
884
- for (const key of Object.keys(original)) {
885
- const current = original[key];
886
- const resolved = resolveTemplatesDeep(current, context);
887
- if (resolved !== current) {
888
- const target = mutable ?? (mutable = { ...original });
889
- target[key] = resolved;
890
- }
891
- }
892
- return mutable ?? input;
893
- }
894
- return input;
895
- }
896
- function extractBindingKeys(template) {
897
- if (!template || typeof template !== "string") {
898
- return [];
899
- }
900
- const keys = /* @__PURE__ */ new Set();
901
- const matches = template.matchAll(TEMPLATE_REGEX);
902
- for (const match of matches) {
903
- const expression = match[1].trim();
904
- const pathMatch = expression.match(/^([a-zA-Z_][a-zA-Z0-9_.]*)/);
905
- if (pathMatch) {
906
- const parts = pathMatch[1].split(".");
907
- if (parts.length > 1 && BINDING_SOURCES.includes(parts[0].toLowerCase())) {
908
- keys.add(parts[parts.length - 1]);
909
- } else {
910
- keys.add(parts[0]);
911
- }
912
- }
913
- }
914
- return Array.from(keys);
915
- }
916
-
917
- // packages/runtime-core/src/styles.ts
918
- var UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
919
- "animationIterationCount",
920
- "borderImageOutset",
921
- "borderImageSlice",
922
- "borderImageWidth",
923
- "boxFlex",
924
- "boxFlexGroup",
925
- "boxOrdinalGroup",
926
- "columnCount",
927
- "columns",
928
- "flex",
929
- "flexGrow",
930
- "flexPositive",
931
- "flexShrink",
932
- "flexNegative",
933
- "flexOrder",
934
- "gridRow",
935
- "gridRowEnd",
936
- "gridRowSpan",
937
- "gridRowStart",
938
- "gridColumn",
939
- "gridColumnEnd",
940
- "gridColumnSpan",
941
- "gridColumnStart",
942
- "fontWeight",
943
- "lineClamp",
944
- "lineHeight",
945
- "opacity",
946
- "order",
947
- "orphans",
948
- "tabSize",
949
- "widows",
950
- "zIndex",
951
- "zoom",
952
- "fillOpacity",
953
- "floodOpacity",
954
- "stopOpacity",
955
- "strokeDasharray",
956
- "strokeDashoffset",
957
- "strokeMiterlimit",
958
- "strokeOpacity",
959
- "strokeWidth"
960
- ]);
961
- function camelToKebab(str) {
962
- return str.replace(/([A-Z])/g, "-$1").toLowerCase();
963
- }
964
- function needsUnits(property) {
965
- if (property.startsWith("--")) {
966
- return false;
967
- }
968
- return !UNITLESS_PROPERTIES.has(property);
969
- }
970
- function formatStyleValue(property, value) {
971
- if (value === null || value === void 0) {
972
- return "";
973
- }
974
- if (typeof value === "number" && needsUnits(property)) {
975
- return `${value}px`;
976
- }
977
- return String(value);
978
- }
979
- function processStyles(style, context) {
980
- if (!style || Object.keys(style).length === 0) {
981
- return {};
982
- }
983
- const processed = {};
984
- for (const [key, value] of Object.entries(style)) {
985
- if (value === void 0 || value === null || value === "") {
986
- continue;
987
- }
988
- let resolvedValue = value;
989
- if (typeof value === "string" && hasTemplateSyntax(value)) {
990
- resolvedValue = resolveTemplate(value, context);
991
- }
992
- if (resolvedValue === void 0 || resolvedValue === null || resolvedValue === "") {
993
- continue;
994
- }
995
- const formattedValue = formatStyleValue(key, resolvedValue);
996
- if (formattedValue) {
997
- processed[key] = formattedValue;
998
- }
999
- }
1000
- return processed;
1001
- }
1002
- function applyStyles(element, styles) {
1003
- for (const [property, value] of Object.entries(styles)) {
1004
- if (property.startsWith("--")) {
1005
- element.style.setProperty(property, value);
1006
- } else {
1007
- element.style[property] = value;
1008
- }
1009
- }
1010
- }
1011
- var CANVAS_ONLY_STYLES = /* @__PURE__ */ new Set([
1012
- "transform",
1013
- "--translate-x",
1014
- "--translate-y",
1015
- "--width",
1016
- "--height",
1017
- "z-index",
1018
- "zIndex"
1019
- ]);
1020
- function filterCanvasStyles(style) {
1021
- const filtered = {};
1022
- for (const [key, value] of Object.entries(style)) {
1023
- if (CANVAS_ONLY_STYLES.has(key)) {
1024
- continue;
1025
- }
1026
- if (key === "transform" && typeof value === "string" && value.includes("translate(")) {
1027
- continue;
1028
- }
1029
- filtered[key] = value;
1030
- }
1031
- return filtered;
1032
- }
1033
- function buildElementStyles(element, context) {
1034
- const config = element.configuration || {};
1035
- const combined = {};
1036
- if (element.style) {
1037
- Object.assign(combined, filterCanvasStyles(element.style));
1038
- }
1039
- if (config.style) {
1040
- Object.assign(combined, filterCanvasStyles(config.style));
1041
- }
1042
- if (config.cssVariables) {
1043
- Object.assign(combined, filterCanvasStyles(config.cssVariables));
1044
- }
1045
- return processStyles(combined, context);
1046
- }
1047
- function buildClassName(element, context) {
1048
- const config = element.configuration || {};
1049
- const classes = [];
1050
- if (element.className) {
1051
- classes.push(element.className);
1052
- }
1053
- if (config.className) {
1054
- const resolved = hasTemplateSyntax(config.className) ? resolveTemplate(config.className, context) : config.className;
1055
- if (resolved) classes.push(resolved);
1056
- }
1057
- if (config.classNames) {
1058
- const resolved = hasTemplateSyntax(config.classNames) ? resolveTemplate(config.classNames, context) : config.classNames;
1059
- if (resolved) classes.push(resolved);
1060
- }
1061
- if (config.dynamicClassName) {
1062
- const resolved = resolveTemplate(config.dynamicClassName, context);
1063
- if (resolved) classes.push(resolved);
1064
- }
1065
- return classes.filter(Boolean).join(" ").trim();
1066
- }
1067
- function clearStyles(element) {
1068
- element.removeAttribute("style");
1069
- }
1070
- function updateStyles(element, oldStyles, newStyles) {
1071
- for (const property of Object.keys(oldStyles)) {
1072
- if (!(property in newStyles)) {
1073
- if (property.startsWith("--")) {
1074
- element.style.removeProperty(property);
1075
- } else {
1076
- element.style[property] = "";
1077
- }
1078
- }
1079
- }
1080
- for (const [property, value] of Object.entries(newStyles)) {
1081
- if (oldStyles[property] !== value) {
1082
- if (property.startsWith("--")) {
1083
- element.style.setProperty(property, value);
1084
- } else {
1085
- element.style[property] = value;
1086
- }
1087
- }
1088
- }
1089
- }
1090
-
1091
- // packages/runtime-core/src/memorySampler.ts
1092
- var MemorySampler = class {
1093
- isSupported;
1094
- constructor() {
1095
- this.isSupported = this.checkSupport();
1096
- }
1097
- /**
1098
- * Check if memory API is available
1099
- */
1100
- checkSupport() {
1101
- if (typeof performance === "undefined") {
1102
- return false;
1103
- }
1104
- const perf = performance;
1105
- return typeof perf.memory !== "undefined";
1106
- }
1107
- /**
1108
- * Check if memory sampling is available
1109
- */
1110
- isAvailable() {
1111
- return this.isSupported;
1112
- }
1113
- /**
1114
- * Take a memory sample
1115
- * Returns null if memory API is not available
1116
- */
1117
- sample() {
1118
- if (!this.isSupported) {
1119
- return null;
1120
- }
1121
- const perf = performance;
1122
- if (!perf.memory) {
1123
- return null;
1124
- }
1125
- return {
1126
- heapUsedKB: Math.round(perf.memory.usedJSHeapSize / 1024),
1127
- heapTotalKB: Math.round(perf.memory.totalJSHeapSize / 1024),
1128
- timestamp: Date.now()
1129
- };
1130
- }
1131
- /**
1132
- * Calculate heap delta between two samples
1133
- * Returns the difference in KB (positive = memory increased)
1134
- */
1135
- calculateDelta(before, after) {
1136
- if (!before || !after) {
1137
- return null;
1138
- }
1139
- return after.heapUsedKB - before.heapUsedKB;
1140
- }
1141
- };
1142
- var memorySamplerInstance = null;
1143
- function getMemorySampler() {
1144
- if (!memorySamplerInstance) {
1145
- memorySamplerInstance = new MemorySampler();
1146
- }
1147
- return memorySamplerInstance;
1148
- }
1149
- function resetMemorySampler() {
1150
- memorySamplerInstance = null;
1151
- }
1152
-
1153
- // packages/runtime-core/src/longTaskObserver.ts
1154
- var LongTaskObserver = class {
1155
- observer = null;
1156
- longTaskCount = 0;
1157
- isSupported;
1158
- isObserving = false;
1159
- constructor() {
1160
- this.isSupported = this.checkSupport();
1161
- }
1162
- /**
1163
- * Check if PerformanceObserver with longtask support is available
1164
- */
1165
- checkSupport() {
1166
- if (typeof PerformanceObserver === "undefined") {
1167
- return false;
1168
- }
1169
- try {
1170
- const supportedTypes = PerformanceObserver.supportedEntryTypes;
1171
- return Array.isArray(supportedTypes) && supportedTypes.includes("longtask");
1172
- } catch {
1173
- return false;
1174
- }
1175
- }
1176
- /**
1177
- * Check if long task observation is available
1178
- */
1179
- isAvailable() {
1180
- return this.isSupported;
1181
- }
1182
- /**
1183
- * Start observing long tasks
1184
- */
1185
- start() {
1186
- if (!this.isSupported || this.isObserving) {
1187
- return;
1188
- }
1189
- this.longTaskCount = 0;
1190
- try {
1191
- this.observer = new PerformanceObserver((list) => {
1192
- const entries = list.getEntries();
1193
- this.longTaskCount += entries.length;
1194
- });
1195
- this.observer.observe({ entryTypes: ["longtask"] });
1196
- this.isObserving = true;
1197
- } catch {
1198
- this.isSupported = false;
1199
- }
1200
- }
1201
- /**
1202
- * Stop observing and return the count of long tasks
1203
- */
1204
- stop() {
1205
- if (!this.isObserving || !this.observer) {
1206
- return this.longTaskCount;
1207
- }
1208
- try {
1209
- this.observer.disconnect();
1210
- } catch {
1211
- }
1212
- this.observer = null;
1213
- this.isObserving = false;
1214
- return this.longTaskCount;
1215
- }
1216
- /**
1217
- * Reset the long task counter
1218
- */
1219
- reset() {
1220
- this.longTaskCount = 0;
1221
- }
1222
- /**
1223
- * Get current count without stopping observation
1224
- */
1225
- getCount() {
1226
- return this.longTaskCount;
1227
- }
1228
- /**
1229
- * Check if currently observing
1230
- */
1231
- isActive() {
1232
- return this.isObserving;
1233
- }
1234
- };
1235
- var longTaskObserverInstance = null;
1236
- function getLongTaskObserver() {
1237
- if (!longTaskObserverInstance) {
1238
- longTaskObserverInstance = new LongTaskObserver();
1239
- }
1240
- return longTaskObserverInstance;
1241
- }
1242
- function resetLongTaskObserver() {
1243
- if (longTaskObserverInstance) {
1244
- longTaskObserverInstance.stop();
1245
- }
1246
- longTaskObserverInstance = null;
1247
- }
1248
-
1249
- // packages/runtime-core/src/stateManager.ts
1250
- var StateManager = class {
1251
- state = {};
1252
- listeners = /* @__PURE__ */ new Set();
1253
- config;
1254
- constructor(config = {}) {
1255
- this.config = config;
1256
- this.state = config.initialState || {};
1257
- if (config.persistToLocalStorage && typeof localStorage !== "undefined") {
1258
- this.loadFromLocalStorage();
1259
- }
1260
- if (config.onStateChange) {
1261
- this.listeners.add(config.onStateChange);
1262
- }
1263
- }
1264
- /**
1265
- * Get value by path from state
1266
- */
1267
- get(path) {
1268
- return getValueByPath(this.state, path);
1269
- }
1270
- /**
1271
- * Get the entire state object
1272
- */
1273
- getState() {
1274
- return { ...this.state };
1275
- }
1276
- /**
1277
- * Set a value in state
1278
- */
1279
- set(key, value, elementId) {
1280
- const previousValue = this.get(key);
1281
- setValueByPath(this.state, key, value);
1282
- this.notifyChange({
1283
- key,
1284
- value,
1285
- previousValue,
1286
- operation: "set",
1287
- elementId,
1288
- timestamp: Date.now()
1289
- });
1290
- }
1291
- /**
1292
- * Merge an object into state at the given path
1293
- */
1294
- merge(key, value, deep = false, elementId) {
1295
- const previousValue = this.get(key);
1296
- const currentValue = previousValue || {};
1297
- const newValue = deep ? deepMerge(currentValue, value) : { ...currentValue, ...value };
1298
- setValueByPath(this.state, key, newValue);
1299
- this.notifyChange({
1300
- key,
1301
- value: newValue,
1302
- previousValue,
1303
- operation: "merge",
1304
- elementId,
1305
- timestamp: Date.now()
1306
- });
1307
- }
1308
- /**
1309
- * Delete a key from state
1310
- */
1311
- delete(key, elementId) {
1312
- const previousValue = this.get(key);
1313
- deleteValueByPath(this.state, key);
1314
- this.notifyChange({
1315
- key,
1316
- value: void 0,
1317
- previousValue,
1318
- operation: "delete",
1319
- elementId,
1320
- timestamp: Date.now()
1321
- });
1322
- }
1323
- /**
1324
- * Append to an array in state
1325
- */
1326
- append(key, value, elementId) {
1327
- const previousValue = this.get(key);
1328
- const currentArray = Array.isArray(previousValue) ? [...previousValue] : [];
1329
- currentArray.push(value);
1330
- setValueByPath(this.state, key, currentArray);
1331
- this.notifyChange({
1332
- key,
1333
- value: currentArray,
1334
- previousValue,
1335
- operation: "append",
1336
- elementId,
1337
- timestamp: Date.now()
1338
- });
1339
- }
1340
- /**
1341
- * Prepend to an array in state
1342
- */
1343
- prepend(key, value, elementId) {
1344
- const previousValue = this.get(key);
1345
- const currentArray = Array.isArray(previousValue) ? [...previousValue] : [];
1346
- currentArray.unshift(value);
1347
- setValueByPath(this.state, key, currentArray);
1348
- this.notifyChange({
1349
- key,
1350
- value: currentArray,
1351
- previousValue,
1352
- operation: "prepend",
1353
- elementId,
1354
- timestamp: Date.now()
1355
- });
1356
- }
1357
- /**
1358
- * Toggle a boolean value in state
1359
- */
1360
- toggle(key, elementId) {
1361
- const previousValue = this.get(key);
1362
- const newValue = !previousValue;
1363
- setValueByPath(this.state, key, newValue);
1364
- this.notifyChange({
1365
- key,
1366
- value: newValue,
1367
- previousValue,
1368
- operation: "toggle",
1369
- elementId,
1370
- timestamp: Date.now()
1371
- });
1372
- }
1373
- /**
1374
- * Subscribe to state changes
1375
- */
1376
- subscribe(listener) {
1377
- this.listeners.add(listener);
1378
- return () => this.listeners.delete(listener);
1379
- }
1380
- /**
1381
- * Notify all listeners of a state change
1382
- */
1383
- notifyChange(event) {
1384
- if (this.config.persistToLocalStorage) {
1385
- this.saveToLocalStorage();
1386
- }
1387
- for (const listener of this.listeners) {
1388
- try {
1389
- listener(event);
1390
- } catch (error) {
1391
- console.error("State change listener error:", error);
1392
- }
1393
- }
1394
- }
1395
- /**
1396
- * Load state from localStorage
1397
- */
1398
- loadFromLocalStorage() {
1399
- try {
1400
- const prefix = this.config.localStoragePrefix || "servly_state_";
1401
- const stored = localStorage.getItem(`${prefix}state`);
1402
- if (stored) {
1403
- const parsed = JSON.parse(stored);
1404
- this.state = { ...this.state, ...parsed };
1405
- }
1406
- } catch (error) {
1407
- console.warn("Failed to load state from localStorage:", error);
1408
- }
1409
- }
1410
- /**
1411
- * Save state to localStorage
1412
- */
1413
- saveToLocalStorage() {
1414
- try {
1415
- const prefix = this.config.localStoragePrefix || "servly_state_";
1416
- localStorage.setItem(`${prefix}state`, JSON.stringify(this.state));
1417
- } catch (error) {
1418
- console.warn("Failed to save state to localStorage:", error);
1419
- }
1420
- }
1421
- /**
1422
- * Clear all state
1423
- */
1424
- clear() {
1425
- const previousState = { ...this.state };
1426
- this.state = {};
1427
- if (this.config.persistToLocalStorage) {
1428
- try {
1429
- const prefix = this.config.localStoragePrefix || "servly_state_";
1430
- localStorage.removeItem(`${prefix}state`);
1431
- } catch (error) {
1432
- console.warn("Failed to clear localStorage:", error);
1433
- }
1434
- }
1435
- this.notifyChange({
1436
- key: "",
1437
- value: {},
1438
- previousValue: previousState,
1439
- operation: "delete",
1440
- timestamp: Date.now()
1441
- });
1442
- }
1443
- };
1444
- function getValueByPath(obj, path) {
1445
- if (!obj || !path) return void 0;
1446
- const parts = path.split(".");
1447
- let current = obj;
1448
- for (const part of parts) {
1449
- if (current === null || current === void 0) return void 0;
1450
- const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1451
- if (arrayMatch) {
1452
- const [, propName, indexStr] = arrayMatch;
1453
- current = current[propName];
1454
- if (!Array.isArray(current)) return void 0;
1455
- current = current[parseInt(indexStr, 10)];
1456
- } else {
1457
- current = current[part];
1458
- }
1459
- }
1460
- return current;
1461
- }
1462
- function setValueByPath(obj, path, value) {
1463
- if (!obj || !path) return;
1464
- const parts = path.split(".");
1465
- let current = obj;
1466
- for (let i = 0; i < parts.length - 1; i++) {
1467
- const part = parts[i];
1468
- const arrayMatch2 = part.match(/^(\w+)\[(\d+)\]$/);
1469
- if (arrayMatch2) {
1470
- const [, propName, indexStr] = arrayMatch2;
1471
- if (!current[propName]) current[propName] = [];
1472
- const index = parseInt(indexStr, 10);
1473
- if (!current[propName][index]) current[propName][index] = {};
1474
- current = current[propName][index];
1475
- } else {
1476
- if (!current[part]) current[part] = {};
1477
- current = current[part];
1478
- }
1479
- }
1480
- const lastPart = parts[parts.length - 1];
1481
- const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
1482
- if (arrayMatch) {
1483
- const [, propName, indexStr] = arrayMatch;
1484
- if (!current[propName]) current[propName] = [];
1485
- current[propName][parseInt(indexStr, 10)] = value;
1486
- } else {
1487
- current[lastPart] = value;
1488
- }
1489
- }
1490
- function deleteValueByPath(obj, path) {
1491
- if (!obj || !path) return;
1492
- const parts = path.split(".");
1493
- let current = obj;
1494
- for (let i = 0; i < parts.length - 1; i++) {
1495
- const part = parts[i];
1496
- if (current[part] === void 0) return;
1497
- current = current[part];
1498
- }
1499
- const lastPart = parts[parts.length - 1];
1500
- delete current[lastPart];
1501
- }
1502
- function deepMerge(target, source) {
1503
- if (!source) return target;
1504
- if (!target) return source;
1505
- const result = { ...target };
1506
- for (const key of Object.keys(source)) {
1507
- if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
1508
- result[key] = deepMerge(target[key], source[key]);
1509
- } else {
1510
- result[key] = source[key];
1511
- }
1512
- }
1513
- return result;
1514
- }
1515
- function addClass(currentClasses, classToAdd) {
1516
- const classes = currentClasses.split(/\s+/).filter(Boolean);
1517
- if (!classes.includes(classToAdd)) {
1518
- classes.push(classToAdd);
1519
- }
1520
- return classes.join(" ");
1521
- }
1522
- function removeClass(currentClasses, classToRemove) {
1523
- return currentClasses.split(/\s+/).filter((cls) => cls && cls !== classToRemove).join(" ");
1524
- }
1525
- function toggleClass(currentClasses, classToToggle) {
1526
- const classes = currentClasses.split(/\s+/).filter(Boolean);
1527
- const index = classes.indexOf(classToToggle);
1528
- if (index > -1) {
1529
- classes.splice(index, 1);
1530
- } else {
1531
- classes.push(classToToggle);
1532
- }
1533
- return classes.join(" ");
1534
- }
1535
- function hasClass(currentClasses, classToCheck) {
1536
- return currentClasses.split(/\s+/).includes(classToCheck);
1537
- }
1538
- function getLocalStorage(key, defaultValue) {
1539
- if (typeof localStorage === "undefined") return defaultValue;
1540
- try {
1541
- const stored = localStorage.getItem(key);
1542
- if (stored === null) return defaultValue;
1543
- return JSON.parse(stored);
1544
- } catch {
1545
- return localStorage.getItem(key) ?? defaultValue;
1546
- }
1547
- }
1548
- function setLocalStorage(key, value) {
1549
- if (typeof localStorage === "undefined") return;
1550
- try {
1551
- localStorage.setItem(key, JSON.stringify(value));
1552
- } catch (error) {
1553
- console.warn("Failed to set localStorage:", error);
1554
- }
1555
- }
1556
- function removeLocalStorage(key) {
1557
- if (typeof localStorage === "undefined") return;
1558
- localStorage.removeItem(key);
1559
- }
1560
- function getSessionStorage(key, defaultValue) {
1561
- if (typeof sessionStorage === "undefined") return defaultValue;
1562
- try {
1563
- const stored = sessionStorage.getItem(key);
1564
- if (stored === null) return defaultValue;
1565
- return JSON.parse(stored);
1566
- } catch {
1567
- return sessionStorage.getItem(key) ?? defaultValue;
1568
- }
1569
- }
1570
- function setSessionStorage(key, value) {
1571
- if (typeof sessionStorage === "undefined") return;
1572
- try {
1573
- sessionStorage.setItem(key, JSON.stringify(value));
1574
- } catch (error) {
1575
- console.warn("Failed to set sessionStorage:", error);
1576
- }
1577
- }
1578
- function removeSessionStorage(key) {
1579
- if (typeof sessionStorage === "undefined") return;
1580
- sessionStorage.removeItem(key);
1581
- }
1582
- function navigateTo(url, options = {}) {
1583
- if (typeof window === "undefined") return;
1584
- const { replace = false, state, newTab = false } = options;
1585
- if (newTab) {
1586
- window.open(url, "_blank");
1587
- return;
1588
- }
1589
- if (url.startsWith("http://") || url.startsWith("https://")) {
1590
- if (replace) {
1591
- window.location.replace(url);
1592
- } else {
1593
- window.location.href = url;
1594
- }
1595
- return;
1596
- }
1597
- if (url.startsWith("#")) {
1598
- window.location.hash = url;
1599
- return;
1600
- }
1601
- if (replace) {
1602
- window.history.replaceState(state, "", url);
1603
- } else {
1604
- window.history.pushState(state, "", url);
1605
- }
1606
- window.dispatchEvent(new PopStateEvent("popstate", { state }));
1607
- }
1608
- function goBack() {
1609
- if (typeof window === "undefined") return;
1610
- window.history.back();
1611
- }
1612
- function goForward() {
1613
- if (typeof window === "undefined") return;
1614
- window.history.forward();
1615
- }
1616
- function getUrlInfo() {
1617
- if (typeof window === "undefined") {
1618
- return {
1619
- href: "",
1620
- pathname: "",
1621
- search: "",
1622
- hash: "",
1623
- searchParams: {}
1624
- };
1625
- }
1626
- const searchParams = {};
1627
- const urlSearchParams = new URLSearchParams(window.location.search);
1628
- urlSearchParams.forEach((value, key) => {
1629
- searchParams[key] = value;
1630
- });
1631
- return {
1632
- href: window.location.href,
1633
- pathname: window.location.pathname,
1634
- search: window.location.search,
1635
- hash: window.location.hash,
1636
- searchParams
1637
- };
1638
- }
1639
-
1640
- // packages/runtime-core/src/eventSystem.ts
1641
- var builtInPlugins = {
1642
- /**
1643
- * Set state value
1644
- * Mirrors: state-setState from actions.ts
1645
- */
1646
- "state-setState": (action, ctx) => {
1647
- const { stateConfig } = action.config || {};
1648
- if (!stateConfig || !ctx.stateManager) return;
1649
- const config = typeof stateConfig === "string" ? JSON.parse(stateConfig) : stateConfig;
1650
- const { key, value, operation = "set" } = config;
1651
- if (!key) return;
1652
- switch (operation) {
1653
- case "set":
1654
- ctx.stateManager.set(key, value, ctx.elementId);
1655
- break;
1656
- case "merge":
1657
- ctx.stateManager.merge(key, value, false, ctx.elementId);
1658
- break;
1659
- case "deepMerge":
1660
- ctx.stateManager.merge(key, value, true, ctx.elementId);
1661
- break;
1662
- case "toggle":
1663
- ctx.stateManager.toggle(key, ctx.elementId);
1664
- break;
1665
- case "append":
1666
- ctx.stateManager.append(key, value, ctx.elementId);
1667
- break;
1668
- case "prepend":
1669
- ctx.stateManager.prepend(key, value, ctx.elementId);
1670
- break;
1671
- case "delete":
1672
- ctx.stateManager.delete(key, ctx.elementId);
1673
- break;
1674
- case "increment":
1675
- const currentVal = ctx.stateManager.get(key) || 0;
1676
- ctx.stateManager.set(key, currentVal + (value || 1), ctx.elementId);
1677
- break;
1678
- case "decrement":
1679
- const currVal = ctx.stateManager.get(key) || 0;
1680
- ctx.stateManager.set(key, currVal - (value || 1), ctx.elementId);
1681
- break;
1682
- }
1683
- return { success: true, key, operation };
1684
- },
1685
- /**
1686
- * Navigate to URL
1687
- * Mirrors: navigateTo from actions.ts
1688
- */
1689
- "navigateTo": (action, ctx) => {
1690
- const { url, replace, newTab, state } = action.config || {};
1691
- if (!url) return;
1692
- navigateTo(url, { replace, newTab, state });
1693
- return { success: true, url };
1694
- },
1695
- /**
1696
- * Set localStorage value
1697
- */
1698
- "localStorage-set": (action, ctx) => {
1699
- const { key, value } = action.config || {};
1700
- if (!key) return;
1701
- setLocalStorage(key, value);
1702
- return { success: true, key };
1703
- },
1704
- /**
1705
- * Get localStorage value
1706
- */
1707
- "localStorage-get": (action, ctx) => {
1708
- const { key, defaultValue } = action.config || {};
1709
- if (!key) return defaultValue;
1710
- return getLocalStorage(key, defaultValue);
1711
- },
1712
- /**
1713
- * Remove localStorage value
1714
- */
1715
- "localStorage-remove": (action, ctx) => {
1716
- const { key } = action.config || {};
1717
- if (!key) return;
1718
- if (typeof localStorage !== "undefined") {
1719
- localStorage.removeItem(key);
1720
- }
1721
- return { success: true, key };
1722
- },
1723
- /**
1724
- * Set sessionStorage value
1725
- */
1726
- "sessionStorage-set": (action, ctx) => {
1727
- const { key, value } = action.config || {};
1728
- if (!key) return;
1729
- setSessionStorage(key, value);
1730
- return { success: true, key };
1731
- },
1732
- /**
1733
- * Get sessionStorage value
1734
- */
1735
- "sessionStorage-get": (action, ctx) => {
1736
- const { key, defaultValue } = action.config || {};
1737
- if (!key) return defaultValue;
1738
- return getSessionStorage(key, defaultValue);
1739
- },
1740
- /**
1741
- * Console log (for debugging)
1742
- */
1743
- "console-log": (action, ctx) => {
1744
- const { message, data } = action.config || {};
1745
- console.log("[Servly]", message, data);
1746
- return { success: true };
1747
- },
1748
- /**
1749
- * Show alert
1750
- */
1751
- "alert": (action, ctx) => {
1752
- const { message } = action.config || {};
1753
- if (typeof alert !== "undefined") {
1754
- alert(message);
1755
- }
1756
- return { success: true };
1757
- },
1758
- /**
1759
- * Copy to clipboard
1760
- */
1761
- "clipboard-copy": async (action, ctx) => {
1762
- const { text } = action.config || {};
1763
- if (!text || typeof navigator === "undefined") return { success: false };
1764
- try {
1765
- await navigator.clipboard.writeText(text);
1766
- return { success: true };
1767
- } catch (error) {
1768
- return { success: false, error };
1769
- }
1770
- },
1771
- /**
1772
- * Scroll to element
1773
- */
1774
- "scrollTo": (action, ctx) => {
1775
- const { selector, behavior = "smooth", block = "start" } = action.config || {};
1776
- if (!selector || typeof document === "undefined") return;
1777
- const element = document.querySelector(selector);
1778
- if (element) {
1779
- element.scrollIntoView({ behavior, block });
1780
- return { success: true };
1781
- }
1782
- return { success: false, error: "Element not found" };
1783
- },
1784
- /**
1785
- * Focus element
1786
- */
1787
- "focus": (action, ctx) => {
1788
- const { selector } = action.config || {};
1789
- if (!selector || typeof document === "undefined") return;
1790
- const element = document.querySelector(selector);
1791
- if (element && typeof element.focus === "function") {
1792
- element.focus();
1793
- return { success: true };
1794
- }
1795
- return { success: false, error: "Element not found" };
1796
- },
1797
- /**
1798
- * Blur element
1799
- */
1800
- "blur": (action, ctx) => {
1801
- const { selector } = action.config || {};
1802
- if (!selector || typeof document === "undefined") return;
1803
- const element = document.querySelector(selector);
1804
- if (element && typeof element.blur === "function") {
1805
- element.blur();
1806
- return { success: true };
1807
- }
1808
- return { success: false, error: "Element not found" };
1809
- },
1810
- /**
1811
- * Add class to element
1812
- */
1813
- "addClass": (action, ctx) => {
1814
- const { selector, className } = action.config || {};
1815
- if (!selector || !className || typeof document === "undefined") return;
1816
- const element = document.querySelector(selector);
1817
- if (element) {
1818
- element.classList.add(className);
1819
- return { success: true };
1820
- }
1821
- return { success: false, error: "Element not found" };
1822
- },
1823
- /**
1824
- * Remove class from element
1825
- */
1826
- "removeClass": (action, ctx) => {
1827
- const { selector, className } = action.config || {};
1828
- if (!selector || !className || typeof document === "undefined") return;
1829
- const element = document.querySelector(selector);
1830
- if (element) {
1831
- element.classList.remove(className);
1832
- return { success: true };
1833
- }
1834
- return { success: false, error: "Element not found" };
1835
- },
1836
- /**
1837
- * Toggle class on element
1838
- */
1839
- "toggleClass": (action, ctx) => {
1840
- const { selector, className } = action.config || {};
1841
- if (!selector || !className || typeof document === "undefined") return;
1842
- const element = document.querySelector(selector);
1843
- if (element) {
1844
- element.classList.toggle(className);
1845
- return { success: true };
1846
- }
1847
- return { success: false, error: "Element not found" };
1848
- },
1849
- /**
1850
- * Set element attribute
1851
- */
1852
- "setAttribute": (action, ctx) => {
1853
- const { selector, attribute, value } = action.config || {};
1854
- if (!selector || !attribute || typeof document === "undefined") return;
1855
- const element = document.querySelector(selector);
1856
- if (element) {
1857
- element.setAttribute(attribute, value);
1858
- return { success: true };
1859
- }
1860
- return { success: false, error: "Element not found" };
1861
- },
1862
- /**
1863
- * Remove element attribute
1864
- */
1865
- "removeAttribute": (action, ctx) => {
1866
- const { selector, attribute } = action.config || {};
1867
- if (!selector || !attribute || typeof document === "undefined") return;
1868
- const element = document.querySelector(selector);
1869
- if (element) {
1870
- element.removeAttribute(attribute);
1871
- return { success: true };
1872
- }
1873
- return { success: false, error: "Element not found" };
1874
- },
1875
- /**
1876
- * Dispatch custom event
1877
- */
1878
- "dispatchEvent": (action, ctx) => {
1879
- const { selector, eventName, detail } = action.config || {};
1880
- if (!eventName || typeof document === "undefined") return;
1881
- const target = selector ? document.querySelector(selector) : document;
1882
- if (target) {
1883
- const event = new CustomEvent(eventName, { detail, bubbles: true });
1884
- target.dispatchEvent(event);
1885
- return { success: true };
1886
- }
1887
- return { success: false, error: "Target not found" };
1888
- },
1889
- /**
1890
- * Delay execution
1891
- */
1892
- "delay": async (action, ctx) => {
1893
- const { ms = 0 } = action.config || {};
1894
- await new Promise((resolve) => setTimeout(resolve, ms));
1895
- return { success: true };
1896
- },
1897
- /**
1898
- * Conditional execution
1899
- */
1900
- "condition": (action, ctx) => {
1901
- const { condition, thenActions, elseActions } = action.config || {};
1902
- return { condition, thenActions, elseActions };
1903
- }
1904
- };
1905
- var EventSystem = class {
1906
- config;
1907
- pluginExecutors;
1908
- debounceTimers = /* @__PURE__ */ new Map();
1909
- throttleTimers = /* @__PURE__ */ new Map();
1910
- constructor(config = {}) {
1911
- this.config = config;
1912
- this.pluginExecutors = {
1913
- ...builtInPlugins,
1914
- ...config.pluginExecutors
1915
- };
1916
- }
1917
- /**
1918
- * Register a custom plugin executor
1919
- */
1920
- registerPlugin(key, executor) {
1921
- this.pluginExecutors[key] = executor;
1922
- }
1923
- /**
1924
- * Create an event handler from Servly plugin format
1925
- */
1926
- createHandler(elementId, handlerConfig, context, options = {}) {
1927
- return (event) => {
1928
- if (handlerConfig.preventDefault || options.preventDefault) {
1929
- event.preventDefault();
1930
- }
1931
- if (handlerConfig.stopPropagation || options.stopPropagation) {
1932
- event.stopPropagation();
1933
- }
1934
- if (this.config.onEvent) {
1935
- this.config.onEvent(event.type, elementId, event);
1936
- }
1937
- if (handlerConfig.plugins && handlerConfig.plugins.length > 0) {
1938
- this.executePlugins(handlerConfig.plugins, {
1939
- event,
1940
- elementId,
1941
- context,
1942
- stateManager: this.config.stateManager
1943
- });
1944
- }
1945
- };
1946
- }
1947
- /**
1948
- * Execute a sequence of plugin actions
1949
- */
1950
- async executePlugins(plugins, eventContext) {
1951
- const results = [];
1952
- for (const action of plugins) {
1953
- try {
1954
- const executor = this.pluginExecutors[action.key];
1955
- if (executor) {
1956
- const result = await executor(action, eventContext);
1957
- results.push(result);
1958
- if (this.config.onPluginExecute) {
1959
- this.config.onPluginExecute(action, result);
1960
- }
1961
- } else {
1962
- console.warn(`[EventSystem] Unknown plugin: ${action.key}`);
1963
- results.push({ error: `Unknown plugin: ${action.key}` });
1964
- }
1965
- } catch (error) {
1966
- console.error(`[EventSystem] Plugin error (${action.key}):`, error);
1967
- results.push({ error });
1968
- }
1969
- }
1970
- return results;
1971
- }
1972
- /**
1973
- * Create a debounced event handler
1974
- */
1975
- createDebouncedHandler(elementId, handler, delay) {
1976
- const key = `${elementId}-debounce`;
1977
- return (event) => {
1978
- const existingTimer = this.debounceTimers.get(key);
1979
- if (existingTimer) {
1980
- clearTimeout(existingTimer);
1981
- }
1982
- const timer = setTimeout(() => {
1983
- handler(event);
1984
- this.debounceTimers.delete(key);
1985
- }, delay);
1986
- this.debounceTimers.set(key, timer);
1987
- };
1988
- }
1989
- /**
1990
- * Create a throttled event handler
1991
- */
1992
- createThrottledHandler(elementId, handler, delay) {
1993
- const key = `${elementId}-throttle`;
1994
- return (event) => {
1995
- if (this.throttleTimers.get(key)) {
1996
- return;
1997
- }
1998
- handler(event);
1999
- this.throttleTimers.set(key, true);
2000
- setTimeout(() => {
2001
- this.throttleTimers.delete(key);
2002
- }, delay);
2003
- };
2004
- }
2005
- /**
2006
- * Clean up all timers
2007
- */
2008
- destroy() {
2009
- for (const timer of this.debounceTimers.values()) {
2010
- clearTimeout(timer);
2011
- }
2012
- this.debounceTimers.clear();
2013
- this.throttleTimers.clear();
2014
- }
2015
- };
2016
- var EVENT_HANDLERS = {
2017
- onClick: "click",
2018
- onDoubleClick: "dblclick",
2019
- onMouseDown: "mousedown",
2020
- onMouseUp: "mouseup",
2021
- onMouseEnter: "mouseenter",
2022
- onMouseLeave: "mouseleave",
2023
- onMouseMove: "mousemove",
2024
- onMouseOver: "mouseover",
2025
- onMouseOut: "mouseout",
2026
- onKeyDown: "keydown",
2027
- onKeyUp: "keyup",
2028
- onKeyPress: "keypress",
2029
- onFocus: "focus",
2030
- onBlur: "blur",
2031
- onChange: "change",
2032
- onInput: "input",
2033
- onSubmit: "submit",
2034
- onReset: "reset",
2035
- onScroll: "scroll",
2036
- onWheel: "wheel",
2037
- onDragStart: "dragstart",
2038
- onDrag: "drag",
2039
- onDragEnd: "dragend",
2040
- onDragEnter: "dragenter",
2041
- onDragLeave: "dragleave",
2042
- onDragOver: "dragover",
2043
- onDrop: "drop",
2044
- onTouchStart: "touchstart",
2045
- onTouchMove: "touchmove",
2046
- onTouchEnd: "touchend",
2047
- onTouchCancel: "touchcancel",
2048
- onContextMenu: "contextmenu",
2049
- onCopy: "copy",
2050
- onCut: "cut",
2051
- onPaste: "paste",
2052
- onLoad: "load",
2053
- onError: "error",
2054
- onAnimationStart: "animationstart",
2055
- onAnimationEnd: "animationend",
2056
- onAnimationIteration: "animationiteration",
2057
- onTransitionEnd: "transitionend"
2058
- };
2059
- function toDomEventName(reactEventName) {
2060
- return EVENT_HANDLERS[reactEventName] || reactEventName.replace(/^on/, "").toLowerCase();
2061
- }
2062
- function toReactEventName(domEventName) {
2063
- const entry = Object.entries(EVENT_HANDLERS).find(([, dom]) => dom === domEventName);
2064
- return entry ? entry[0] : `on${domEventName.charAt(0).toUpperCase()}${domEventName.slice(1)}`;
2065
- }
2066
- var defaultEventSystem = null;
2067
- function getEventSystem(config) {
2068
- if (!defaultEventSystem || config) {
2069
- defaultEventSystem = new EventSystem(config);
2070
- }
2071
- return defaultEventSystem;
2072
- }
2073
- function resetEventSystem() {
2074
- if (defaultEventSystem) {
2075
- defaultEventSystem.destroy();
2076
- defaultEventSystem = null;
2077
- }
2078
- }
2079
-
2080
- // packages/runtime-core/src/overrides.ts
2081
- var OverrideSystem = class {
2082
- config;
2083
- elementStates = /* @__PURE__ */ new Map();
2084
- watchIntervals = /* @__PURE__ */ new Map();
2085
- constructor(config = {}) {
2086
- this.config = config;
2087
- }
2088
- /**
2089
- * Get overrides from an element
2090
- */
2091
- getOverrides(element) {
2092
- const rawOverrides = element.configuration?._overrides_;
2093
- if (!rawOverrides) return [];
2094
- if (Array.isArray(rawOverrides)) {
2095
- return rawOverrides.filter((o) => o && typeof o === "object");
2096
- }
2097
- if (typeof rawOverrides === "object") {
2098
- console.warn("[OverrideSystem] Legacy object override format detected, ignoring");
2099
- return [];
2100
- }
2101
- return [];
2102
- }
2103
- /**
2104
- * Initialize overrides for an element (called on mount)
2105
- */
2106
- async initializeElement(element, context) {
2107
- const elementId = element.i;
2108
- const overrides = this.getOverrides(element);
2109
- if (overrides.length === 0) return;
2110
- const state = {
2111
- previousValues: /* @__PURE__ */ new Map(),
2112
- initialized: false,
2113
- abortController: new AbortController()
2114
- };
2115
- this.elementStates.set(elementId, state);
2116
- const mountOverrides = overrides.filter((o) => !o.isCleanUp);
2117
- overrides.forEach((override, index) => {
2118
- if (override.isCleanUp) return;
2119
- if (!override.dependencies || override.dependencies.length === 0) return;
2120
- const initialValues = override.dependencies.map(
2121
- (dep) => resolveTemplate(dep, context)
2122
- );
2123
- state.previousValues.set(index, initialValues);
2124
- });
2125
- await this.processOverrides(elementId, mountOverrides, context, "onMount");
2126
- state.initialized = true;
2127
- }
2128
- /**
2129
- * Check and process dependency changes for an element
2130
- */
2131
- async checkDependencies(element, context) {
2132
- const elementId = element.i;
2133
- const overrides = this.getOverrides(element);
2134
- const state = this.elementStates.get(elementId);
2135
- if (!state || !state.initialized) return;
2136
- const overridesToTrigger = [];
2137
- overrides.forEach((override, index) => {
2138
- if (override.isCleanUp) return;
2139
- if (!override.dependencies || override.dependencies.length === 0) return;
2140
- const currentValues = override.dependencies.map(
2141
- (dep) => resolveTemplate(dep, context)
2142
- );
2143
- const previousValues = state.previousValues.get(index) || [];
2144
- let hasChanged = false;
2145
- if (previousValues.length !== currentValues.length) {
2146
- hasChanged = true;
2147
- } else {
2148
- for (let i = 0; i < currentValues.length; i++) {
2149
- if (JSON.stringify(previousValues[i]) !== JSON.stringify(currentValues[i])) {
2150
- hasChanged = true;
2151
- break;
2152
- }
2153
- }
2154
- }
2155
- if (hasChanged) {
2156
- overridesToTrigger.push(override);
2157
- state.previousValues.set(index, currentValues);
2158
- if (this.config.onDependencyChange) {
2159
- this.config.onDependencyChange(elementId, override.dependencies, currentValues);
2160
- }
2161
- }
2162
- });
2163
- if (overridesToTrigger.length > 0) {
2164
- await this.processOverrides(elementId, overridesToTrigger, context, "onDependencyChange");
2165
- }
2166
- }
2167
- /**
2168
- * Cleanup overrides for an element (called on unmount)
2169
- */
2170
- async cleanupElement(element, context) {
2171
- const elementId = element.i;
2172
- const overrides = this.getOverrides(element);
2173
- const state = this.elementStates.get(elementId);
2174
- if (state?.abortController) {
2175
- state.abortController.abort();
2176
- }
2177
- this.stopWatching(elementId);
2178
- const cleanupOverrides = overrides.filter((o) => o.isCleanUp);
2179
- if (cleanupOverrides.length > 0) {
2180
- await this.processOverrides(elementId, cleanupOverrides, context, "onUnmount");
2181
- }
2182
- this.elementStates.delete(elementId);
2183
- }
2184
- /**
2185
- * Process a list of overrides
2186
- */
2187
- async processOverrides(elementId, overrides, context, eventType) {
2188
- if (!overrides || overrides.length === 0) return;
2189
- const validOverrides = overrides.filter((o) => o.plugins && o.plugins.length > 0);
2190
- if (validOverrides.length === 0) return;
2191
- const results = await Promise.allSettled(
2192
- validOverrides.map(async (override) => {
2193
- if (this.config.onOverrideTrigger) {
2194
- this.config.onOverrideTrigger(elementId, override, eventType);
2195
- }
2196
- if (this.config.eventSystem && override.plugins) {
2197
- const eventContext = {
2198
- event: new CustomEvent(eventType),
2199
- elementId,
2200
- context,
2201
- stateManager: this.config.stateManager
2202
- };
2203
- return this.config.eventSystem.executePlugins(override.plugins, eventContext);
2204
- }
2205
- return null;
2206
- })
2207
- );
2208
- results.forEach((result, index) => {
2209
- if (result.status === "rejected") {
2210
- console.error(`[OverrideSystem] Override ${index} failed:`, result.reason);
2211
- }
2212
- });
2213
- }
2214
- /**
2215
- * Start watching an element for dependency changes
2216
- */
2217
- startWatching(element, context, intervalMs = 100) {
2218
- const elementId = element.i;
2219
- this.stopWatching(elementId);
2220
- const overrides = this.getOverrides(element);
2221
- const hasDependencies = overrides.some(
2222
- (o) => !o.isCleanUp && o.dependencies && o.dependencies.length > 0
2223
- );
2224
- if (!hasDependencies) return;
2225
- const interval = setInterval(() => {
2226
- this.checkDependencies(element, context);
2227
- }, intervalMs);
2228
- this.watchIntervals.set(elementId, interval);
2229
- }
2230
- /**
2231
- * Stop watching an element
2232
- */
2233
- stopWatching(elementId) {
2234
- const interval = this.watchIntervals.get(elementId);
2235
- if (interval) {
2236
- clearInterval(interval);
2237
- this.watchIntervals.delete(elementId);
2238
- }
2239
- }
2240
- /**
2241
- * Destroy the override system
2242
- */
2243
- destroy() {
2244
- for (const interval of this.watchIntervals.values()) {
2245
- clearInterval(interval);
2246
- }
2247
- this.watchIntervals.clear();
2248
- for (const state of this.elementStates.values()) {
2249
- if (state.abortController) {
2250
- state.abortController.abort();
2251
- }
2252
- }
2253
- this.elementStates.clear();
2254
- }
2255
- };
2256
- function extractOverrideDependencies(element) {
2257
- const overrides = element.configuration?._overrides_;
2258
- if (!Array.isArray(overrides)) return [];
2259
- const dependencies = [];
2260
- for (const override of overrides) {
2261
- if (override?.dependencies && Array.isArray(override.dependencies)) {
2262
- dependencies.push(...override.dependencies);
2263
- }
2264
- }
2265
- return [...new Set(dependencies)];
2266
- }
2267
- function hasOverrides(element) {
2268
- const overrides = element.configuration?._overrides_;
2269
- return Array.isArray(overrides) && overrides.length > 0;
2270
- }
2271
- function hasDependencyOverrides(element) {
2272
- const overrides = element.configuration?._overrides_;
2273
- if (!Array.isArray(overrides)) return false;
2274
- return overrides.some(
2275
- (o) => !o?.isCleanUp && o?.dependencies && o.dependencies.length > 0
2276
- );
2277
- }
2278
- function getMountOverrides(element) {
2279
- const overrides = element.configuration?._overrides_;
2280
- if (!Array.isArray(overrides)) return [];
2281
- return overrides.filter((o) => o && !o.isCleanUp);
2282
- }
2283
- function getCleanupOverrides(element) {
2284
- const overrides = element.configuration?._overrides_;
2285
- if (!Array.isArray(overrides)) return [];
2286
- return overrides.filter((o) => o && o.isCleanUp);
2287
- }
2288
- var defaultOverrideSystem = null;
2289
- function getOverrideSystem(config) {
2290
- if (!defaultOverrideSystem || config) {
2291
- defaultOverrideSystem = new OverrideSystem(config);
2292
- }
2293
- return defaultOverrideSystem;
2294
- }
2295
- function resetOverrideSystem() {
2296
- if (defaultOverrideSystem) {
2297
- defaultOverrideSystem.destroy();
2298
- defaultOverrideSystem = null;
2299
- }
2300
- }
2301
-
2302
- // packages/runtime-core/src/icons.ts
2303
- var cdnEnabled = false;
2304
- function setIconCdnEnabled(enabled) {
2305
- cdnEnabled = enabled;
2306
- }
2307
- function isIconCdnEnabled() {
2308
- return cdnEnabled;
2309
- }
2310
- var ICONIFY_COLLECTIONS = {
2311
- Ai: "ant-design",
2312
- Bi: "bi",
2313
- Bs: "bi",
2314
- Bx: "bx",
2315
- Ci: "circum",
2316
- Cg: "gg",
2317
- Di: "devicon",
2318
- Fi: "feather",
2319
- Fc: "flat-color-icons",
2320
- Fa: "fa-solid",
2321
- Fa6: "fa6-solid",
2322
- Gi: "game-icons",
2323
- Go: "octicon",
2324
- Gr: "grommet-icons",
2325
- Hi: "heroicons-outline",
2326
- Hi2: "heroicons",
2327
- Im: "icomoon-free",
2328
- Io: "ion",
2329
- Io5: "ion",
2330
- Lu: "lucide",
2331
- Md: "ic",
2332
- Pi: "ph",
2333
- Ri: "ri",
2334
- Rx: "radix-icons",
2335
- Si: "simple-icons",
2336
- Sl: "simple-line-icons",
2337
- Tb: "tabler",
2338
- Tfi: "themify",
2339
- Vsc: "codicon",
2340
- Wi: "wi"
2341
- };
2342
- var ICON_SET_STYLES = {
2343
- Fi: { stroke: true },
2344
- Lu: { stroke: true },
2345
- Hi: { stroke: true },
2346
- Hi2: { stroke: true },
2347
- Tb: { stroke: true }
2348
- };
2349
- var iconCache = /* @__PURE__ */ new Map();
2350
- var pendingFetches = /* @__PURE__ */ new Map();
2351
- var registeredIcons = /* @__PURE__ */ new Map();
2352
- function registerIcon(set, name, data) {
2353
- const key = `${set}:${name}`;
2354
- registeredIcons.set(key, data);
2355
- iconCache.set(key, data);
2356
- }
2357
- function registerIcons(icons) {
2358
- for (const [set, setIcons] of Object.entries(icons)) {
2359
- for (const [name, data] of Object.entries(setIcons)) {
2360
- registerIcon(set, name, data);
2361
- }
2362
- }
2363
- }
2364
- function isIconRegistered(icon) {
2365
- const key = `${icon.set}:${icon.name}`;
2366
- return registeredIcons.has(key);
2367
- }
2368
- function getRegisteredIconKeys() {
2369
- return Array.from(registeredIcons.keys());
2370
- }
2371
- function toKebabCase(str) {
2372
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
2373
- }
2374
- function getIconifyName(name, set) {
2375
- const prefixLength = set.length;
2376
- let baseName = name;
2377
- if (name.startsWith(set)) {
2378
- baseName = name.slice(prefixLength);
2379
- }
2380
- let kebabName = toKebabCase(baseName);
2381
- switch (set) {
2382
- case "Md":
2383
- if (baseName.startsWith("Outline")) {
2384
- kebabName = toKebabCase(baseName.slice(7)) + "-outline";
2385
- }
2386
- kebabName = "baseline-" + kebabName;
2387
- break;
2388
- case "Hi2":
2389
- if (baseName.startsWith("Outline")) {
2390
- kebabName = toKebabCase(baseName.slice(7));
2391
- } else if (baseName.startsWith("Solid")) {
2392
- kebabName = toKebabCase(baseName.slice(5)) + "-solid";
2393
- }
2394
- break;
2395
- case "Io":
2396
- case "Io5":
2397
- if (baseName.startsWith("Ios")) {
2398
- kebabName = toKebabCase(baseName.slice(3));
2399
- } else if (baseName.startsWith("Md")) {
2400
- kebabName = toKebabCase(baseName.slice(2));
2401
- }
2402
- break;
2403
- case "Pi":
2404
- if (baseName.endsWith("Bold")) {
2405
- kebabName = toKebabCase(baseName.slice(0, -4)) + "-bold";
2406
- } else if (baseName.endsWith("Fill")) {
2407
- kebabName = toKebabCase(baseName.slice(0, -4)) + "-fill";
2408
- } else if (baseName.endsWith("Light")) {
2409
- kebabName = toKebabCase(baseName.slice(0, -5)) + "-light";
2410
- } else if (baseName.endsWith("Thin")) {
2411
- kebabName = toKebabCase(baseName.slice(0, -4)) + "-thin";
2412
- } else if (baseName.endsWith("Duotone")) {
2413
- kebabName = toKebabCase(baseName.slice(0, -7)) + "-duotone";
2414
- }
2415
- break;
2416
- }
2417
- return kebabName;
2418
- }
2419
- async function fetchIconFromIconify(set, name) {
2420
- if (!cdnEnabled) {
2421
- return null;
2422
- }
2423
- const collection = ICONIFY_COLLECTIONS[set];
2424
- if (!collection) {
2425
- return null;
2426
- }
2427
- const iconName = getIconifyName(name, set);
2428
- try {
2429
- const url = `https://api.iconify.design/${collection}/${iconName}.svg`;
2430
- const response = await fetch(url);
2431
- if (!response.ok) {
2432
- const altNames = [
2433
- iconName.replace(/-outline$/, ""),
2434
- iconName.replace(/-solid$/, ""),
2435
- iconName.replace(/-fill$/, ""),
2436
- iconName.replace(/^baseline-/, "")
2437
- ].filter((alt) => alt !== iconName);
2438
- for (const altName of altNames) {
2439
- const altUrl = `https://api.iconify.design/${collection}/${altName}.svg`;
2440
- const altResponse = await fetch(altUrl);
2441
- if (altResponse.ok) {
2442
- return parseSvgResponse(await altResponse.text());
2443
- }
2444
- }
2445
- return null;
2446
- }
2447
- return parseSvgResponse(await response.text());
2448
- } catch {
2449
- return null;
2450
- }
2451
- }
2452
- function parseSvgResponse(svgText) {
2453
- try {
2454
- const parser = new DOMParser();
2455
- const doc = parser.parseFromString(svgText, "image/svg+xml");
2456
- const svg = doc.querySelector("svg");
2457
- if (!svg) {
2458
- return null;
2459
- }
2460
- const viewBox = svg.getAttribute("viewBox");
2461
- const width = parseInt(svg.getAttribute("width") || "24", 10);
2462
- const height = parseInt(svg.getAttribute("height") || "24", 10);
2463
- const body = svg.innerHTML;
2464
- return {
2465
- body,
2466
- width,
2467
- height,
2468
- viewBox: viewBox || `0 0 ${width} ${height}`
2469
- };
2470
- } catch {
2471
- return null;
2472
- }
2473
- }
2474
- async function getIconData(icon) {
2475
- const key = `${icon.set}:${icon.name}`;
2476
- if (iconCache.has(key)) {
2477
- return iconCache.get(key);
2478
- }
2479
- if (pendingFetches.has(key)) {
2480
- return pendingFetches.get(key);
2481
- }
2482
- if (!cdnEnabled) {
2483
- return null;
2484
- }
2485
- const fetchPromise = fetchIconFromIconify(icon.set, icon.name);
2486
- pendingFetches.set(key, fetchPromise);
2487
- const data = await fetchPromise;
2488
- pendingFetches.delete(key);
2489
- if (data) {
2490
- iconCache.set(key, data);
2491
- }
2492
- return data;
2493
- }
2494
- function getIconDataSync(icon) {
2495
- const key = `${icon.set}:${icon.name}`;
2496
- return iconCache.get(key) || null;
2497
- }
2498
- function createIconSVG(icon, data, size = 24, color = "currentColor") {
2499
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
2500
- svg.setAttribute("width", String(size));
2501
- svg.setAttribute("height", String(size));
2502
- svg.setAttribute("viewBox", data.viewBox || "0 0 24 24");
2503
- svg.setAttribute("data-icon-name", icon.name);
2504
- svg.setAttribute("data-icon-set", icon.set);
2505
- svg.setAttribute("class", `servly-icon servly-icon-${icon.set.toLowerCase()}`);
2506
- const style = ICON_SET_STYLES[icon.set];
2507
- if (style?.stroke) {
2508
- svg.setAttribute("fill", "none");
2509
- svg.setAttribute("stroke", color);
2510
- svg.setAttribute("stroke-width", "2");
2511
- svg.setAttribute("stroke-linecap", "round");
2512
- svg.setAttribute("stroke-linejoin", "round");
2513
- } else {
2514
- svg.setAttribute("fill", color);
2515
- }
2516
- svg.innerHTML = data.body;
2517
- const paths = svg.querySelectorAll("path, circle, rect, polygon, line, polyline");
2518
- paths.forEach((el) => {
2519
- if (!el.getAttribute("fill") || el.getAttribute("fill") === "currentColor") {
2520
- if (style?.stroke) {
2521
- el.setAttribute("stroke", color);
2522
- } else {
2523
- el.setAttribute("fill", color);
2524
- }
2525
- }
2526
- });
2527
- return svg;
2528
- }
2529
- function createPlaceholderIcon(icon, size = 24, color = "currentColor") {
2530
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
2531
- svg.setAttribute("width", String(size));
2532
- svg.setAttribute("height", String(size));
2533
- svg.setAttribute("viewBox", "0 0 24 24");
2534
- svg.setAttribute("fill", "none");
2535
- svg.setAttribute("stroke", color);
2536
- svg.setAttribute("stroke-width", "2");
2537
- svg.setAttribute("stroke-linecap", "round");
2538
- svg.setAttribute("stroke-linejoin", "round");
2539
- svg.setAttribute("data-icon-name", icon.name);
2540
- svg.setAttribute("data-icon-set", icon.set);
2541
- svg.setAttribute("data-icon-missing", "true");
2542
- svg.setAttribute("class", "servly-icon servly-icon-placeholder");
2543
- const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
2544
- circle.setAttribute("cx", "12");
2545
- circle.setAttribute("cy", "12");
2546
- circle.setAttribute("r", "10");
2547
- const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
2548
- path.setAttribute("d", "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3");
2549
- const dot = document.createElementNS("http://www.w3.org/2000/svg", "line");
2550
- dot.setAttribute("x1", "12");
2551
- dot.setAttribute("y1", "17");
2552
- dot.setAttribute("x2", "12.01");
2553
- dot.setAttribute("y2", "17");
2554
- svg.appendChild(circle);
2555
- svg.appendChild(path);
2556
- svg.appendChild(dot);
2557
- return svg;
2558
- }
2559
- function renderIcon(container, icon, size = 24, color = "currentColor") {
2560
- const cachedData = getIconDataSync(icon);
2561
- if (cachedData) {
2562
- const svg = createIconSVG(icon, cachedData, size, color);
2563
- container.innerHTML = "";
2564
- container.appendChild(svg);
2565
- return;
2566
- }
2567
- if (!cdnEnabled) {
2568
- const placeholder2 = createPlaceholderIcon(icon, size, color);
2569
- container.innerHTML = "";
2570
- container.appendChild(placeholder2);
2571
- console.warn(
2572
- `[Icons] Icon not bundled: ${icon.set}:${icon.name}. Use registerIcon() to bundle it, or enable CDN with setIconCdnEnabled(true).`
2573
- );
2574
- return;
2575
- }
2576
- const placeholder = createPlaceholderIcon(icon, size, color);
2577
- placeholder.setAttribute("data-icon-loading", "true");
2578
- placeholder.removeAttribute("data-icon-missing");
2579
- container.innerHTML = "";
2580
- container.appendChild(placeholder);
2581
- getIconData(icon).then((data) => {
2582
- if (data) {
2583
- const svg = createIconSVG(icon, data, size, color);
2584
- container.innerHTML = "";
2585
- container.appendChild(svg);
2586
- } else {
2587
- placeholder.setAttribute("data-icon-missing", "true");
2588
- placeholder.removeAttribute("data-icon-loading");
2589
- }
2590
- });
2591
- }
2592
- async function preloadIcons(icons) {
2593
- await Promise.all(icons.map((icon) => getIconData(icon)));
2594
- }
2595
- function clearIconCache() {
2596
- iconCache.clear();
2597
- pendingFetches.clear();
2598
- registeredIcons.forEach((data, key) => {
2599
- iconCache.set(key, data);
2600
- });
2601
- }
2602
- function isIconSetSupported(set) {
2603
- return set in ICONIFY_COLLECTIONS;
2604
- }
2605
- function getSupportedIconSets() {
2606
- return Object.keys(ICONIFY_COLLECTIONS);
2607
- }
2608
- function getIconifyCollection(set) {
2609
- return ICONIFY_COLLECTIONS[set];
2610
- }
2611
-
2612
- // packages/runtime-core/src/renderer.ts
2613
- var COMPONENT_TO_TAG = {
2614
- container: "div",
2615
- text: "span",
2616
- button: "button",
2617
- input: "input",
2618
- image: "img",
2619
- link: "a",
2620
- form: "form",
2621
- label: "label",
2622
- textarea: "textarea",
2623
- select: "select",
2624
- option: "option",
2625
- list: "ul",
2626
- listItem: "li",
2627
- heading: "h1",
2628
- paragraph: "p",
2629
- section: "section",
2630
- article: "article",
2631
- header: "header",
2632
- footer: "footer",
2633
- nav: "nav",
2634
- aside: "aside",
2635
- main: "main",
2636
- span: "span",
2637
- div: "div"
2638
- };
2639
- var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
2640
- "input",
2641
- "img",
2642
- "br",
2643
- "hr",
2644
- "area",
2645
- "base",
2646
- "col",
2647
- "embed",
2648
- "link",
2649
- "meta",
2650
- "param",
2651
- "source",
2652
- "track",
2653
- "wbr"
2654
- ]);
2655
- function getElementTag(element) {
2656
- const config = element.configuration;
2657
- if (config?.tag) {
2658
- return config.tag;
2659
- }
2660
- if (element.componentId && COMPONENT_TO_TAG[element.componentId]) {
2661
- return COMPONENT_TO_TAG[element.componentId];
2662
- }
2663
- if (element.isGroup) {
2664
- return "div";
2665
- }
2666
- return "div";
2667
- }
2668
- function isSelfClosing(tag) {
2669
- return SELF_CLOSING_TAGS.has(tag.toLowerCase());
2670
- }
2671
- function buildTree(elements) {
2672
- const tree = /* @__PURE__ */ new Map();
2673
- for (const element of elements) {
2674
- const parentId = element.parent || null;
2675
- if (!tree.has(parentId)) {
2676
- tree.set(parentId, []);
2677
- }
2678
- tree.get(parentId).push(element);
2679
- }
2680
- return tree;
2681
- }
2682
- function getTextContent(element, context) {
2683
- const config = element.configuration;
2684
- if (config?.dynamicText) {
2685
- return resolveTemplate(config.dynamicText, context);
2686
- }
2687
- if (config?.text) {
2688
- if (hasTemplateSyntax(config.text)) {
2689
- return resolveTemplate(config.text, context);
2690
- }
2691
- return config.text;
2692
- }
2693
- return "";
2694
- }
2695
- function applyAttributes(domElement, element, context) {
2696
- const config = element.configuration || {};
2697
- const attributeMap = [
2698
- { key: "id", attr: "id" },
2699
- { key: "src", dynamicKey: "dynamicSrc", attr: "src" },
2700
- { key: "alt", attr: "alt" },
2701
- { key: "href", dynamicKey: "dynamicHref", attr: "href" },
2702
- { key: "target", attr: "target" },
2703
- { key: "placeholder", attr: "placeholder" },
2704
- { key: "type", attr: "type" },
2705
- { key: "name", attr: "name" },
2706
- { key: "value", dynamicKey: "dynamicValue", attr: "value" }
2707
- ];
2708
- for (const { key, dynamicKey, attr } of attributeMap) {
2709
- const dynamicValue = dynamicKey ? config[dynamicKey] : void 0;
2710
- if (dynamicValue !== void 0 && dynamicValue !== null && dynamicValue !== "") {
2711
- const resolved = resolveTemplate(String(dynamicValue), context);
2712
- if (resolved) {
2713
- domElement.setAttribute(attr, resolved);
2714
- }
2715
- continue;
2716
- }
2717
- const staticValue = config[key];
2718
- if (staticValue !== void 0 && staticValue !== null && staticValue !== "") {
2719
- const resolved = hasTemplateSyntax(String(staticValue)) ? resolveTemplate(String(staticValue), context) : String(staticValue);
2720
- if (resolved) {
2721
- domElement.setAttribute(attr, resolved);
2722
- }
2723
- }
2724
- }
2725
- if (config.disabled) domElement.setAttribute("disabled", "");
2726
- if (config.required) domElement.setAttribute("required", "");
2727
- if (config.readOnly) domElement.setAttribute("readonly", "");
2728
- for (const [key, value] of Object.entries(config)) {
2729
- if (key.startsWith("data-") && value !== void 0) {
2730
- const resolved = hasTemplateSyntax(String(value)) ? resolveTemplate(String(value), context) : String(value);
2731
- domElement.setAttribute(key, resolved);
2732
- }
2733
- }
2734
- for (const [key, value] of Object.entries(config)) {
2735
- if (key.startsWith("aria-") && value !== void 0) {
2736
- const resolved = hasTemplateSyntax(String(value)) ? resolveTemplate(String(value), context) : String(value);
2737
- domElement.setAttribute(key, resolved);
2738
- }
2739
- }
2740
- }
2741
- function resolveFunctionBinding(binding, context) {
2742
- if (binding.source === "props" || binding.source === "parent") {
2743
- const path = binding.path;
2744
- if (!path) return void 0;
2745
- const parts = path.split(".");
2746
- let value = context.props;
2747
- for (const part of parts) {
2748
- if (value === null || value === void 0) return void 0;
2749
- value = value[part];
2750
- }
2751
- if (typeof value === "function") {
2752
- return value;
2753
- }
2754
- return void 0;
2755
- }
2756
- if (binding.source === "static" && typeof binding.value === "function") {
2757
- return binding.value;
2758
- }
2759
- return void 0;
2760
- }
2761
- function attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement = false, state) {
2762
- const elementId = element.i;
2763
- if (eventHandlers && eventHandlers[elementId]) {
2764
- const handlers = eventHandlers[elementId];
2765
- for (const [eventName, handler] of Object.entries(handlers)) {
2766
- const domEventName = eventName.replace(/^on/, "").toLowerCase();
2767
- elementState.eventListeners.set(domEventName, handler);
2768
- domElement.addEventListener(domEventName, handler);
2769
- }
2770
- }
2771
- const config = element.configuration || {};
2772
- for (const eventPropName of Object.keys(EVENT_HANDLERS)) {
2773
- const handlerConfig = config[eventPropName];
2774
- if (handlerConfig && handlerConfig.plugins && handlerConfig.plugins.length > 0) {
2775
- const domEventName = toDomEventName(eventPropName);
2776
- if (!elementState.eventListeners.has(domEventName)) {
2777
- if (state?.eventSystem) {
2778
- const handler = state.eventSystem.createHandler(elementId, handlerConfig, context);
2779
- elementState.eventListeners.set(domEventName, handler);
2780
- domElement.addEventListener(domEventName, handler);
2781
- } else {
2782
- const handler = (e) => {
2783
- if (handlerConfig.preventDefault) e.preventDefault();
2784
- if (handlerConfig.stopPropagation) e.stopPropagation();
2785
- console.log(`[Servly] Event ${eventPropName} triggered on ${elementId}`, handlerConfig.plugins);
2786
- };
2787
- elementState.eventListeners.set(domEventName, handler);
2788
- domElement.addEventListener(domEventName, handler);
2789
- }
2790
- }
2791
- }
2792
- }
2793
- const bindings = element.configuration?.bindings?.inputs;
2794
- if (bindings) {
2795
- for (const [propName, binding] of Object.entries(bindings)) {
2796
- if (propName.startsWith("on") && propName.length > 2) {
2797
- const handler = resolveFunctionBinding(binding, context);
2798
- if (handler) {
2799
- const domEventName = propName.slice(2).toLowerCase();
2800
- if (!elementState.eventListeners.has(domEventName)) {
2801
- elementState.eventListeners.set(domEventName, handler);
2802
- domElement.addEventListener(domEventName, handler);
2803
- }
2804
- }
2805
- }
2806
- }
2807
- }
2808
- if (isRootElement && context.props) {
2809
- for (const [propName, value] of Object.entries(context.props)) {
2810
- if (propName.startsWith("on") && propName.length > 2 && typeof value === "function") {
2811
- const domEventName = propName.slice(2).toLowerCase();
2812
- if (!elementState.eventListeners.has(domEventName)) {
2813
- const handler = value;
2814
- elementState.eventListeners.set(domEventName, handler);
2815
- domElement.addEventListener(domEventName, handler);
2816
- }
2817
- }
2818
- }
2819
- }
2820
- }
2821
- function detachEventHandlers(elementState) {
2822
- for (const [eventName, handler] of elementState.eventListeners) {
2823
- elementState.domElement.removeEventListener(eventName, handler);
2824
- }
2825
- elementState.eventListeners.clear();
2826
- }
2827
- function createElement(element, context, eventHandlers, isRootElement = false, state) {
2828
- const tag = getElementTag(element);
2829
- const domElement = document.createElement(tag);
2830
- domElement.setAttribute("data-servly-id", element.i);
2831
- const slotName = element.slotName || element.configuration?.slotName;
2832
- if (element.componentId === "slot" || slotName) {
2833
- const name = slotName || element.i;
2834
- domElement.setAttribute("data-slot", name);
2835
- domElement.setAttribute("data-servly-slot", "true");
2836
- }
2837
- const styles = buildElementStyles(element, context);
2838
- applyStyles(domElement, styles);
2839
- const className = buildClassName(element, context);
2840
- if (className) {
2841
- domElement.className = className;
2842
- }
2843
- applyAttributes(domElement, element, context);
2844
- let textContent = "";
2845
- if (element.componentId === "text" || !isSelfClosing(tag)) {
2846
- textContent = getTextContent(element, context);
2847
- if (textContent && !element.children?.length) {
2848
- domElement.textContent = textContent;
2849
- }
2850
- }
2851
- const elementState = {
2852
- element,
2853
- domElement,
2854
- styles,
2855
- className,
2856
- textContent,
2857
- eventListeners: /* @__PURE__ */ new Map()
2858
- };
2859
- attachEventHandlers(domElement, element, eventHandlers, context, elementState, isRootElement, state);
2860
- return elementState;
2861
- }
2862
- var globalRenderingStack = /* @__PURE__ */ new Set();
2863
- function renderComponentRef(element, container, context, state) {
2864
- const config = element.configuration;
2865
- if (!config?.componentViewRef) return void 0;
2866
- const refId = config.componentViewRef;
2867
- const refVersion = config.componentViewVersion;
2868
- if (globalRenderingStack.has(refId)) {
2869
- console.warn(`Circular dependency detected: ${refId} is already being rendered`);
2870
- const placeholder = document.createElement("div");
2871
- placeholder.setAttribute("data-servly-circular", refId);
2872
- placeholder.textContent = `[Circular: ${refId}]`;
2873
- container.appendChild(placeholder);
2874
- return void 0;
2875
- }
2876
- let component;
2877
- if (state.componentRegistry) {
2878
- component = state.componentRegistry.get(refId, refVersion);
2879
- }
2880
- if (!component) {
2881
- const placeholder = document.createElement("div");
2882
- placeholder.setAttribute("data-servly-loading", refId);
2883
- placeholder.className = "servly-loading";
2884
- container.appendChild(placeholder);
2885
- if (state.onDependencyNeeded) {
2886
- state.onDependencyNeeded(refId, refVersion).then((loaded) => {
2887
- if (loaded && state.componentRegistry) {
2888
- state.componentRegistry.set(refId, refVersion || "latest", loaded);
2889
- container.innerHTML = "";
2890
- renderComponentRef(element, container, context, state);
2891
- }
2892
- }).catch((err) => {
2893
- console.error(`Failed to load dependency ${refId}:`, err);
2894
- placeholder.textContent = `[Failed to load: ${refId}]`;
2895
- placeholder.className = "servly-error";
2896
- });
2897
- }
2898
- return void 0;
2899
- }
2900
- const refProps = config.componentViewProps ? resolveTemplatesDeep(config.componentViewProps, context) : {};
2901
- globalRenderingStack.add(refId);
2902
- const refContext = {
2903
- props: refProps,
2904
- state: context.state,
2905
- context: context.context
2906
- };
2907
- try {
2908
- const result = render({
2909
- container,
2910
- elements: component.layout,
2911
- context: refContext,
2912
- componentRegistry: state.componentRegistry,
2913
- onDependencyNeeded: state.onDependencyNeeded
2914
- });
2915
- return result;
2916
- } finally {
2917
- globalRenderingStack.delete(refId);
2918
- }
2919
- }
2920
- function extractViewId(binding) {
2921
- if (!binding) return null;
2922
- if (typeof binding === "string") return binding;
2923
- if (binding.source === "static" && typeof binding.value === "string") return binding.value;
2924
- if (binding.source === "node" && binding.binding?.viewId) return binding.binding.viewId;
2925
- if (binding.source === "view" && typeof binding.value === "string") return binding.value;
2926
- if (binding.viewId) return binding.viewId;
2927
- if (binding.type === "view" && binding.viewId) return binding.viewId;
2928
- return null;
2929
- }
2930
- function resolveComponentViewInputs(bindings, context, parentInputs) {
2931
- if (!bindings) return {};
2932
- const resolved = {};
2933
- for (const [key, binding] of Object.entries(bindings)) {
2934
- if (!binding) continue;
2935
- const source = (binding.source || "").toLowerCase();
2936
- switch (source) {
2937
- case "static":
2938
- case "value":
2939
- case "constant":
2940
- resolved[key] = binding.value;
2941
- break;
2942
- case "state":
2943
- if (binding.path && context.state) {
2944
- resolved[key] = getValueByPath2(context.state, binding.path);
2945
- }
2946
- break;
2947
- case "props":
2948
- case "parent":
2949
- case "input":
2950
- if (binding.path) {
2951
- const source2 = parentInputs || context.props;
2952
- const resolvedValue = getValueByPath2(source2, binding.path);
2953
- if (resolvedValue !== void 0) {
2954
- resolved[key] = resolvedValue;
2955
- } else {
2956
- resolved[key] = binding.value;
2957
- }
2958
- } else {
2959
- resolved[key] = binding.value;
2960
- }
2961
- break;
2962
- case "node":
2963
- if (binding.binding?.viewId) {
2964
- resolved[key] = binding.binding.viewId;
2965
- } else if (binding.binding) {
2966
- resolved[key] = binding.binding;
2967
- } else {
2968
- resolved[key] = binding.value;
2969
- }
2970
- break;
2971
- default:
2972
- resolved[key] = binding.value;
2973
- }
2974
- }
2975
- return resolved;
2976
- }
2977
- function getValueByPath2(obj, path) {
2978
- if (!obj || !path) return void 0;
2979
- const parts = path.split(".");
2980
- let current = obj;
2981
- for (const part of parts) {
2982
- if (current === null || current === void 0) return void 0;
2983
- current = current[part];
2984
- }
2985
- return current;
2986
- }
2987
- function renderElement(element, tree, context, eventHandlers, elementStates, state, isRootElement = false) {
2988
- if (element.isComponentView) {
2989
- return renderComponentViewElement(element, tree, context, eventHandlers, elementStates, state, isRootElement);
2990
- }
2991
- if (element.componentId === "slot") {
2992
- return renderSlotElement(element, tree, context, eventHandlers, elementStates, state, isRootElement);
2993
- }
2994
- if (element.componentId === "icon") {
2995
- return renderIconElement(element, context, eventHandlers, elementStates, state, isRootElement);
2996
- }
2997
- const elementState = createElement(element, context, eventHandlers, isRootElement, state);
2998
- elementStates.set(element.i, elementState);
2999
- const config = element.configuration;
3000
- if (config?.componentViewRef) {
3001
- const nestedResult = renderComponentRef(element, elementState.domElement, context, state);
3002
- if (nestedResult) {
3003
- elementState.nestedRender = nestedResult;
3004
- }
3005
- return elementState.domElement;
3006
- }
3007
- const children = tree.get(element.i) || [];
3008
- for (const child of children) {
3009
- const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state, false);
3010
- elementState.domElement.appendChild(childElement);
3011
- }
3012
- return elementState.domElement;
3013
- }
3014
- function renderIconElement(element, context, eventHandlers, elementStates, state, isRootElement) {
3015
- const config = element.configuration || {};
3016
- const icon = config.icon;
3017
- const iconColor = config.iconColor || "currentColor";
3018
- const iconSize = config.iconSize || 24;
3019
- const wrapper = document.createElement("div");
3020
- wrapper.setAttribute("data-servly-id", element.i);
3021
- wrapper.className = "pointer-events-none";
3022
- const styles = buildElementStyles(element, context);
3023
- applyStyles(wrapper, styles);
3024
- const className = buildClassName(element, context);
3025
- if (className) {
3026
- wrapper.className = `pointer-events-none ${className}`;
3027
- }
3028
- if (state.iconRenderer && icon) {
3029
- const iconElement = state.iconRenderer(icon, iconSize, iconColor, "");
3030
- if (iconElement) {
3031
- wrapper.appendChild(iconElement);
3032
- }
3033
- } else if (icon) {
3034
- renderIcon(wrapper, icon, iconSize, iconColor);
3035
- }
3036
- const elementState = {
3037
- element,
3038
- domElement: wrapper,
3039
- styles,
3040
- className: wrapper.className,
3041
- textContent: "",
3042
- eventListeners: /* @__PURE__ */ new Map()
3043
- };
3044
- elementStates.set(element.i, elementState);
3045
- attachEventHandlers(wrapper, element, eventHandlers, context, elementState, isRootElement, state);
3046
- return wrapper;
3047
- }
3048
- function renderComponentViewElement(element, tree, context, eventHandlers, elementStates, state, isRootElement) {
3049
- const componentViewId = `${element.componentId}-${element.i}`;
3050
- if (globalRenderingStack.has(componentViewId)) {
3051
- const placeholder = document.createElement("div");
3052
- placeholder.className = "border-2 border-red-500 border-dashed p-4 bg-red-50";
3053
- placeholder.innerHTML = `<p class="text-red-600 text-sm">Recursive component: ${element.componentId}</p>`;
3054
- return placeholder;
3055
- }
3056
- let viewLayout;
3057
- if (state.views?.has(element.componentId)) {
3058
- const view = state.views.get(element.componentId);
3059
- viewLayout = view?.layout;
3060
- }
3061
- if (!viewLayout && state.componentRegistry) {
3062
- const component = state.componentRegistry.get(element.componentId);
3063
- if (component) {
3064
- viewLayout = component.layout;
3065
- }
3066
- }
3067
- if (!viewLayout && state.views === void 0 && state.viewsArray) {
3068
- const viewsArray = state.viewsArray;
3069
- const found = viewsArray.find((v) => v.id === element.componentId);
3070
- if (found) {
3071
- viewLayout = found.layout;
3072
- }
3073
- }
3074
- if (!viewLayout) {
3075
- 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"}`);
3076
- const placeholder = document.createElement("div");
3077
- placeholder.className = "border-2 border-yellow-500 border-dashed p-4 bg-yellow-50";
3078
- placeholder.innerHTML = `<p class="text-yellow-600 text-sm">Component not found: ${element.componentId}</p>`;
3079
- return placeholder;
3080
- }
3081
- const bindings = element.configuration?.bindings?.inputs || {};
3082
- const resolvedInputs = resolveComponentViewInputs(bindings, context, context.props);
3083
- const componentContext = {
3084
- props: { ...context.props, ...resolvedInputs },
3085
- state: context.state,
3086
- context: context.context
3087
- };
3088
- globalRenderingStack.add(componentViewId);
3089
- try {
3090
- const wrapper = document.createElement("div");
3091
- wrapper.id = element.i;
3092
- wrapper.className = "contents";
3093
- const viewTree = buildTree(viewLayout);
3094
- const viewRoots = viewLayout.filter((el) => !el.parent || el.parent === null);
3095
- const nestedState = {
3096
- ...state,
3097
- elements: viewLayout,
3098
- context: componentContext,
3099
- elementStates: /* @__PURE__ */ new Map(),
3100
- rootElement: null,
3101
- renderingStack: new Set(state.renderingStack)
3102
- };
3103
- nestedState.renderingStack.add(componentViewId);
3104
- for (const root of viewRoots) {
3105
- const rootElement = renderElement(
3106
- root,
3107
- viewTree,
3108
- componentContext,
3109
- eventHandlers,
3110
- nestedState.elementStates,
3111
- nestedState,
3112
- true
3113
- );
3114
- wrapper.appendChild(rootElement);
3115
- }
3116
- const elementState = {
3117
- element,
3118
- domElement: wrapper,
3119
- styles: {},
3120
- className: "contents",
3121
- textContent: "",
3122
- eventListeners: /* @__PURE__ */ new Map()
3123
- };
3124
- elementStates.set(element.i, elementState);
3125
- return wrapper;
3126
- } finally {
3127
- globalRenderingStack.delete(componentViewId);
3128
- }
3129
- }
3130
- function renderSlotElement(element, tree, context, eventHandlers, elementStates, state, isRootElement) {
3131
- const elementState = createElement(element, context, eventHandlers, isRootElement, state);
3132
- elementStates.set(element.i, elementState);
3133
- const bindings = element.configuration?.bindings?.inputs || {};
3134
- let childViewId = extractViewId(bindings.child) || extractViewId(bindings.children) || extractViewId(bindings.content);
3135
- if (!childViewId && context.props) {
3136
- childViewId = extractViewId(context.props.child) || extractViewId(context.props.children) || extractViewId(context.props.content);
3137
- }
3138
- if (childViewId && typeof childViewId === "string") {
3139
- let viewLayout;
3140
- if (state.views?.has(childViewId)) {
3141
- const view = state.views.get(childViewId);
3142
- viewLayout = view?.layout;
3143
- }
3144
- if (!viewLayout && state.componentRegistry) {
3145
- const component = state.componentRegistry.get(childViewId);
3146
- if (component) {
3147
- viewLayout = component.layout;
3148
- }
3149
- }
3150
- if (viewLayout) {
3151
- const viewTree = buildTree(viewLayout);
3152
- const viewRoots = viewLayout.filter((el) => !el.parent || el.parent === null);
3153
- const nestedState = {
3154
- ...state,
3155
- elements: viewLayout,
3156
- elementStates: /* @__PURE__ */ new Map(),
3157
- rootElement: null
3158
- };
3159
- for (const root of viewRoots) {
3160
- const rootElement = renderElement(
3161
- root,
3162
- viewTree,
3163
- context,
3164
- eventHandlers,
3165
- nestedState.elementStates,
3166
- nestedState,
3167
- false
3168
- );
3169
- elementState.domElement.appendChild(rootElement);
3170
- }
3171
- return elementState.domElement;
3172
- }
3173
- }
3174
- const children = tree.get(element.i) || [];
3175
- for (const child of children) {
3176
- const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state, false);
3177
- elementState.domElement.appendChild(childElement);
3178
- }
3179
- return elementState.domElement;
3180
- }
3181
- function render(options) {
3182
- const {
3183
- container,
3184
- elements,
3185
- context,
3186
- eventHandlers,
3187
- componentRegistry,
3188
- onDependencyNeeded,
3189
- views: viewsInput,
3190
- enableStateManager,
3191
- initialState,
3192
- onStateChange,
3193
- pluginExecutors,
3194
- onNavigate,
3195
- onApiCall,
3196
- iconRenderer
3197
- } = options;
3198
- let views;
3199
- if (viewsInput instanceof Map) {
3200
- views = viewsInput;
3201
- } else if (Array.isArray(viewsInput)) {
3202
- views = /* @__PURE__ */ new Map();
3203
- for (const view of viewsInput) {
3204
- if (view && view.id) {
3205
- views.set(view.id, view);
3206
- }
3207
- }
3208
- }
3209
- const startTime = performance.now();
3210
- const memorySampler = getMemorySampler();
3211
- const longTaskObserver = getLongTaskObserver();
3212
- const memoryBefore = memorySampler.sample();
3213
- longTaskObserver.start();
3214
- const rootElements = elements.filter((el) => !el.parent || el.parent === null);
3215
- const componentId = rootElements[0]?.componentId || "unknown";
3216
- const version = "latest";
3217
- let stateManager;
3218
- if (enableStateManager) {
3219
- stateManager = new StateManager({
3220
- initialState: initialState || context.state,
3221
- onStateChange: onStateChange ? (event) => onStateChange({
3222
- key: event.key,
3223
- value: event.value,
3224
- previousValue: event.previousValue
3225
- }) : void 0
3226
- });
3227
- }
3228
- const eventSystem = new EventSystem({
3229
- stateManager,
3230
- pluginExecutors,
3231
- onNavigate,
3232
- onApiCall
3233
- });
3234
- const overrideSystem = new OverrideSystem({
3235
- eventSystem,
3236
- stateManager
3237
- });
3238
- const hasAnyOverrides = elements.some((el) => hasOverrides(el));
3239
- const hasAnyDependencyOverrides = elements.some((el) => hasDependencyOverrides(el));
3240
- try {
3241
- const tree = buildTree(elements);
3242
- const state = {
3243
- container,
3244
- elements,
3245
- context,
3246
- eventHandlers,
3247
- elementStates: /* @__PURE__ */ new Map(),
3248
- rootElement: null,
3249
- componentRegistry,
3250
- onDependencyNeeded,
3251
- renderingStack: /* @__PURE__ */ new Set(),
3252
- views,
3253
- eventSystem,
3254
- stateManager,
3255
- overrideSystem,
3256
- enableOverrides: hasAnyOverrides,
3257
- iconRenderer
3258
- };
3259
- container.innerHTML = "";
3260
- if (rootElements.length === 0) {
3261
- const duration2 = performance.now() - startTime;
3262
- const longTasks2 = longTaskObserver.stop();
3263
- analytics.trackRender(componentId, version, duration2, {
3264
- elementCount: 0,
3265
- longTaskCount: longTasks2
3266
- });
3267
- return {
3268
- rootElement: null,
3269
- update: (newContext) => update(state, newContext),
3270
- destroy: () => destroy(state)
3271
- };
3272
- }
3273
- if (rootElements.length === 1) {
3274
- state.rootElement = renderElement(
3275
- rootElements[0],
3276
- tree,
3277
- context,
3278
- eventHandlers,
3279
- state.elementStates,
3280
- state,
3281
- true
3282
- // isRootElement
3283
- );
3284
- container.appendChild(state.rootElement);
3285
- } else {
3286
- const wrapper = document.createElement("div");
3287
- wrapper.setAttribute("data-servly-wrapper", "true");
3288
- for (const root of rootElements) {
3289
- const rootElement = renderElement(root, tree, context, eventHandlers, state.elementStates, state, true);
3290
- wrapper.appendChild(rootElement);
3291
- }
3292
- state.rootElement = wrapper;
3293
- container.appendChild(wrapper);
3294
- }
3295
- if (hasAnyOverrides && overrideSystem) {
3296
- for (const element of elements) {
3297
- if (hasOverrides(element)) {
3298
- overrideSystem.initializeElement(element, context);
3299
- if (hasDependencyOverrides(element)) {
3300
- overrideSystem.startWatching(element, context);
3301
- }
3302
- }
3303
- }
3304
- }
3305
- const duration = performance.now() - startTime;
3306
- const memoryAfter = memorySampler.sample();
3307
- const longTasks = longTaskObserver.stop();
3308
- const heapDelta = memorySampler.calculateDelta(memoryBefore, memoryAfter);
3309
- analytics.trackRender(componentId, version, duration, {
3310
- elementCount: elements.length,
3311
- heapDeltaKB: heapDelta ?? void 0,
3312
- longTaskCount: longTasks,
3313
- hasEventHandlers: !!eventHandlers && Object.keys(eventHandlers).length > 0,
3314
- hasDependencies: !!componentRegistry
3315
- });
3316
- return {
3317
- rootElement: state.rootElement,
3318
- update: (newContext) => update(state, newContext),
3319
- destroy: () => destroy(state)
3320
- };
3321
- } catch (error) {
3322
- longTaskObserver.stop();
3323
- overrideSystem.destroy();
3324
- analytics.trackError(componentId, version, error, {
3325
- errorType: "render"
3326
- });
3327
- throw error;
3328
- }
3329
- }
3330
- function update(state, newContext) {
3331
- state.context = newContext;
3332
- for (const [elementId, elementState] of state.elementStates) {
3333
- const { element, domElement } = elementState;
3334
- const newStyles = buildElementStyles(element, newContext);
3335
- updateStyles(domElement, elementState.styles, newStyles);
3336
- elementState.styles = newStyles;
3337
- const newClassName = buildClassName(element, newContext);
3338
- if (newClassName !== elementState.className) {
3339
- domElement.className = newClassName;
3340
- elementState.className = newClassName;
3341
- }
3342
- const newTextContent = getTextContent(element, newContext);
3343
- if (newTextContent !== elementState.textContent) {
3344
- const children = state.elements.filter((el) => el.parent === elementId);
3345
- if (children.length === 0) {
3346
- domElement.textContent = newTextContent;
3347
- }
3348
- elementState.textContent = newTextContent;
3349
- }
3350
- applyAttributes(domElement, element, newContext);
3351
- }
3352
- }
3353
- function destroy(state) {
3354
- if (state.overrideSystem && state.enableOverrides) {
3355
- for (const element of state.elements) {
3356
- if (hasOverrides(element)) {
3357
- state.overrideSystem.cleanupElement(element, state.context);
3358
- }
3359
- }
3360
- state.overrideSystem.destroy();
3361
- }
3362
- for (const elementState of state.elementStates.values()) {
3363
- detachEventHandlers(elementState);
3364
- if (elementState.nestedRender) {
3365
- elementState.nestedRender.destroy();
3366
- }
3367
- }
3368
- if (state.eventSystem) {
3369
- state.eventSystem.destroy();
3370
- }
3371
- state.elementStates.clear();
3372
- if (state.rootElement && state.rootElement.parentNode) {
3373
- state.rootElement.parentNode.removeChild(state.rootElement);
3374
- }
3375
- state.rootElement = null;
3376
- }
3377
- function renderDynamicList(options) {
3378
- const {
3379
- targetContainer,
3380
- blueprint,
3381
- blueprintVersion,
3382
- data,
3383
- renderType = "renderInto",
3384
- itemKey = "item",
3385
- indexKey = "index",
3386
- componentRegistry,
3387
- context = { props: {} }
3388
- } = options;
3389
- let container;
3390
- if (typeof targetContainer === "string") {
3391
- container = document.querySelector(targetContainer);
3392
- } else {
3393
- container = targetContainer;
3394
- }
3395
- if (!container) {
3396
- console.error(`renderDynamicList: Container not found: ${targetContainer}`);
3397
- return [];
3398
- }
3399
- const blueprintComponent = componentRegistry.get(blueprint, blueprintVersion);
3400
- if (!blueprintComponent) {
3401
- console.error(`renderDynamicList: Blueprint not found: ${blueprint}`);
3402
- return [];
3403
- }
3404
- if (renderType === "renderInto") {
3405
- container.innerHTML = "";
3406
- }
3407
- const results = [];
3408
- const fragment = document.createDocumentFragment();
3409
- data.forEach((item, index) => {
3410
- const itemContainer = document.createElement("div");
3411
- itemContainer.setAttribute("data-servly-list-item", String(index));
3412
- const itemContext = {
3413
- props: {
3414
- ...context.props,
3415
- [itemKey]: item,
3416
- [indexKey]: index
3417
- },
3418
- state: context.state,
3419
- context: context.context
3420
- };
3421
- const result = render({
3422
- container: itemContainer,
3423
- elements: blueprintComponent.layout,
3424
- context: itemContext,
3425
- componentRegistry
3426
- });
3427
- results.push(result);
3428
- fragment.appendChild(itemContainer);
3429
- });
3430
- if (renderType === "prepend") {
3431
- container.insertBefore(fragment, container.firstChild);
3432
- } else {
3433
- container.appendChild(fragment);
3434
- }
3435
- return results;
3436
- }
3437
- function renderNode(options) {
3438
- const {
3439
- container,
3440
- elements,
3441
- nodeId,
3442
- context,
3443
- includeChildren = true,
3444
- eventHandlers,
3445
- componentRegistry,
3446
- views
3447
- } = options;
3448
- const targetNode = elements.find((el) => el.i === nodeId);
3449
- if (!targetNode) {
3450
- console.error(`renderNode: Node not found: ${nodeId}`);
3451
- return null;
3452
- }
3453
- if (!includeChildren) {
3454
- const singleElementLayout = [targetNode];
3455
- return render({
3456
- container,
3457
- elements: singleElementLayout,
3458
- context,
3459
- eventHandlers,
3460
- componentRegistry,
3461
- views
3462
- });
3463
- }
3464
- const descendantIds = /* @__PURE__ */ new Set();
3465
- const collectDescendants = (parentId) => {
3466
- for (const el of elements) {
3467
- if (el.parent === parentId) {
3468
- descendantIds.add(el.i);
3469
- collectDescendants(el.i);
3470
- }
3471
- }
3472
- };
3473
- descendantIds.add(nodeId);
3474
- collectDescendants(nodeId);
3475
- const nodeElements = elements.filter((el) => descendantIds.has(el.i));
3476
- const rootedElements = nodeElements.map(
3477
- (el) => el.i === nodeId ? { ...el, parent: null } : el
3478
- );
3479
- return render({
3480
- container,
3481
- elements: rootedElements,
3482
- context,
3483
- eventHandlers,
3484
- componentRegistry,
3485
- views
3486
- });
3487
- }
3488
- function renderInShadow(options) {
3489
- const { container, mode = "open", styles, injectTailwind: shouldInjectTailwind, ...renderOptions } = options;
3490
- const shadowRoot = container.attachShadow({ mode });
3491
- const innerContainer = document.createElement("div");
3492
- innerContainer.className = "servly-shadow-container";
3493
- shadowRoot.appendChild(innerContainer);
3494
- if (styles) {
3495
- const styleEl = document.createElement("style");
3496
- styleEl.textContent = styles;
3497
- shadowRoot.insertBefore(styleEl, innerContainer);
3498
- }
3499
- if (shouldInjectTailwind) {
3500
- const tailwindLink = document.createElement("link");
3501
- tailwindLink.rel = "stylesheet";
3502
- tailwindLink.href = "https://cdn.tailwindcss.com";
3503
- shadowRoot.insertBefore(tailwindLink, innerContainer);
3504
- }
3505
- const result = render({
3506
- ...renderOptions,
3507
- container: innerContainer
3508
- });
3509
- return {
3510
- ...result,
3511
- shadowRoot
3512
- };
3513
- }
3514
- async function createServlyRenderer(options) {
3515
- const {
3516
- container: containerOption,
3517
- injectTailwind: shouldInjectTailwind = true,
3518
- tailwindConfig,
3519
- initialState,
3520
- onStateChange,
3521
- onNavigate
3522
- } = options;
3523
- let container;
3524
- if (typeof containerOption === "string") {
3525
- const el = document.querySelector(containerOption);
3526
- if (!el) {
3527
- throw new Error(`Container not found: ${containerOption}`);
3528
- }
3529
- container = el;
3530
- } else {
3531
- container = containerOption;
3532
- }
3533
- if (shouldInjectTailwind) {
3534
- const { initServlyTailwind: initServlyTailwind2 } = await import("./tailwind-UHWJOUFF.mjs");
3535
- await initServlyTailwind2(tailwindConfig);
3536
- }
3537
- const activeRenders = [];
3538
- return {
3539
- render: (elements, context = { props: {} }) => {
3540
- const result = render({
3541
- container,
3542
- elements,
3543
- context,
3544
- enableStateManager: true,
3545
- initialState,
3546
- onStateChange,
3547
- onNavigate
3548
- });
3549
- activeRenders.push(result);
3550
- return result;
3551
- },
3552
- renderNode: (elements, nodeId, context = { props: {} }) => {
3553
- const result = renderNode({
3554
- container,
3555
- elements,
3556
- nodeId,
3557
- context
3558
- });
3559
- if (result) {
3560
- activeRenders.push(result);
3561
- }
3562
- return result;
3563
- },
3564
- renderDynamicList,
3565
- destroy: () => {
3566
- for (const result of activeRenders) {
3567
- result.destroy();
3568
- }
3569
- activeRenders.length = 0;
3570
- container.innerHTML = "";
3571
- }
3572
- };
3573
- }
3574
- function createViewsMap(views) {
3575
- const viewsMap = /* @__PURE__ */ new Map();
3576
- for (const view of views) {
3577
- const id = view.id || view._id;
3578
- if (id && view.layout) {
3579
- viewsMap.set(id, {
3580
- id,
3581
- layout: view.layout,
3582
- props: view.props
3583
- });
3584
- }
3585
- }
3586
- return viewsMap;
3587
- }
3588
- function extractReferencedViewIds(elements) {
3589
- const viewIds = /* @__PURE__ */ new Set();
3590
- for (const element of elements) {
3591
- if (element.isComponentView && element.componentId) {
3592
- viewIds.add(element.componentId);
3593
- }
3594
- if (element.configuration?.componentViewRef) {
3595
- viewIds.add(element.configuration.componentViewRef);
3596
- }
3597
- const bindings = element.configuration?.bindings?.inputs;
3598
- if (bindings) {
3599
- for (const binding of Object.values(bindings)) {
3600
- if (binding && typeof binding === "object") {
3601
- if (binding.source === "node" && binding.binding?.viewId) {
3602
- viewIds.add(binding.binding.viewId);
3603
- }
3604
- if (binding.viewId) {
3605
- viewIds.add(binding.viewId);
3606
- }
3607
- }
3608
- }
3609
- }
3610
- }
3611
- return Array.from(viewIds);
3612
- }
3613
- function collectAllViewDependencies(views, startViewId) {
3614
- const collected = /* @__PURE__ */ new Set();
3615
- const toProcess = [startViewId];
3616
- while (toProcess.length > 0) {
3617
- const viewId = toProcess.pop();
3618
- if (collected.has(viewId)) continue;
3619
- collected.add(viewId);
3620
- const view = views.get(viewId);
3621
- if (!view) continue;
3622
- const referencedIds = extractReferencedViewIds(view.layout);
3623
- for (const refId of referencedIds) {
3624
- if (!collected.has(refId)) {
3625
- toProcess.push(refId);
3626
- }
3627
- }
3628
- }
3629
- return collected;
3630
- }
3631
-
3632
- // packages/runtime-core/src/cache.ts
3633
- var DEFAULT_CACHE_CONFIG = {
3634
- maxEntries: 50,
3635
- ttl: 5 * 60 * 1e3,
3636
- // 5 minutes
3637
- storageKeyPrefix: "servly_component_"
3638
- };
3639
- var memoryCache = /* @__PURE__ */ new Map();
3640
- function getCacheKey(id, version = "latest") {
3641
- return `${id}@${version}`;
3642
- }
3643
- function getFromMemoryCache(id, version = "latest", config = DEFAULT_CACHE_CONFIG) {
3644
- const key = getCacheKey(id, version);
3645
- const entry = memoryCache.get(key);
3646
- if (!entry) {
3647
- return null;
3648
- }
3649
- const ttl = config.ttl ?? DEFAULT_CACHE_CONFIG.ttl;
3650
- if (Date.now() - entry.timestamp > ttl) {
3651
- memoryCache.delete(key);
3652
- return null;
3653
- }
3654
- entry.accessCount++;
3655
- entry.lastAccessed = Date.now();
3656
- return entry.data;
3657
- }
3658
- function setInMemoryCache(id, version, data, config = DEFAULT_CACHE_CONFIG) {
3659
- const key = getCacheKey(id, version);
3660
- const maxEntries = config.maxEntries ?? DEFAULT_CACHE_CONFIG.maxEntries;
3661
- if (memoryCache.size >= maxEntries && !memoryCache.has(key)) {
3662
- evictLRUFromMemory();
3663
- }
3664
- memoryCache.set(key, {
3665
- data,
3666
- timestamp: Date.now(),
3667
- version,
3668
- accessCount: 1,
3669
- lastAccessed: Date.now()
3670
- });
3671
- }
3672
- function evictLRUFromMemory() {
3673
- let lruKey = null;
3674
- let lruTime = Infinity;
3675
- for (const [key, entry] of memoryCache.entries()) {
3676
- if (entry.lastAccessed < lruTime) {
3677
- lruTime = entry.lastAccessed;
3678
- lruKey = key;
3679
- }
3680
- }
3681
- if (lruKey) {
3682
- memoryCache.delete(lruKey);
3683
- }
3684
- }
3685
- function clearMemoryCache() {
3686
- memoryCache.clear();
3687
- }
3688
- function getMemoryCacheSize() {
3689
- return memoryCache.size;
3690
- }
3691
- function isLocalStorageAvailable() {
3692
- try {
3693
- const test = "__servly_test__";
3694
- localStorage.setItem(test, test);
3695
- localStorage.removeItem(test);
3696
- return true;
3697
- } catch {
3698
- return false;
3699
- }
3700
- }
3701
- function getStorageKey(id, version, config) {
3702
- const prefix = config.storageKeyPrefix ?? DEFAULT_CACHE_CONFIG.storageKeyPrefix;
3703
- return `${prefix}${getCacheKey(id, version)}`;
3704
- }
3705
- function getFromLocalStorage(id, version = "latest", config = DEFAULT_CACHE_CONFIG) {
3706
- if (!isLocalStorageAvailable()) {
3707
- return null;
3708
- }
3709
- try {
3710
- const key = getStorageKey(id, version, config);
3711
- const stored = localStorage.getItem(key);
3712
- if (!stored) {
3713
- return null;
3714
- }
3715
- const entry = JSON.parse(stored);
3716
- const ttl = config.ttl ?? DEFAULT_CACHE_CONFIG.ttl;
3717
- if (Date.now() - entry.timestamp > ttl) {
3718
- localStorage.removeItem(key);
3719
- return null;
3720
- }
3721
- entry.accessCount++;
3722
- entry.lastAccessed = Date.now();
3723
- localStorage.setItem(key, JSON.stringify(entry));
3724
- return entry.data;
3725
- } catch (error) {
3726
- console.warn("Error reading from localStorage cache:", error);
3727
- return null;
3728
- }
3729
- }
3730
- function setInLocalStorage(id, version, data, config = DEFAULT_CACHE_CONFIG) {
3731
- if (!isLocalStorageAvailable()) {
3732
- return;
3733
- }
3734
- try {
3735
- const key = getStorageKey(id, version, config);
3736
- const maxEntries = config.maxEntries ?? DEFAULT_CACHE_CONFIG.maxEntries;
3737
- const currentCount = getLocalStorageCacheCount(config);
3738
- if (currentCount >= maxEntries) {
3739
- evictLRUFromLocalStorage(config);
3740
- }
3741
- const entry = {
3742
- data,
3743
- timestamp: Date.now(),
3744
- version,
3745
- accessCount: 1,
3746
- lastAccessed: Date.now()
3747
- };
3748
- localStorage.setItem(key, JSON.stringify(entry));
3749
- } catch (error) {
3750
- console.warn("Error writing to localStorage cache:", error);
3751
- if (error instanceof DOMException && error.name === "QuotaExceededError") {
3752
- evictLRUFromLocalStorage(config);
3753
- try {
3754
- const key = getStorageKey(id, version, config);
3755
- const entry = {
3756
- data,
3757
- timestamp: Date.now(),
3758
- version,
3759
- accessCount: 1,
3760
- lastAccessed: Date.now()
3761
- };
3762
- localStorage.setItem(key, JSON.stringify(entry));
3763
- } catch {
3764
- }
3765
- }
3766
- }
3767
- }
3768
- function getLocalStorageCacheCount(config) {
3769
- const prefix = config.storageKeyPrefix ?? DEFAULT_CACHE_CONFIG.storageKeyPrefix;
3770
- let count = 0;
3771
- for (let i = 0; i < localStorage.length; i++) {
3772
- const key = localStorage.key(i);
3773
- if (key?.startsWith(prefix)) {
3774
- count++;
3775
- }
3776
- }
3777
- return count;
3778
- }
3779
- function evictLRUFromLocalStorage(config) {
3780
- const prefix = config.storageKeyPrefix ?? DEFAULT_CACHE_CONFIG.storageKeyPrefix;
3781
- let lruKey = null;
3782
- let lruTime = Infinity;
3783
- for (let i = 0; i < localStorage.length; i++) {
3784
- const key = localStorage.key(i);
3785
- if (!key?.startsWith(prefix)) continue;
3786
- try {
3787
- const stored = localStorage.getItem(key);
3788
- if (stored) {
3789
- const entry = JSON.parse(stored);
3790
- if (entry.lastAccessed < lruTime) {
3791
- lruTime = entry.lastAccessed;
3792
- lruKey = key;
3793
- }
3794
- }
3795
- } catch {
3796
- if (key) localStorage.removeItem(key);
3797
- }
3798
- }
3799
- if (lruKey) {
3800
- localStorage.removeItem(lruKey);
3801
- }
3802
- }
3803
- function clearLocalStorageCache(config = DEFAULT_CACHE_CONFIG) {
3804
- if (!isLocalStorageAvailable()) {
3805
- return;
3806
- }
3807
- const prefix = config.storageKeyPrefix ?? DEFAULT_CACHE_CONFIG.storageKeyPrefix;
3808
- const keysToRemove = [];
3809
- for (let i = 0; i < localStorage.length; i++) {
3810
- const key = localStorage.key(i);
3811
- if (key?.startsWith(prefix)) {
3812
- keysToRemove.push(key);
3813
- }
3814
- }
3815
- keysToRemove.forEach((key) => localStorage.removeItem(key));
3816
- }
3817
- function getFromCache(id, version = "latest", strategy = "memory", config = DEFAULT_CACHE_CONFIG) {
3818
- if (strategy === "none") {
3819
- return null;
3820
- }
3821
- const memoryResult = getFromMemoryCache(id, version, config);
3822
- if (memoryResult) {
3823
- return memoryResult;
3824
- }
3825
- if (strategy === "localStorage") {
3826
- const localResult = getFromLocalStorage(id, version, config);
3827
- if (localResult) {
3828
- setInMemoryCache(id, version, localResult, config);
3829
- return localResult;
3830
- }
3831
- }
3832
- return null;
3833
- }
3834
- function setInCache(id, version, data, strategy = "memory", config = DEFAULT_CACHE_CONFIG) {
3835
- if (strategy === "none") {
3836
- return;
3837
- }
3838
- setInMemoryCache(id, version, data, config);
3839
- if (strategy === "localStorage") {
3840
- setInLocalStorage(id, version, data, config);
3841
- }
3842
- }
3843
- function clearAllCaches(config = DEFAULT_CACHE_CONFIG) {
3844
- clearMemoryCache();
3845
- clearLocalStorageCache(config);
3846
- }
3847
- function invalidateCache(id, version, config = DEFAULT_CACHE_CONFIG) {
3848
- if (version) {
3849
- const key = getCacheKey(id, version);
3850
- memoryCache.delete(key);
3851
- if (isLocalStorageAvailable()) {
3852
- const storageKey = getStorageKey(id, version, config);
3853
- localStorage.removeItem(storageKey);
3854
- }
3855
- } else {
3856
- const prefix = `${id}@`;
3857
- for (const key of memoryCache.keys()) {
3858
- if (key.startsWith(prefix)) {
3859
- memoryCache.delete(key);
3860
- }
3861
- }
3862
- if (isLocalStorageAvailable()) {
3863
- const storagePrefix = (config.storageKeyPrefix ?? DEFAULT_CACHE_CONFIG.storageKeyPrefix) + prefix;
3864
- const keysToRemove = [];
3865
- for (let i = 0; i < localStorage.length; i++) {
3866
- const key = localStorage.key(i);
3867
- if (key?.startsWith(storagePrefix)) {
3868
- keysToRemove.push(key);
3869
- }
3870
- }
3871
- keysToRemove.forEach((key) => localStorage.removeItem(key));
3872
- }
3873
- }
3874
- }
3875
-
3876
- // packages/runtime-core/src/fetcher.ts
3877
- var DEFAULT_RETRY_CONFIG = {
3878
- maxRetries: 3,
3879
- initialDelay: 1e3,
3880
- maxDelay: 1e4,
3881
- backoffMultiplier: 2
3882
- };
3883
- var registryBaseUrl = "/api/views/registry";
3884
- function setRegistryUrl(url) {
3885
- registryBaseUrl = url;
3886
- }
3887
- function getRegistryUrl() {
3888
- if (typeof window !== "undefined") {
3889
- const envUrl = window.__SERVLY_REGISTRY_URL__;
3890
- if (envUrl) return envUrl;
3891
- }
3892
- if (typeof process !== "undefined" && process.env?.SERVLY_REGISTRY_URL) {
3893
- return process.env.SERVLY_REGISTRY_URL;
3894
- }
3895
- return registryBaseUrl;
3896
- }
3897
- function calculateBackoffDelay(retryCount, config) {
3898
- const delay = config.initialDelay * Math.pow(config.backoffMultiplier, retryCount);
3899
- return Math.min(delay, config.maxDelay);
3900
- }
3901
- function sleep(ms) {
3902
- return new Promise((resolve) => setTimeout(resolve, ms));
3903
- }
3904
- function buildViewsMap(views) {
3905
- if (!views || views.length === 0) return void 0;
3906
- const viewsMap = /* @__PURE__ */ new Map();
3907
- for (const view of views) {
3908
- viewsMap.set(view.id, {
3909
- id: view.id,
3910
- layout: view.layout,
3911
- props: view.props
3912
- });
3913
- }
3914
- return viewsMap;
3915
- }
3916
- async function resolveVersionFromApi(id, specifier, apiKey) {
3917
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
3918
- return specifier;
3919
- }
3920
- const baseUrl = getRegistryUrl();
3921
- const headers = {
3922
- "Content-Type": "application/json"
3923
- };
3924
- if (apiKey) {
3925
- headers["Authorization"] = `Bearer ${apiKey}`;
3926
- }
3927
- try {
3928
- const response = await fetch(
3929
- `${baseUrl}/${id}/resolve?specifier=${encodeURIComponent(specifier)}`,
3930
- { headers }
3931
- );
3932
- if (!response.ok) {
3933
- throw new Error(`Failed to resolve version: ${response.statusText}`);
3934
- }
3935
- const data = await response.json();
3936
- if (data.success && data.data?.resolved) {
3937
- return data.data.resolved;
3938
- }
3939
- throw new Error(data.error || "Failed to resolve version");
3940
- } catch (error) {
3941
- console.warn(`Failed to resolve version for ${id}@${specifier}:`, error);
3942
- return "latest";
3943
- }
3944
- }
3945
- async function fetchFromRegistry(id, version, apiKey, includeBundle, includeViews) {
3946
- const baseUrl = getRegistryUrl();
3947
- const headers = {
3948
- "Content-Type": "application/json"
3949
- };
3950
- if (apiKey) {
3951
- headers["Authorization"] = `Bearer ${apiKey}`;
3952
- }
3953
- let url;
3954
- if (version && version !== "latest" && /^\d+\.\d+\.\d+/.test(version)) {
3955
- url = `${baseUrl}/${id}/versions/${version}`;
3956
- } else if (version && version !== "latest") {
3957
- url = `${baseUrl}/${id}?version=${encodeURIComponent(version)}`;
3958
- } else {
3959
- url = `${baseUrl}/${id}`;
3960
- }
3961
- if (includeBundle) {
3962
- url += (url.includes("?") ? "&" : "?") + "bundle=true";
3963
- }
3964
- if (includeViews) {
3965
- url += (url.includes("?") ? "&" : "?") + "includeViews=true";
3966
- }
3967
- const response = await fetch(url, { headers });
3968
- if (!response.ok) {
3969
- if (response.status === 404) {
3970
- throw new Error(`View not found: ${id}@${version}`);
3971
- }
3972
- if (response.status === 401) {
3973
- throw new Error("Unauthorized: Invalid or missing API key");
3974
- }
3975
- if (response.status === 403) {
3976
- throw new Error("Forbidden: Access denied to this view");
3977
- }
3978
- throw new Error(`Failed to fetch view: ${response.statusText}`);
3979
- }
3980
- const data = await response.json();
3981
- if (!data.success || !data.data) {
3982
- throw new Error(data.error || "Failed to fetch view data");
3983
- }
3984
- const result = data.data;
3985
- if (result.viewId && !result.id) {
3986
- result.id = result.viewId;
3987
- }
3988
- return result;
3989
- }
3990
- async function fetchComponent(id, options = {}) {
3991
- const {
3992
- version = "latest",
3993
- apiKey,
3994
- cacheStrategy = "memory",
3995
- cacheConfig = DEFAULT_CACHE_CONFIG,
3996
- retryConfig = {},
3997
- forceRefresh = false,
3998
- signal,
3999
- bundleStrategy = "eager",
4000
- includeBundle = true,
4001
- includeViews = true
4002
- // Default to true - fetch all views needed
4003
- } = options;
4004
- const fullRetryConfig = {
4005
- ...DEFAULT_RETRY_CONFIG,
4006
- ...retryConfig
4007
- };
4008
- const startTime = performance.now();
4009
- if (!forceRefresh) {
4010
- const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
4011
- if (cached) {
4012
- let registry;
4013
- if (cached.bundle) {
4014
- registry = buildRegistryFromBundle(cached);
4015
- }
4016
- const views = buildViewsMap(cached.views);
4017
- const duration = performance.now() - startTime;
4018
- analytics.trackFetch(id, cached.version, duration, true, {
4019
- cacheHit: true,
4020
- fetchDuration: Math.round(duration)
4021
- });
4022
- return {
4023
- data: cached,
4024
- fromCache: true,
4025
- version: cached.version,
4026
- registry,
4027
- views
4028
- };
4029
- }
4030
- }
4031
- const resolvedVersion = await resolveVersionFromApi(id, version, apiKey);
4032
- if (!forceRefresh && resolvedVersion !== version) {
4033
- const cached = getFromCache(id, resolvedVersion, cacheStrategy, cacheConfig);
4034
- if (cached) {
4035
- let registry;
4036
- if (cached.bundle) {
4037
- registry = buildRegistryFromBundle(cached);
4038
- }
4039
- const views = buildViewsMap(cached.views);
4040
- return {
4041
- data: cached,
4042
- fromCache: true,
4043
- version: resolvedVersion,
4044
- registry,
4045
- views
4046
- };
4047
- }
4048
- }
4049
- let lastError = null;
4050
- const shouldIncludeBundle = bundleStrategy !== "none" && includeBundle;
4051
- for (let attempt = 0; attempt <= fullRetryConfig.maxRetries; attempt++) {
4052
- if (signal?.aborted) {
4053
- throw new Error("Fetch aborted");
4054
- }
4055
- try {
4056
- const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle, includeViews);
4057
- setInCache(id, resolvedVersion, data, cacheStrategy, cacheConfig);
4058
- if (version !== resolvedVersion) {
4059
- setInCache(id, version, data, cacheStrategy, cacheConfig);
4060
- }
4061
- let registry;
4062
- let pendingDependencies;
4063
- if (data.bundle) {
4064
- registry = buildRegistryFromBundle(data);
4065
- } else if (data.dependencies && bundleStrategy === "lazy") {
4066
- pendingDependencies = Object.entries(data.dependencies).map(([depId, entry]) => ({
4067
- id: depId,
4068
- version: entry.resolved || entry.version
4069
- }));
4070
- }
4071
- const views = buildViewsMap(data.views);
4072
- const duration = performance.now() - startTime;
4073
- analytics.trackFetch(id, resolvedVersion, duration, false, {
4074
- cacheHit: false,
4075
- fetchDuration: Math.round(duration),
4076
- dependencyCount: data.dependencies ? Object.keys(data.dependencies).length : 0
4077
- });
4078
- return {
4079
- data,
4080
- fromCache: false,
4081
- version: resolvedVersion,
4082
- registry,
4083
- pendingDependencies,
4084
- views
4085
- };
4086
- } catch (error) {
4087
- lastError = error instanceof Error ? error : new Error(String(error));
4088
- if (lastError.message.includes("not found") || lastError.message.includes("Unauthorized") || lastError.message.includes("Forbidden")) {
4089
- throw lastError;
4090
- }
4091
- if (attempt < fullRetryConfig.maxRetries) {
4092
- const delay = calculateBackoffDelay(attempt, fullRetryConfig);
4093
- await sleep(delay);
4094
- }
4095
- }
4096
- }
4097
- const finalError = lastError || new Error("Failed to fetch component");
4098
- analytics.trackError(id, version, finalError, {
4099
- errorType: "fetch"
4100
- });
4101
- throw finalError;
4102
- }
4103
- async function fetchComponentWithDependencies(id, options = {}) {
4104
- const result = await fetchComponent(id, { ...options, includeBundle: true });
4105
- if (result.pendingDependencies && result.pendingDependencies.length > 0) {
4106
- const { createRegistry: createRegistry2 } = await import("./registry-HKUXXQ5V.mjs");
4107
- const registry = result.registry || createRegistry2();
4108
- await Promise.all(
4109
- result.pendingDependencies.map(async (dep) => {
4110
- try {
4111
- const depResult = await fetchComponent(dep.id, {
4112
- ...options,
4113
- version: dep.version,
4114
- bundleStrategy: "none"
4115
- // Don't recursively bundle
4116
- });
4117
- registry.set(dep.id, depResult.version, {
4118
- layout: depResult.data.layout,
4119
- propsInterface: depResult.data.propsInterface
4120
- });
4121
- } catch (err) {
4122
- console.warn(`Failed to fetch dependency ${dep.id}:`, err);
4123
- }
4124
- })
4125
- );
4126
- result.registry = registry;
4127
- result.pendingDependencies = void 0;
4128
- }
4129
- return result;
4130
- }
4131
- async function batchFetchComponents(components, options = {}) {
4132
- const baseUrl = getRegistryUrl();
4133
- const headers = {
4134
- "Content-Type": "application/json"
4135
- };
4136
- if (options.apiKey) {
4137
- headers["Authorization"] = `Bearer ${options.apiKey}`;
4138
- }
4139
- const response = await fetch(`${baseUrl}/batch`, {
4140
- method: "POST",
4141
- headers,
4142
- body: JSON.stringify({ components })
4143
- });
4144
- if (!response.ok) {
4145
- throw new Error(`Batch fetch failed: ${response.statusText}`);
4146
- }
4147
- const data = await response.json();
4148
- if (!data.success || !data.data) {
4149
- throw new Error(data.error || "Batch fetch failed");
4150
- }
4151
- return data.data;
4152
- }
4153
- async function prefetchComponents(ids, options = {}) {
4154
- const promises = ids.map(
4155
- ({ id, version }) => fetchComponent(id, { ...options, version }).catch((error) => {
4156
- console.warn(`Failed to prefetch view ${id}:`, error);
4157
- })
4158
- );
4159
- await Promise.all(promises);
4160
- }
4161
- async function isComponentAvailable(id, version = "latest", options = {}) {
4162
- const { cacheStrategy = "memory", cacheConfig = DEFAULT_CACHE_CONFIG } = options;
4163
- const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
4164
- if (cached) {
4165
- return { available: true, cached: true, version: cached.version };
4166
- }
4167
- const baseUrl = getRegistryUrl();
4168
- const headers = {
4169
- "Content-Type": "application/json"
4170
- };
4171
- if (options.apiKey) {
4172
- headers["Authorization"] = `Bearer ${options.apiKey}`;
4173
- }
4174
- try {
4175
- const url = version && version !== "latest" ? `${baseUrl}/${id}/available?version=${encodeURIComponent(version)}` : `${baseUrl}/${id}/available`;
4176
- const response = await fetch(url, { headers });
4177
- if (!response.ok) {
4178
- return { available: false, cached: false };
4179
- }
4180
- const data = await response.json();
4181
- if (data.success && data.data) {
4182
- return data.data;
4183
- }
4184
- return { available: false, cached: false };
4185
- } catch {
4186
- return { available: false, cached: false };
4187
- }
4188
- }
4189
- async function getDependencyTree(id, options = {}) {
4190
- const baseUrl = getRegistryUrl();
4191
- const headers = {
4192
- "Content-Type": "application/json"
4193
- };
4194
- if (options.apiKey) {
4195
- headers["Authorization"] = `Bearer ${options.apiKey}`;
4196
- }
4197
- const params = new URLSearchParams();
4198
- if (options.version) params.set("version", options.version);
4199
- if (options.depth) params.set("depth", String(options.depth));
4200
- const url = `${baseUrl}/${id}/dependencies${params.toString() ? "?" + params.toString() : ""}`;
4201
- const response = await fetch(url, { headers });
4202
- if (!response.ok) {
4203
- throw new Error(`Failed to get dependency tree: ${response.statusText}`);
4204
- }
4205
- const data = await response.json();
4206
- if (!data.success || !data.data) {
4207
- throw new Error(data.error || "Failed to get dependency tree");
4208
- }
4209
- return data.data;
4210
- }
4211
-
4212
- // packages/runtime-core/src/version.ts
4213
- function parseVersion(version) {
4214
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
4215
- if (!match) return null;
4216
- return {
4217
- major: parseInt(match[1], 10),
4218
- minor: parseInt(match[2], 10),
4219
- patch: parseInt(match[3], 10)
4220
- };
4221
- }
4222
- function compareVersions(a, b) {
4223
- const parsedA = parseVersion(a);
4224
- const parsedB = parseVersion(b);
4225
- if (!parsedA || !parsedB) return 0;
4226
- if (parsedA.major !== parsedB.major) {
4227
- return parsedA.major > parsedB.major ? 1 : -1;
4228
- }
4229
- if (parsedA.minor !== parsedB.minor) {
4230
- return parsedA.minor > parsedB.minor ? 1 : -1;
4231
- }
4232
- if (parsedA.patch !== parsedB.patch) {
4233
- return parsedA.patch > parsedB.patch ? 1 : -1;
4234
- }
4235
- return 0;
4236
- }
4237
- function satisfiesVersion(version, specifier) {
4238
- if (specifier === "latest" || specifier === "*") {
4239
- return true;
4240
- }
4241
- const parsed = parseVersion(version);
4242
- if (!parsed) return false;
4243
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
4244
- return version === specifier;
4245
- }
4246
- if (specifier.startsWith("^")) {
4247
- const specParsed = parseVersion(specifier.slice(1));
4248
- if (!specParsed) return false;
4249
- if (parsed.major !== specParsed.major) return false;
4250
- if (parsed.major === 0) {
4251
- return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
4252
- }
4253
- return compareVersions(version, specifier.slice(1)) >= 0;
4254
- }
4255
- if (specifier.startsWith("~")) {
4256
- const specParsed = parseVersion(specifier.slice(1));
4257
- if (!specParsed) return false;
4258
- return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
4259
- }
4260
- if (specifier.startsWith(">=")) {
4261
- return compareVersions(version, specifier.slice(2)) >= 0;
4262
- }
4263
- if (specifier.startsWith(">")) {
4264
- return compareVersions(version, specifier.slice(1)) > 0;
4265
- }
4266
- if (specifier.startsWith("<=")) {
4267
- return compareVersions(version, specifier.slice(2)) <= 0;
4268
- }
4269
- if (specifier.startsWith("<")) {
4270
- return compareVersions(version, specifier.slice(1)) < 0;
4271
- }
4272
- return false;
4273
- }
4274
- function resolveVersion(versions, specifier = "latest") {
4275
- if (versions.length === 0) {
4276
- return null;
4277
- }
4278
- const sorted = [...versions].sort((a, b) => compareVersions(b, a));
4279
- if (specifier === "latest" || specifier === "*") {
4280
- return sorted[0];
4281
- }
4282
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
4283
- return versions.includes(specifier) ? specifier : null;
4284
- }
4285
- for (const version of sorted) {
4286
- if (satisfiesVersion(version, specifier)) {
4287
- return version;
4288
- }
4289
- }
4290
- return null;
4291
- }
4292
- function bumpVersion(currentVersion, bumpType) {
4293
- const parsed = parseVersion(currentVersion);
4294
- if (!parsed) return "1.0.0";
4295
- switch (bumpType) {
4296
- case "major":
4297
- return `${parsed.major + 1}.0.0`;
4298
- case "minor":
4299
- return `${parsed.major}.${parsed.minor + 1}.0`;
4300
- case "patch":
4301
- return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
4302
- default:
4303
- return currentVersion;
4304
- }
4305
- }
4306
- function isValidSpecifier(specifier) {
4307
- if (specifier === "latest" || specifier === "*") {
4308
- return true;
4309
- }
4310
- if (/^\d+\.\d+\.\d+$/.test(specifier)) {
4311
- return true;
4312
- }
4313
- if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
4314
- return true;
4315
- }
4316
- return false;
4317
- }
4318
- function formatVersion(version) {
4319
- const parsed = parseVersion(version);
4320
- if (!parsed) return version;
4321
- return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
4322
- }
4323
-
4324
- // packages/runtime-core/src/testRunner.ts
4325
- function runTestCase(elements, testCase, container) {
4326
- const startTime = performance.now();
4327
- const assertionResults = [];
4328
- let renderResult = null;
4329
- let error;
4330
- const testContainer = container || document.createElement("div");
4331
- if (!container) {
4332
- document.body.appendChild(testContainer);
4333
- }
4334
- try {
4335
- const context = {
4336
- props: testCase.props
4337
- };
4338
- renderResult = render({
4339
- container: testContainer,
4340
- elements,
4341
- context
4342
- });
4343
- for (const assertion of testCase.assertions) {
4344
- const result = validateAssertion(testContainer, assertion);
4345
- assertionResults.push(result);
4346
- }
4347
- } catch (err) {
4348
- error = err instanceof Error ? err.message : String(err);
4349
- } finally {
4350
- if (renderResult) {
4351
- renderResult.destroy();
4352
- }
4353
- if (!container) {
4354
- document.body.removeChild(testContainer);
4355
- }
4356
- }
4357
- const duration = performance.now() - startTime;
4358
- const passed = !error && assertionResults.every((r) => r.passed);
4359
- return {
4360
- testId: testCase.id,
4361
- testName: testCase.name,
4362
- passed,
4363
- assertions: assertionResults,
4364
- duration,
4365
- error
4366
- };
4367
- }
4368
- function runAllTests(elements, testCases) {
4369
- const startTime = performance.now();
4370
- const results = [];
4371
- for (const testCase of testCases) {
4372
- const result = runTestCase(elements, testCase);
4373
- results.push(result);
4374
- }
4375
- const duration = performance.now() - startTime;
4376
- const passed = results.filter((r) => r.passed).length;
4377
- const failed = results.filter((r) => !r.passed).length;
4378
- return {
4379
- total: testCases.length,
4380
- passed,
4381
- failed,
4382
- duration,
4383
- results
4384
- };
4385
- }
4386
- function validateAssertion(container, assertion) {
4387
- const elements = container.querySelectorAll(assertion.selector);
4388
- switch (assertion.type) {
4389
- case "exists":
4390
- return {
4391
- assertion,
4392
- passed: elements.length > 0,
4393
- actual: elements.length > 0,
4394
- expected: true,
4395
- message: elements.length > 0 ? `Element "${assertion.selector}" exists` : `Element "${assertion.selector}" not found`
4396
- };
4397
- case "count":
4398
- const count = elements.length;
4399
- const expectedCount = assertion.expected;
4400
- return {
4401
- assertion,
4402
- passed: count === expectedCount,
4403
- actual: count,
4404
- expected: expectedCount,
4405
- message: count === expectedCount ? `Found ${count} elements matching "${assertion.selector}"` : `Expected ${expectedCount} elements, found ${count}`
4406
- };
4407
- case "text":
4408
- if (elements.length === 0) {
4409
- return {
4410
- assertion,
4411
- passed: false,
4412
- actual: null,
4413
- expected: assertion.expected,
4414
- message: `Element "${assertion.selector}" not found`
4415
- };
4416
- }
4417
- const textContent = elements[0].textContent?.trim();
4418
- const expectedText = assertion.expected;
4419
- return {
4420
- assertion,
4421
- passed: textContent === expectedText,
4422
- actual: textContent,
4423
- expected: expectedText,
4424
- message: textContent === expectedText ? `Text matches: "${expectedText}"` : `Expected text "${expectedText}", got "${textContent}"`
4425
- };
4426
- case "attribute":
4427
- if (elements.length === 0) {
4428
- return {
4429
- assertion,
4430
- passed: false,
4431
- actual: null,
4432
- expected: assertion.expected,
4433
- message: `Element "${assertion.selector}" not found`
4434
- };
4435
- }
4436
- const attrValue = elements[0].getAttribute(assertion.attribute || "");
4437
- const expectedAttr = assertion.expected;
4438
- return {
4439
- assertion,
4440
- passed: attrValue === expectedAttr,
4441
- actual: attrValue,
4442
- expected: expectedAttr,
4443
- message: attrValue === expectedAttr ? `Attribute "${assertion.attribute}" matches` : `Expected attribute "${assertion.attribute}" to be "${expectedAttr}", got "${attrValue}"`
4444
- };
4445
- case "class":
4446
- if (elements.length === 0) {
4447
- return {
4448
- assertion,
4449
- passed: false,
4450
- actual: null,
4451
- expected: assertion.expected,
4452
- message: `Element "${assertion.selector}" not found`
4453
- };
4454
- }
4455
- const hasClass2 = elements[0].classList.contains(assertion.expected);
4456
- return {
4457
- assertion,
4458
- passed: hasClass2,
4459
- actual: Array.from(elements[0].classList),
4460
- expected: assertion.expected,
4461
- message: hasClass2 ? `Element has class "${assertion.expected}"` : `Element does not have class "${assertion.expected}"`
4462
- };
4463
- case "style":
4464
- if (elements.length === 0) {
4465
- return {
4466
- assertion,
4467
- passed: false,
4468
- actual: null,
4469
- expected: assertion.expected,
4470
- message: `Element "${assertion.selector}" not found`
4471
- };
4472
- }
4473
- const el = elements[0];
4474
- const styleValue = el.style.getPropertyValue(assertion.property || "");
4475
- const expectedStyle = assertion.expected;
4476
- return {
4477
- assertion,
4478
- passed: styleValue === expectedStyle,
4479
- actual: styleValue,
4480
- expected: expectedStyle,
4481
- message: styleValue === expectedStyle ? `Style "${assertion.property}" matches` : `Expected style "${assertion.property}" to be "${expectedStyle}", got "${styleValue}"`
4482
- };
4483
- case "visible":
4484
- if (elements.length === 0) {
4485
- return {
4486
- assertion,
4487
- passed: false,
4488
- actual: null,
4489
- expected: true,
4490
- message: `Element "${assertion.selector}" not found`
4491
- };
4492
- }
4493
- const visible = isElementVisible(elements[0]);
4494
- const expectedVisible = assertion.expected !== false;
4495
- return {
4496
- assertion,
4497
- passed: visible === expectedVisible,
4498
- actual: visible,
4499
- expected: expectedVisible,
4500
- message: visible === expectedVisible ? `Element visibility matches expected` : `Expected element to be ${expectedVisible ? "visible" : "hidden"}`
4501
- };
4502
- default:
4503
- return {
4504
- assertion,
4505
- passed: false,
4506
- message: `Unknown assertion type: ${assertion.type}`
4507
- };
4508
- }
4509
- }
4510
- function isElementVisible(element) {
4511
- const style = window.getComputedStyle(element);
4512
- return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0" && element.offsetWidth > 0 && element.offsetHeight > 0;
4513
- }
4514
- function validateProps(props, definitions) {
4515
- const errors = [];
4516
- for (const def of definitions) {
4517
- const value = props[def.name];
4518
- if (def.required && (value === void 0 || value === null)) {
4519
- errors.push(`Required prop "${def.name}" is missing`);
4520
- continue;
4521
- }
4522
- if (value === void 0) continue;
4523
- const actualType = Array.isArray(value) ? "array" : typeof value;
4524
- if (def.type === "array" && !Array.isArray(value)) {
4525
- errors.push(`Prop "${def.name}" should be an array, got ${actualType}`);
4526
- } else if (def.type !== "array" && actualType !== def.type) {
4527
- errors.push(`Prop "${def.name}" should be ${def.type}, got ${actualType}`);
4528
- }
4529
- }
4530
- return {
4531
- valid: errors.length === 0,
4532
- errors
4533
- };
4534
- }
4535
- function generateTestCases(definitions) {
4536
- const testCases = [];
4537
- const defaultProps = {};
4538
- for (const def of definitions) {
4539
- if (def.defaultValue !== void 0) {
4540
- defaultProps[def.name] = def.defaultValue;
4541
- }
4542
- }
4543
- testCases.push({
4544
- id: "default-props",
4545
- name: "Render with default props",
4546
- props: defaultProps,
4547
- assertions: [{ type: "exists", selector: "*" }]
4548
- });
4549
- for (const def of definitions) {
4550
- const sampleValue = getSampleValue(def);
4551
- if (sampleValue !== void 0) {
4552
- testCases.push({
4553
- id: `prop-${def.name}`,
4554
- name: `Test prop: ${def.name}`,
4555
- props: { ...defaultProps, [def.name]: sampleValue },
4556
- assertions: [{ type: "exists", selector: "*" }]
4557
- });
4558
- }
4559
- }
4560
- return testCases;
4561
- }
4562
- function getSampleValue(def) {
4563
- switch (def.type) {
4564
- case "string":
4565
- return "Sample Text";
4566
- case "number":
4567
- return 42;
4568
- case "boolean":
4569
- return true;
4570
- case "array":
4571
- return [];
4572
- case "object":
4573
- return {};
4574
- default:
4575
- return def.defaultValue;
4576
- }
4577
- }
4578
-
4579
- // packages/runtime-core/src/iconExtractor.ts
4580
- var REACT_ICONS_PACKAGES = {
4581
- Ai: "react-icons/ai",
4582
- Bi: "react-icons/bi",
4583
- Bs: "react-icons/bs",
4584
- Cg: "react-icons/cg",
4585
- Di: "react-icons/di",
4586
- Fa: "react-icons/fa",
4587
- Fa6: "react-icons/fa6",
4588
- Fc: "react-icons/fc",
4589
- Fi: "react-icons/fi",
4590
- Gi: "react-icons/gi",
4591
- Go: "react-icons/go",
4592
- Gr: "react-icons/gr",
4593
- Hi: "react-icons/hi",
4594
- Hi2: "react-icons/hi2",
4595
- Im: "react-icons/im",
4596
- Io: "react-icons/io",
4597
- Io5: "react-icons/io5",
4598
- Lu: "react-icons/lu",
4599
- Md: "react-icons/md",
4600
- Pi: "react-icons/pi",
4601
- Ri: "react-icons/ri",
4602
- Rx: "react-icons/rx",
4603
- Si: "react-icons/si",
4604
- Sl: "react-icons/sl",
4605
- Tb: "react-icons/tb",
4606
- Tfi: "react-icons/tfi",
4607
- Vsc: "react-icons/vsc",
4608
- Wi: "react-icons/wi"
4609
- };
4610
- async function extractIconFromReactIcons(iconName, iconSet) {
4611
- const packagePath = REACT_ICONS_PACKAGES[iconSet];
4612
- if (!packagePath) {
4613
- console.warn(`Unknown icon set: ${iconSet}`);
4614
- return null;
4615
- }
4616
- try {
4617
- const iconModule = await import(packagePath);
4618
- const IconComponent = iconModule[iconName];
4619
- if (!IconComponent) {
4620
- console.warn(`Icon not found: ${iconName} in ${packagePath}`);
4621
- return null;
4622
- }
4623
- const React = await import("react");
4624
- const { renderToStaticMarkup } = await import("react-dom/server");
4625
- const svgString = renderToStaticMarkup(React.createElement(IconComponent, { size: 24 }));
4626
- return parseSvgString(svgString);
4627
- } catch (error) {
4628
- console.error(`Failed to extract icon ${iconSet}:${iconName}:`, error);
4629
- return null;
4630
- }
4631
- }
4632
- function parseSvgString(svgString) {
4633
- const viewBoxMatch = svgString.match(/viewBox="([^"]+)"/);
4634
- const viewBox = viewBoxMatch ? viewBoxMatch[1] : "0 0 24 24";
4635
- const widthMatch = svgString.match(/width="(\d+)"/);
4636
- const heightMatch = svgString.match(/height="(\d+)"/);
4637
- const width = widthMatch ? parseInt(widthMatch[1], 10) : 24;
4638
- const height = heightMatch ? parseInt(heightMatch[1], 10) : 24;
4639
- const bodyMatch = svgString.match(/<svg[^>]*>([\s\S]*)<\/svg>/);
4640
- const body = bodyMatch ? bodyMatch[1].trim() : "";
4641
- if (!body) {
4642
- return null;
4643
- }
4644
- return {
4645
- body,
4646
- viewBox,
4647
- width,
4648
- height
4649
- };
4650
- }
4651
- function findIconsInLayout(elements) {
4652
- const icons = [];
4653
- const seen = /* @__PURE__ */ new Set();
4654
- for (const element of elements) {
4655
- if (element.componentId === "icon" && element.configuration?.icon) {
4656
- const icon = element.configuration.icon;
4657
- const key = `${icon.set}:${icon.name}`;
4658
- if (!seen.has(key)) {
4659
- seen.add(key);
4660
- icons.push({
4661
- name: icon.name,
4662
- set: icon.set,
4663
- setName: icon.setName
4664
- });
4665
- }
4666
- }
4667
- }
4668
- return icons;
4669
- }
4670
- async function extractIconsForLayout(elements) {
4671
- const icons = findIconsInLayout(elements);
4672
- const result = {};
4673
- for (const icon of icons) {
4674
- const data = await extractIconFromReactIcons(icon.name, icon.set);
4675
- if (data) {
4676
- if (!result[icon.set]) {
4677
- result[icon.set] = {};
4678
- }
4679
- result[icon.set][icon.name] = data;
4680
- }
4681
- }
4682
- return result;
4683
- }
4684
- function generateIconBundle(icons) {
4685
- const lines = [
4686
- "// Auto-generated icon bundle",
4687
- "// Do not edit manually",
4688
- "",
4689
- "import { registerIcons, type IconData } from '@servlyadmin/runtime-core';",
4690
- "",
4691
- "const BUNDLED_ICONS: Record<string, Record<string, IconData>> = {"
4692
- ];
4693
- for (const [set, setIcons] of Object.entries(icons)) {
4694
- lines.push(` ${set}: {`);
4695
- for (const [name, data] of Object.entries(setIcons)) {
4696
- const escapedBody = data.body.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
4697
- lines.push(` ${name}: {`);
4698
- lines.push(` body: '${escapedBody}',`);
4699
- lines.push(` viewBox: '${data.viewBox}',`);
4700
- if (data.width) lines.push(` width: ${data.width},`);
4701
- if (data.height) lines.push(` height: ${data.height},`);
4702
- lines.push(` },`);
4703
- }
4704
- lines.push(` },`);
4705
- }
4706
- lines.push("};");
4707
- lines.push("");
4708
- lines.push("// Register all bundled icons");
4709
- lines.push("registerIcons(BUNDLED_ICONS);");
4710
- lines.push("");
4711
- lines.push("export { BUNDLED_ICONS };");
4712
- return lines.join("\n");
4713
- }
4714
- export {
4715
- AnalyticsCollector,
4716
- DEFAULT_CACHE_CONFIG,
4717
- DEFAULT_RETRY_CONFIG,
4718
- DEFAULT_SERVLY_TAILWIND_CONFIG,
4719
- EVENT_HANDLERS,
4720
- EventSystem,
4721
- LongTaskObserver,
4722
- MemorySampler,
4723
- OverrideSystem,
4724
- SessionManager,
4725
- StateManager,
4726
- addClass,
4727
- addCustomStyles,
4728
- analytics,
4729
- applyStyles,
4730
- batchFetchComponents,
4731
- buildClassName,
4732
- buildElementStyles,
4733
- buildRegistryFromBundle,
4734
- bumpVersion,
4735
- camelToKebab,
4736
- clearAllCaches,
4737
- clearIconCache,
4738
- clearLocalStorageCache,
4739
- clearMemoryCache,
4740
- clearStyles,
4741
- collectAllDependencies,
4742
- collectAllViewDependencies,
4743
- compareVersions,
4744
- configureAnalytics,
4745
- createIconSVG,
4746
- createPlaceholderIcon,
4747
- createRegistry,
4748
- createServlyRenderer,
4749
- createViewsMap,
4750
- deepMerge,
4751
- deleteValueByPath,
4752
- detectCircularDependencies,
4753
- extractBindingKeys,
4754
- extractDependencies,
4755
- extractDependenciesFromCode,
4756
- extractIconFromReactIcons,
4757
- extractIconsForLayout,
4758
- extractOverrideDependencies,
4759
- extractReferencedViewIds,
4760
- fetchComponent,
4761
- fetchComponentWithDependencies,
4762
- findIconsInLayout,
4763
- formatStyleValue,
4764
- formatVersion,
4765
- generateIconBundle,
4766
- generateTestCases,
4767
- getAnalytics,
4768
- getCacheKey,
4769
- getCleanupOverrides,
4770
- getDependencyTree,
4771
- getEventSystem,
4772
- getFromCache,
4773
- getIconData,
4774
- getIconDataSync,
4775
- getIconifyCollection,
4776
- getLocalStorage,
4777
- getLongTaskObserver,
4778
- getMemoryCacheSize,
4779
- getMemorySampler,
4780
- getMountOverrides,
4781
- getOverrideSystem,
4782
- getRegisteredIconKeys,
4783
- getRegistryUrl,
4784
- getSessionManager,
4785
- getSessionStorage,
4786
- getSupportedIconSets,
4787
- getTailwind,
4788
- getUrlInfo,
4789
- getValueByPath,
4790
- goBack,
4791
- goForward,
4792
- hasClass,
4793
- hasDependencyOverrides,
4794
- hasOverrides,
4795
- hasTemplateSyntax,
4796
- initServlyTailwind,
4797
- injectTailwind,
4798
- invalidateCache,
4799
- isComponentAvailable,
4800
- isIconCdnEnabled,
4801
- isIconRegistered,
4802
- isIconSetSupported,
4803
- isTailwindLoaded,
4804
- isValidSpecifier,
4805
- navigateTo,
4806
- parseVersion,
4807
- prefetchComponents,
4808
- preloadIcons,
4809
- processStyles,
4810
- registerIcon,
4811
- registerIcons,
4812
- removeClass,
4813
- removeCustomStyles,
4814
- removeLocalStorage,
4815
- removeSessionStorage,
4816
- removeTailwind,
4817
- render,
4818
- renderDynamicList,
4819
- renderIcon,
4820
- renderInShadow,
4821
- renderNode,
4822
- resetAnalytics,
4823
- resetEventSystem,
4824
- resetLongTaskObserver,
4825
- resetMemorySampler,
4826
- resetOverrideSystem,
4827
- resetSessionManager,
4828
- resolveBindingPath,
4829
- resolveTemplate,
4830
- resolveTemplateValue,
4831
- resolveTemplatesDeep,
4832
- resolveVersion,
4833
- runAllTests,
4834
- runTestCase,
4835
- satisfiesVersion,
4836
- setIconCdnEnabled,
4837
- setInCache,
4838
- setLocalStorage,
4839
- setRegistryUrl,
4840
- setSessionStorage,
4841
- setValueByPath,
4842
- toDomEventName,
4843
- toReactEventName,
4844
- toggleClass,
4845
- updateStyles,
4846
- updateTailwindConfig,
4847
- validateAssertion,
4848
- validateProps
4849
- };