@servlyadmin/runtime-core 0.1.9 → 0.1.10
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/{chunk-EQFZFPI7.mjs → chunk-CIUQK4GA.js} +1 -1
- package/dist/{chunk-RKUT63EF.mjs → chunk-IWFVKY5N.js} +1 -1
- package/dist/chunk-MCKGQKYU.js +15 -0
- package/dist/chunk-OHWFJHAT.js +2189 -0
- package/dist/index.cjs +21728 -0
- package/dist/index.js +85 -580
- package/dist/react-EKMBDYIU.js +5 -0
- package/dist/{registry-HKUXXQ5V.mjs → registry-7UL42655.js} +2 -1
- package/dist/server.node-CQL3CG75.js +14187 -0
- package/dist/{tailwind-UHWJOUFF.mjs → tailwind-3FTT56ZG.js} +2 -1
- package/package.json +2 -1
- package/dist/index.mjs +0 -4849
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
|
-
};
|