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