@servlyadmin/runtime-core 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-CIUQK4GA.js +182 -0
- package/dist/index.cjs +1456 -206
- package/dist/index.d.cts +629 -7
- package/dist/index.d.ts +629 -7
- package/dist/index.js +1254 -214
- package/dist/registry-GCCVK65D.js +16 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,30 +20,237 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
23
|
+
// src/registry.ts
|
|
24
|
+
var registry_exports = {};
|
|
25
|
+
__export(registry_exports, {
|
|
26
|
+
buildRegistryFromBundle: () => buildRegistryFromBundle,
|
|
27
|
+
collectAllDependencies: () => collectAllDependencies,
|
|
28
|
+
createRegistry: () => createRegistry,
|
|
29
|
+
detectCircularDependencies: () => detectCircularDependencies,
|
|
30
|
+
extractDependencies: () => extractDependencies,
|
|
31
|
+
extractDependenciesFromCode: () => extractDependenciesFromCode
|
|
32
|
+
});
|
|
33
|
+
function createRegistry() {
|
|
34
|
+
const components = /* @__PURE__ */ new Map();
|
|
35
|
+
return {
|
|
36
|
+
get(id, version) {
|
|
37
|
+
if (version) {
|
|
38
|
+
const key = `${id}@${version}`;
|
|
39
|
+
if (components.has(key)) {
|
|
40
|
+
return components.get(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const [key, component] of components) {
|
|
44
|
+
if (key.startsWith(`${id}@`)) {
|
|
45
|
+
return component;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return components.get(id);
|
|
49
|
+
},
|
|
50
|
+
has(id, version) {
|
|
51
|
+
if (version) {
|
|
52
|
+
return components.has(`${id}@${version}`);
|
|
53
|
+
}
|
|
54
|
+
for (const key of components.keys()) {
|
|
55
|
+
if (key.startsWith(`${id}@`) || key === id) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
},
|
|
61
|
+
set(id, version, component) {
|
|
62
|
+
components.set(`${id}@${version}`, component);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function buildRegistryFromBundle(data) {
|
|
67
|
+
const registry = createRegistry();
|
|
68
|
+
registry.set(data.id, data.version, {
|
|
69
|
+
layout: data.layout,
|
|
70
|
+
propsInterface: data.propsInterface
|
|
71
|
+
});
|
|
72
|
+
if (data.bundle) {
|
|
73
|
+
for (const [key, component] of Object.entries(data.bundle)) {
|
|
74
|
+
const [id, version] = key.split("@");
|
|
75
|
+
if (id && version) {
|
|
76
|
+
registry.set(id, version, component);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return registry;
|
|
81
|
+
}
|
|
82
|
+
function extractDependencies(elements) {
|
|
83
|
+
const dependencies = [];
|
|
84
|
+
for (const element of elements) {
|
|
85
|
+
const config = element.configuration;
|
|
86
|
+
if (!config) continue;
|
|
87
|
+
if (config.componentViewRef) {
|
|
88
|
+
dependencies.push({
|
|
89
|
+
id: config.componentViewRef,
|
|
90
|
+
version: config.componentViewVersion,
|
|
91
|
+
type: "viewRef",
|
|
92
|
+
elementId: element.i
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (config.blueprint) {
|
|
96
|
+
dependencies.push({
|
|
97
|
+
id: config.blueprint,
|
|
98
|
+
version: config.blueprintVersion,
|
|
99
|
+
type: "blueprint",
|
|
100
|
+
elementId: element.i
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return dependencies;
|
|
105
|
+
}
|
|
106
|
+
function extractDependenciesFromCode(code) {
|
|
107
|
+
const dependencies = [];
|
|
108
|
+
const pattern = /renderDynamicList\s*\(\s*\{[^}]*blueprint\s*:\s*["']([^"']+)["']/g;
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
111
|
+
dependencies.push({
|
|
112
|
+
id: match[1],
|
|
113
|
+
type: "blueprint"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return dependencies;
|
|
117
|
+
}
|
|
118
|
+
async function collectAllDependencies(rootId, rootVersion, fetchComponent2, maxDepth = 10) {
|
|
119
|
+
const manifest = {};
|
|
120
|
+
const visited = /* @__PURE__ */ new Set();
|
|
121
|
+
async function collect(id, version, via, depth) {
|
|
122
|
+
if (depth > maxDepth) {
|
|
123
|
+
console.warn(`Max dependency depth (${maxDepth}) reached for ${id}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const key = `${id}@${version || "latest"}`;
|
|
127
|
+
if (visited.has(key)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
visited.add(key);
|
|
131
|
+
try {
|
|
132
|
+
const component = await fetchComponent2(id, version);
|
|
133
|
+
if (!component) {
|
|
134
|
+
console.warn(`Dependency not found: ${id}@${version || "latest"}`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
manifest[id] = {
|
|
138
|
+
version: version || "latest",
|
|
139
|
+
resolved: component.version,
|
|
140
|
+
type: via ? "viewRef" : "viewRef",
|
|
141
|
+
// Will be set by caller
|
|
142
|
+
via
|
|
143
|
+
};
|
|
144
|
+
const nestedDeps = extractDependencies(component.layout);
|
|
145
|
+
for (const dep of nestedDeps) {
|
|
146
|
+
await collect(dep.id, dep.version, id, depth + 1);
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(`Failed to fetch dependency ${id}:`, error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const rootComponent = await fetchComponent2(rootId, rootVersion);
|
|
153
|
+
if (rootComponent) {
|
|
154
|
+
const rootDeps = extractDependencies(rootComponent.layout);
|
|
155
|
+
for (const dep of rootDeps) {
|
|
156
|
+
manifest[dep.id] = {
|
|
157
|
+
version: dep.version || "latest",
|
|
158
|
+
resolved: "",
|
|
159
|
+
// Will be filled when fetched
|
|
160
|
+
type: dep.type
|
|
161
|
+
};
|
|
162
|
+
await collect(dep.id, dep.version, void 0, 1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return manifest;
|
|
166
|
+
}
|
|
167
|
+
function detectCircularDependencies(manifest) {
|
|
168
|
+
const graph = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const [id, entry] of Object.entries(manifest)) {
|
|
170
|
+
if (entry.via) {
|
|
171
|
+
const deps = graph.get(entry.via) || [];
|
|
172
|
+
deps.push(id);
|
|
173
|
+
graph.set(entry.via, deps);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const visited = /* @__PURE__ */ new Set();
|
|
177
|
+
const stack = /* @__PURE__ */ new Set();
|
|
178
|
+
const path = [];
|
|
179
|
+
function dfs(node) {
|
|
180
|
+
if (stack.has(node)) {
|
|
181
|
+
const cycleStart = path.indexOf(node);
|
|
182
|
+
return [...path.slice(cycleStart), node];
|
|
183
|
+
}
|
|
184
|
+
if (visited.has(node)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
visited.add(node);
|
|
188
|
+
stack.add(node);
|
|
189
|
+
path.push(node);
|
|
190
|
+
const neighbors = graph.get(node) || [];
|
|
191
|
+
for (const neighbor of neighbors) {
|
|
192
|
+
const cycle = dfs(neighbor);
|
|
193
|
+
if (cycle) return cycle;
|
|
194
|
+
}
|
|
195
|
+
stack.delete(node);
|
|
196
|
+
path.pop();
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
for (const node of graph.keys()) {
|
|
200
|
+
const cycle = dfs(node);
|
|
201
|
+
if (cycle) return cycle;
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
var init_registry = __esm({
|
|
206
|
+
"src/registry.ts"() {
|
|
207
|
+
"use strict";
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
20
211
|
// src/index.ts
|
|
21
212
|
var index_exports = {};
|
|
22
213
|
__export(index_exports, {
|
|
214
|
+
AnalyticsCollector: () => AnalyticsCollector,
|
|
23
215
|
DEFAULT_CACHE_CONFIG: () => DEFAULT_CACHE_CONFIG,
|
|
24
216
|
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
217
|
+
LongTaskObserver: () => LongTaskObserver,
|
|
218
|
+
MemorySampler: () => MemorySampler,
|
|
219
|
+
SessionManager: () => SessionManager,
|
|
220
|
+
analytics: () => analytics,
|
|
25
221
|
applyStyles: () => applyStyles,
|
|
222
|
+
batchFetchComponents: () => batchFetchComponents,
|
|
26
223
|
buildClassName: () => buildClassName,
|
|
27
224
|
buildElementStyles: () => buildElementStyles,
|
|
225
|
+
buildRegistryFromBundle: () => buildRegistryFromBundle,
|
|
28
226
|
bumpVersion: () => bumpVersion,
|
|
29
227
|
camelToKebab: () => camelToKebab,
|
|
30
228
|
clearAllCaches: () => clearAllCaches,
|
|
31
229
|
clearLocalStorageCache: () => clearLocalStorageCache,
|
|
32
230
|
clearMemoryCache: () => clearMemoryCache,
|
|
33
231
|
clearStyles: () => clearStyles,
|
|
232
|
+
collectAllDependencies: () => collectAllDependencies,
|
|
34
233
|
compareVersions: () => compareVersions,
|
|
234
|
+
configureAnalytics: () => configureAnalytics,
|
|
235
|
+
createRegistry: () => createRegistry,
|
|
236
|
+
detectCircularDependencies: () => detectCircularDependencies,
|
|
35
237
|
extractBindingKeys: () => extractBindingKeys,
|
|
238
|
+
extractDependencies: () => extractDependencies,
|
|
239
|
+
extractDependenciesFromCode: () => extractDependenciesFromCode,
|
|
36
240
|
fetchComponent: () => fetchComponent,
|
|
241
|
+
fetchComponentWithDependencies: () => fetchComponentWithDependencies,
|
|
37
242
|
formatStyleValue: () => formatStyleValue,
|
|
38
243
|
formatVersion: () => formatVersion,
|
|
39
244
|
generateTestCases: () => generateTestCases,
|
|
245
|
+
getAnalytics: () => getAnalytics,
|
|
40
246
|
getCacheKey: () => getCacheKey,
|
|
247
|
+
getDependencyTree: () => getDependencyTree,
|
|
41
248
|
getFromCache: () => getFromCache,
|
|
249
|
+
getLongTaskObserver: () => getLongTaskObserver,
|
|
42
250
|
getMemoryCacheSize: () => getMemoryCacheSize,
|
|
251
|
+
getMemorySampler: () => getMemorySampler,
|
|
43
252
|
getRegistryUrl: () => getRegistryUrl,
|
|
253
|
+
getSessionManager: () => getSessionManager,
|
|
44
254
|
hasTemplateSyntax: () => hasTemplateSyntax,
|
|
45
255
|
invalidateCache: () => invalidateCache,
|
|
46
256
|
isComponentAvailable: () => isComponentAvailable,
|
|
@@ -49,6 +259,11 @@ __export(index_exports, {
|
|
|
49
259
|
prefetchComponents: () => prefetchComponents,
|
|
50
260
|
processStyles: () => processStyles,
|
|
51
261
|
render: () => render,
|
|
262
|
+
renderDynamicList: () => renderDynamicList,
|
|
263
|
+
resetAnalytics: () => resetAnalytics,
|
|
264
|
+
resetLongTaskObserver: () => resetLongTaskObserver,
|
|
265
|
+
resetMemorySampler: () => resetMemorySampler,
|
|
266
|
+
resetSessionManager: () => resetSessionManager,
|
|
52
267
|
resolveBindingPath: () => resolveBindingPath,
|
|
53
268
|
resolveTemplate: () => resolveTemplate,
|
|
54
269
|
resolveTemplateValue: () => resolveTemplateValue,
|
|
@@ -65,6 +280,516 @@ __export(index_exports, {
|
|
|
65
280
|
});
|
|
66
281
|
module.exports = __toCommonJS(index_exports);
|
|
67
282
|
|
|
283
|
+
// src/analyticsTypes.ts
|
|
284
|
+
var DEFAULT_ANALYTICS_CONFIG = {
|
|
285
|
+
enabled: true,
|
|
286
|
+
endpoint: "/api/v1/analytics/events",
|
|
287
|
+
batchSize: 50,
|
|
288
|
+
flushInterval: 3e4,
|
|
289
|
+
// 30 seconds
|
|
290
|
+
sampleRate: 1,
|
|
291
|
+
environment: "production",
|
|
292
|
+
debug: false
|
|
293
|
+
};
|
|
294
|
+
var MAX_ERROR_MESSAGE_LENGTH = 1e3;
|
|
295
|
+
var MAX_STACK_TRACE_LENGTH = 500;
|
|
296
|
+
var MAX_QUEUE_SIZE = 500;
|
|
297
|
+
var MAX_RETRY_ATTEMPTS = 3;
|
|
298
|
+
var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
299
|
+
var SDK_VERSION = "1.0.0";
|
|
300
|
+
|
|
301
|
+
// src/sessionManager.ts
|
|
302
|
+
var SESSION_STORAGE_KEY = "servly_analytics_session";
|
|
303
|
+
function generateUUID() {
|
|
304
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
305
|
+
return crypto.randomUUID();
|
|
306
|
+
}
|
|
307
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
308
|
+
const r = Math.random() * 16 | 0;
|
|
309
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
310
|
+
return v.toString(16);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function isSessionStorageAvailable() {
|
|
314
|
+
try {
|
|
315
|
+
if (typeof sessionStorage === "undefined") {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const testKey = "__servly_test__";
|
|
319
|
+
sessionStorage.setItem(testKey, "test");
|
|
320
|
+
sessionStorage.removeItem(testKey);
|
|
321
|
+
return true;
|
|
322
|
+
} catch {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function loadSession() {
|
|
327
|
+
if (!isSessionStorageAvailable()) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
const stored = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
332
|
+
if (!stored) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
return JSON.parse(stored);
|
|
336
|
+
} catch {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function saveSession(session) {
|
|
341
|
+
if (!isSessionStorageAvailable()) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(session));
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function clearSession() {
|
|
350
|
+
if (!isSessionStorageAvailable()) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
sessionStorage.removeItem(SESSION_STORAGE_KEY);
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function isSessionExpired(session) {
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
return now - session.lastActivityAt > SESSION_TIMEOUT_MS;
|
|
361
|
+
}
|
|
362
|
+
var SessionManager = class {
|
|
363
|
+
constructor() {
|
|
364
|
+
this.session = null;
|
|
365
|
+
this.initialize();
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Initialize session manager
|
|
369
|
+
*/
|
|
370
|
+
initialize() {
|
|
371
|
+
const stored = loadSession();
|
|
372
|
+
if (stored && !isSessionExpired(stored)) {
|
|
373
|
+
this.session = stored;
|
|
374
|
+
this.touch();
|
|
375
|
+
} else {
|
|
376
|
+
this.createNewSession();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Create a new session
|
|
381
|
+
*/
|
|
382
|
+
createNewSession() {
|
|
383
|
+
const now = Date.now();
|
|
384
|
+
this.session = {
|
|
385
|
+
id: generateUUID(),
|
|
386
|
+
createdAt: now,
|
|
387
|
+
lastActivityAt: now
|
|
388
|
+
};
|
|
389
|
+
saveSession(this.session);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get current session ID
|
|
393
|
+
* Creates new session if expired
|
|
394
|
+
*/
|
|
395
|
+
getSessionId() {
|
|
396
|
+
if (!this.session || isSessionExpired(this.session)) {
|
|
397
|
+
this.createNewSession();
|
|
398
|
+
}
|
|
399
|
+
return this.session.id;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Update last activity timestamp
|
|
403
|
+
*/
|
|
404
|
+
touch() {
|
|
405
|
+
if (this.session) {
|
|
406
|
+
this.session.lastActivityAt = Date.now();
|
|
407
|
+
saveSession(this.session);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Force create a new session
|
|
412
|
+
*/
|
|
413
|
+
rotate() {
|
|
414
|
+
this.createNewSession();
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Clear current session
|
|
418
|
+
*/
|
|
419
|
+
clear() {
|
|
420
|
+
this.session = null;
|
|
421
|
+
clearSession();
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get session info (for debugging)
|
|
425
|
+
*/
|
|
426
|
+
getSessionInfo() {
|
|
427
|
+
return this.session ? { ...this.session } : null;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var sessionManagerInstance = null;
|
|
431
|
+
function getSessionManager() {
|
|
432
|
+
if (!sessionManagerInstance) {
|
|
433
|
+
sessionManagerInstance = new SessionManager();
|
|
434
|
+
}
|
|
435
|
+
return sessionManagerInstance;
|
|
436
|
+
}
|
|
437
|
+
function resetSessionManager() {
|
|
438
|
+
if (sessionManagerInstance) {
|
|
439
|
+
sessionManagerInstance.clear();
|
|
440
|
+
}
|
|
441
|
+
sessionManagerInstance = null;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/analytics.ts
|
|
445
|
+
var AnalyticsCollector = class {
|
|
446
|
+
constructor(config) {
|
|
447
|
+
this.eventQueue = [];
|
|
448
|
+
this.flushTimer = null;
|
|
449
|
+
this.isFlushing = false;
|
|
450
|
+
this.retryDelay = 1e3;
|
|
451
|
+
this.config = { ...DEFAULT_ANALYTICS_CONFIG, ...config };
|
|
452
|
+
this.isEnabled = this.config.enabled;
|
|
453
|
+
this.startFlushTimer();
|
|
454
|
+
}
|
|
455
|
+
// ============================================
|
|
456
|
+
// Configuration
|
|
457
|
+
// ============================================
|
|
458
|
+
/**
|
|
459
|
+
* Configure analytics
|
|
460
|
+
*/
|
|
461
|
+
configure(config) {
|
|
462
|
+
this.config = { ...this.config, ...config };
|
|
463
|
+
this.isEnabled = this.config.enabled;
|
|
464
|
+
this.stopFlushTimer();
|
|
465
|
+
if (this.isEnabled) {
|
|
466
|
+
this.startFlushTimer();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Disable analytics collection
|
|
471
|
+
*/
|
|
472
|
+
disable() {
|
|
473
|
+
this.isEnabled = false;
|
|
474
|
+
this.stopFlushTimer();
|
|
475
|
+
this.eventQueue = [];
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Enable analytics collection
|
|
479
|
+
*/
|
|
480
|
+
enable() {
|
|
481
|
+
this.isEnabled = true;
|
|
482
|
+
this.startFlushTimer();
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Destroy and cleanup
|
|
486
|
+
*/
|
|
487
|
+
destroy() {
|
|
488
|
+
this.disable();
|
|
489
|
+
}
|
|
490
|
+
// ============================================
|
|
491
|
+
// Event Tracking
|
|
492
|
+
// ============================================
|
|
493
|
+
/**
|
|
494
|
+
* Track component render
|
|
495
|
+
*/
|
|
496
|
+
trackRender(componentId, version, duration, metadata) {
|
|
497
|
+
if (!this.shouldTrack()) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const event = {
|
|
501
|
+
type: "render",
|
|
502
|
+
componentId,
|
|
503
|
+
version,
|
|
504
|
+
timestamp: Date.now(),
|
|
505
|
+
sessionId: getSessionManager().getSessionId(),
|
|
506
|
+
appId: this.config.appId,
|
|
507
|
+
duration: Math.round(duration),
|
|
508
|
+
metadata
|
|
509
|
+
};
|
|
510
|
+
this.queueEvent(event);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Track component fetch
|
|
514
|
+
*/
|
|
515
|
+
trackFetch(componentId, version, duration, fromCache, metadata) {
|
|
516
|
+
if (!this.shouldTrack()) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const event = {
|
|
520
|
+
type: "fetch",
|
|
521
|
+
componentId,
|
|
522
|
+
version,
|
|
523
|
+
timestamp: Date.now(),
|
|
524
|
+
sessionId: getSessionManager().getSessionId(),
|
|
525
|
+
appId: this.config.appId,
|
|
526
|
+
duration: Math.round(duration),
|
|
527
|
+
metadata: {
|
|
528
|
+
cacheHit: fromCache,
|
|
529
|
+
fetchDuration: Math.round(duration),
|
|
530
|
+
...metadata
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
this.queueEvent(event);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Track error
|
|
537
|
+
*/
|
|
538
|
+
trackError(componentId, version, error, context) {
|
|
539
|
+
if (!this.shouldTrack()) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const errorMessage = this.truncateString(
|
|
543
|
+
error.message || "Unknown error",
|
|
544
|
+
MAX_ERROR_MESSAGE_LENGTH
|
|
545
|
+
);
|
|
546
|
+
const stackTrace = error.stack ? this.truncateString(error.stack, MAX_STACK_TRACE_LENGTH) : void 0;
|
|
547
|
+
const event = {
|
|
548
|
+
type: "error",
|
|
549
|
+
componentId,
|
|
550
|
+
version,
|
|
551
|
+
timestamp: Date.now(),
|
|
552
|
+
sessionId: getSessionManager().getSessionId(),
|
|
553
|
+
appId: this.config.appId,
|
|
554
|
+
metadata: {
|
|
555
|
+
errorType: context?.errorType || "render",
|
|
556
|
+
errorMessage,
|
|
557
|
+
stackTrace,
|
|
558
|
+
bindingPath: context?.bindingPath,
|
|
559
|
+
elementId: context?.elementId
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
this.queueEvent(event);
|
|
563
|
+
}
|
|
564
|
+
// ============================================
|
|
565
|
+
// Queue Management
|
|
566
|
+
// ============================================
|
|
567
|
+
/**
|
|
568
|
+
* Check if event should be tracked (sampling + enabled)
|
|
569
|
+
*/
|
|
570
|
+
shouldTrack() {
|
|
571
|
+
if (!this.isEnabled) {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
if (this.config.sampleRate < 1) {
|
|
575
|
+
return Math.random() < this.config.sampleRate;
|
|
576
|
+
}
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Add event to queue
|
|
581
|
+
*/
|
|
582
|
+
queueEvent(event) {
|
|
583
|
+
if (this.eventQueue.length >= MAX_QUEUE_SIZE) {
|
|
584
|
+
this.eventQueue.shift();
|
|
585
|
+
this.log("Queue full, dropping oldest event");
|
|
586
|
+
}
|
|
587
|
+
this.eventQueue.push({
|
|
588
|
+
event,
|
|
589
|
+
retryCount: 0,
|
|
590
|
+
addedAt: Date.now()
|
|
591
|
+
});
|
|
592
|
+
if (this.eventQueue.length >= this.config.batchSize) {
|
|
593
|
+
this.flush();
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Get current queue size
|
|
598
|
+
*/
|
|
599
|
+
getQueueSize() {
|
|
600
|
+
return this.eventQueue.length;
|
|
601
|
+
}
|
|
602
|
+
// ============================================
|
|
603
|
+
// Flush Logic
|
|
604
|
+
// ============================================
|
|
605
|
+
/**
|
|
606
|
+
* Start the flush timer
|
|
607
|
+
*/
|
|
608
|
+
startFlushTimer() {
|
|
609
|
+
if (this.flushTimer) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
this.flushTimer = setInterval(() => {
|
|
613
|
+
this.flush();
|
|
614
|
+
}, this.config.flushInterval);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Stop the flush timer
|
|
618
|
+
*/
|
|
619
|
+
stopFlushTimer() {
|
|
620
|
+
if (this.flushTimer) {
|
|
621
|
+
clearInterval(this.flushTimer);
|
|
622
|
+
this.flushTimer = null;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Flush events to server
|
|
627
|
+
*/
|
|
628
|
+
async flush() {
|
|
629
|
+
if (this.isFlushing || this.eventQueue.length === 0) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
this.isFlushing = true;
|
|
633
|
+
if (typeof requestIdleCallback !== "undefined") {
|
|
634
|
+
requestIdleCallback(
|
|
635
|
+
() => {
|
|
636
|
+
this.doFlush();
|
|
637
|
+
},
|
|
638
|
+
{ timeout: 5e3 }
|
|
639
|
+
);
|
|
640
|
+
} else {
|
|
641
|
+
setTimeout(() => {
|
|
642
|
+
this.doFlush();
|
|
643
|
+
}, 0);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Perform the actual flush
|
|
648
|
+
*/
|
|
649
|
+
async doFlush() {
|
|
650
|
+
const eventsToSend = this.eventQueue.splice(0, this.config.batchSize);
|
|
651
|
+
if (eventsToSend.length === 0) {
|
|
652
|
+
this.isFlushing = false;
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const request = {
|
|
656
|
+
events: eventsToSend.map((q) => q.event),
|
|
657
|
+
clientInfo: {
|
|
658
|
+
sdkVersion: SDK_VERSION,
|
|
659
|
+
environment: this.config.environment
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
try {
|
|
663
|
+
const response = await this.sendEvents(request);
|
|
664
|
+
if (response.success) {
|
|
665
|
+
this.log(`Flushed ${response.accepted} events`);
|
|
666
|
+
this.retryDelay = 1e3;
|
|
667
|
+
} else {
|
|
668
|
+
this.handleFailedEvents(eventsToSend, response);
|
|
669
|
+
}
|
|
670
|
+
} catch (error) {
|
|
671
|
+
this.handleNetworkError(eventsToSend, error);
|
|
672
|
+
} finally {
|
|
673
|
+
this.isFlushing = false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Send events to the API
|
|
678
|
+
*/
|
|
679
|
+
async sendEvents(request) {
|
|
680
|
+
const headers = {
|
|
681
|
+
"Content-Type": "application/json"
|
|
682
|
+
};
|
|
683
|
+
if (this.config.apiKey) {
|
|
684
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
685
|
+
}
|
|
686
|
+
const response = await fetch(this.config.endpoint, {
|
|
687
|
+
method: "POST",
|
|
688
|
+
headers,
|
|
689
|
+
body: JSON.stringify(request)
|
|
690
|
+
});
|
|
691
|
+
if (response.status === 429) {
|
|
692
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") || "60", 10);
|
|
693
|
+
return {
|
|
694
|
+
success: false,
|
|
695
|
+
accepted: 0,
|
|
696
|
+
rejected: request.events.length,
|
|
697
|
+
retryAfter
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
if (!response.ok) {
|
|
701
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
702
|
+
}
|
|
703
|
+
return await response.json();
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Handle failed events (partial success)
|
|
707
|
+
*/
|
|
708
|
+
handleFailedEvents(events, response) {
|
|
709
|
+
const eventsToRetry = events.filter((e) => e.retryCount < MAX_RETRY_ATTEMPTS);
|
|
710
|
+
eventsToRetry.forEach((e) => {
|
|
711
|
+
e.retryCount++;
|
|
712
|
+
this.eventQueue.unshift(e);
|
|
713
|
+
});
|
|
714
|
+
const dropped = events.length - eventsToRetry.length;
|
|
715
|
+
if (dropped > 0) {
|
|
716
|
+
this.log(`Dropped ${dropped} events after max retries`);
|
|
717
|
+
}
|
|
718
|
+
if (response.retryAfter) {
|
|
719
|
+
this.scheduleRetry(response.retryAfter * 1e3);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Handle network error
|
|
724
|
+
*/
|
|
725
|
+
handleNetworkError(events, error) {
|
|
726
|
+
this.log(`Network error: ${error}`);
|
|
727
|
+
const eventsToRetry = events.filter((e) => e.retryCount < MAX_RETRY_ATTEMPTS);
|
|
728
|
+
eventsToRetry.forEach((e) => {
|
|
729
|
+
e.retryCount++;
|
|
730
|
+
this.eventQueue.unshift(e);
|
|
731
|
+
});
|
|
732
|
+
this.scheduleRetry(this.retryDelay);
|
|
733
|
+
this.retryDelay = Math.min(this.retryDelay * 2, 3e4);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Schedule a retry flush
|
|
737
|
+
*/
|
|
738
|
+
scheduleRetry(delayMs) {
|
|
739
|
+
setTimeout(() => {
|
|
740
|
+
this.flush();
|
|
741
|
+
}, delayMs);
|
|
742
|
+
}
|
|
743
|
+
// ============================================
|
|
744
|
+
// Utilities
|
|
745
|
+
// ============================================
|
|
746
|
+
/**
|
|
747
|
+
* Truncate string to max length
|
|
748
|
+
*/
|
|
749
|
+
truncateString(str, maxLength) {
|
|
750
|
+
if (str.length <= maxLength) {
|
|
751
|
+
return str;
|
|
752
|
+
}
|
|
753
|
+
return str.substring(0, maxLength - 3) + "...";
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Log debug message
|
|
757
|
+
*/
|
|
758
|
+
log(message) {
|
|
759
|
+
if (this.config.debug) {
|
|
760
|
+
console.log(`[Analytics] ${message}`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
var analyticsInstance = null;
|
|
765
|
+
function getAnalytics() {
|
|
766
|
+
if (!analyticsInstance) {
|
|
767
|
+
analyticsInstance = new AnalyticsCollector();
|
|
768
|
+
}
|
|
769
|
+
return analyticsInstance;
|
|
770
|
+
}
|
|
771
|
+
function configureAnalytics(config) {
|
|
772
|
+
getAnalytics().configure(config);
|
|
773
|
+
}
|
|
774
|
+
function resetAnalytics() {
|
|
775
|
+
if (analyticsInstance) {
|
|
776
|
+
analyticsInstance.destroy();
|
|
777
|
+
}
|
|
778
|
+
analyticsInstance = null;
|
|
779
|
+
}
|
|
780
|
+
var analytics = {
|
|
781
|
+
get instance() {
|
|
782
|
+
return getAnalytics();
|
|
783
|
+
},
|
|
784
|
+
configure: configureAnalytics,
|
|
785
|
+
trackRender: (componentId, version, duration, metadata) => getAnalytics().trackRender(componentId, version, duration, metadata),
|
|
786
|
+
trackFetch: (componentId, version, duration, fromCache, metadata) => getAnalytics().trackFetch(componentId, version, duration, fromCache, metadata),
|
|
787
|
+
trackError: (componentId, version, error, context) => getAnalytics().trackError(componentId, version, error, context),
|
|
788
|
+
flush: () => getAnalytics().flush(),
|
|
789
|
+
disable: () => getAnalytics().disable(),
|
|
790
|
+
enable: () => getAnalytics().enable()
|
|
791
|
+
};
|
|
792
|
+
|
|
68
793
|
// src/bindings.ts
|
|
69
794
|
var BINDING_SOURCES = [
|
|
70
795
|
"props",
|
|
@@ -171,44 +896,64 @@ function resolveTernaryValue(value, context) {
|
|
|
171
896
|
if (value === "undefined") return void 0;
|
|
172
897
|
return resolveExpression(value, context);
|
|
173
898
|
}
|
|
174
|
-
function resolveTemplate(template, context) {
|
|
899
|
+
function resolveTemplate(template, context, componentId) {
|
|
175
900
|
if (!template || typeof template !== "string") {
|
|
176
901
|
return template;
|
|
177
902
|
}
|
|
178
903
|
if (!hasTemplateSyntax(template)) {
|
|
179
904
|
return template;
|
|
180
905
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return template.replace(TEMPLATE_REGEX, (match, expression) => {
|
|
190
|
-
const value = resolveExpression(expression, context);
|
|
191
|
-
if (value === void 0 || value === null) {
|
|
192
|
-
return "";
|
|
906
|
+
try {
|
|
907
|
+
const singleMatch = template.match(/^\{\{([^}]+)\}\}$/);
|
|
908
|
+
if (singleMatch) {
|
|
909
|
+
const value = resolveExpression(singleMatch[1], context);
|
|
910
|
+
if (value === void 0 || value === null) {
|
|
911
|
+
return "";
|
|
912
|
+
}
|
|
913
|
+
return String(value);
|
|
193
914
|
}
|
|
194
|
-
|
|
195
|
-
|
|
915
|
+
return template.replace(TEMPLATE_REGEX, (match, expression) => {
|
|
916
|
+
const value = resolveExpression(expression, context);
|
|
917
|
+
if (value === void 0 || value === null) {
|
|
918
|
+
return "";
|
|
919
|
+
}
|
|
920
|
+
if (typeof value === "object") {
|
|
921
|
+
return JSON.stringify(value);
|
|
922
|
+
}
|
|
923
|
+
return String(value);
|
|
924
|
+
});
|
|
925
|
+
} catch (error) {
|
|
926
|
+
if (componentId) {
|
|
927
|
+
analytics.trackError(componentId, "latest", error, {
|
|
928
|
+
errorType: "binding",
|
|
929
|
+
bindingPath: template
|
|
930
|
+
});
|
|
196
931
|
}
|
|
197
|
-
return
|
|
198
|
-
}
|
|
932
|
+
return "";
|
|
933
|
+
}
|
|
199
934
|
}
|
|
200
|
-
function resolveTemplateValue(template, context) {
|
|
935
|
+
function resolveTemplateValue(template, context, componentId) {
|
|
201
936
|
if (!template || typeof template !== "string") {
|
|
202
937
|
return template;
|
|
203
938
|
}
|
|
204
939
|
if (!hasTemplateSyntax(template)) {
|
|
205
940
|
return template;
|
|
206
941
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
942
|
+
try {
|
|
943
|
+
const singleMatch = template.match(/^\{\{([^}]+)\}\}$/);
|
|
944
|
+
if (singleMatch) {
|
|
945
|
+
return resolveExpression(singleMatch[1], context);
|
|
946
|
+
}
|
|
947
|
+
return resolveTemplate(template, context, componentId);
|
|
948
|
+
} catch (error) {
|
|
949
|
+
if (componentId) {
|
|
950
|
+
analytics.trackError(componentId, "latest", error, {
|
|
951
|
+
errorType: "binding",
|
|
952
|
+
bindingPath: template
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
return void 0;
|
|
210
956
|
}
|
|
211
|
-
return resolveTemplate(template, context);
|
|
212
957
|
}
|
|
213
958
|
function isPlainObject(value) {
|
|
214
959
|
return Object.prototype.toString.call(value) === "[object Object]";
|
|
@@ -361,17 +1106,39 @@ function applyStyles(element, styles) {
|
|
|
361
1106
|
}
|
|
362
1107
|
}
|
|
363
1108
|
}
|
|
1109
|
+
var CANVAS_ONLY_STYLES = /* @__PURE__ */ new Set([
|
|
1110
|
+
"transform",
|
|
1111
|
+
"--translate-x",
|
|
1112
|
+
"--translate-y",
|
|
1113
|
+
"--width",
|
|
1114
|
+
"--height",
|
|
1115
|
+
"z-index",
|
|
1116
|
+
"zIndex"
|
|
1117
|
+
]);
|
|
1118
|
+
function filterCanvasStyles(style) {
|
|
1119
|
+
const filtered = {};
|
|
1120
|
+
for (const [key, value] of Object.entries(style)) {
|
|
1121
|
+
if (CANVAS_ONLY_STYLES.has(key)) {
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (key === "transform" && typeof value === "string" && value.includes("translate(")) {
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
filtered[key] = value;
|
|
1128
|
+
}
|
|
1129
|
+
return filtered;
|
|
1130
|
+
}
|
|
364
1131
|
function buildElementStyles(element, context) {
|
|
365
1132
|
const config = element.configuration || {};
|
|
366
1133
|
const combined = {};
|
|
367
1134
|
if (element.style) {
|
|
368
|
-
Object.assign(combined, element.style);
|
|
1135
|
+
Object.assign(combined, filterCanvasStyles(element.style));
|
|
369
1136
|
}
|
|
370
1137
|
if (config.style) {
|
|
371
|
-
Object.assign(combined, config.style);
|
|
1138
|
+
Object.assign(combined, filterCanvasStyles(config.style));
|
|
372
1139
|
}
|
|
373
1140
|
if (config.cssVariables) {
|
|
374
|
-
Object.assign(combined, config.cssVariables);
|
|
1141
|
+
Object.assign(combined, filterCanvasStyles(config.cssVariables));
|
|
375
1142
|
}
|
|
376
1143
|
return processStyles(combined, context);
|
|
377
1144
|
}
|
|
@@ -419,6 +1186,162 @@ function updateStyles(element, oldStyles, newStyles) {
|
|
|
419
1186
|
}
|
|
420
1187
|
}
|
|
421
1188
|
|
|
1189
|
+
// src/memorySampler.ts
|
|
1190
|
+
var MemorySampler = class {
|
|
1191
|
+
constructor() {
|
|
1192
|
+
this.isSupported = this.checkSupport();
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Check if memory API is available
|
|
1196
|
+
*/
|
|
1197
|
+
checkSupport() {
|
|
1198
|
+
if (typeof performance === "undefined") {
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1201
|
+
const perf = performance;
|
|
1202
|
+
return typeof perf.memory !== "undefined";
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Check if memory sampling is available
|
|
1206
|
+
*/
|
|
1207
|
+
isAvailable() {
|
|
1208
|
+
return this.isSupported;
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Take a memory sample
|
|
1212
|
+
* Returns null if memory API is not available
|
|
1213
|
+
*/
|
|
1214
|
+
sample() {
|
|
1215
|
+
if (!this.isSupported) {
|
|
1216
|
+
return null;
|
|
1217
|
+
}
|
|
1218
|
+
const perf = performance;
|
|
1219
|
+
if (!perf.memory) {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
return {
|
|
1223
|
+
heapUsedKB: Math.round(perf.memory.usedJSHeapSize / 1024),
|
|
1224
|
+
heapTotalKB: Math.round(perf.memory.totalJSHeapSize / 1024),
|
|
1225
|
+
timestamp: Date.now()
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Calculate heap delta between two samples
|
|
1230
|
+
* Returns the difference in KB (positive = memory increased)
|
|
1231
|
+
*/
|
|
1232
|
+
calculateDelta(before, after) {
|
|
1233
|
+
if (!before || !after) {
|
|
1234
|
+
return null;
|
|
1235
|
+
}
|
|
1236
|
+
return after.heapUsedKB - before.heapUsedKB;
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
var memorySamplerInstance = null;
|
|
1240
|
+
function getMemorySampler() {
|
|
1241
|
+
if (!memorySamplerInstance) {
|
|
1242
|
+
memorySamplerInstance = new MemorySampler();
|
|
1243
|
+
}
|
|
1244
|
+
return memorySamplerInstance;
|
|
1245
|
+
}
|
|
1246
|
+
function resetMemorySampler() {
|
|
1247
|
+
memorySamplerInstance = null;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// src/longTaskObserver.ts
|
|
1251
|
+
var LongTaskObserver = class {
|
|
1252
|
+
constructor() {
|
|
1253
|
+
this.observer = null;
|
|
1254
|
+
this.longTaskCount = 0;
|
|
1255
|
+
this.isObserving = false;
|
|
1256
|
+
this.isSupported = this.checkSupport();
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Check if PerformanceObserver with longtask support is available
|
|
1260
|
+
*/
|
|
1261
|
+
checkSupport() {
|
|
1262
|
+
if (typeof PerformanceObserver === "undefined") {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
try {
|
|
1266
|
+
const supportedTypes = PerformanceObserver.supportedEntryTypes;
|
|
1267
|
+
return Array.isArray(supportedTypes) && supportedTypes.includes("longtask");
|
|
1268
|
+
} catch {
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Check if long task observation is available
|
|
1274
|
+
*/
|
|
1275
|
+
isAvailable() {
|
|
1276
|
+
return this.isSupported;
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Start observing long tasks
|
|
1280
|
+
*/
|
|
1281
|
+
start() {
|
|
1282
|
+
if (!this.isSupported || this.isObserving) {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
this.longTaskCount = 0;
|
|
1286
|
+
try {
|
|
1287
|
+
this.observer = new PerformanceObserver((list) => {
|
|
1288
|
+
const entries = list.getEntries();
|
|
1289
|
+
this.longTaskCount += entries.length;
|
|
1290
|
+
});
|
|
1291
|
+
this.observer.observe({ entryTypes: ["longtask"] });
|
|
1292
|
+
this.isObserving = true;
|
|
1293
|
+
} catch {
|
|
1294
|
+
this.isSupported = false;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Stop observing and return the count of long tasks
|
|
1299
|
+
*/
|
|
1300
|
+
stop() {
|
|
1301
|
+
if (!this.isObserving || !this.observer) {
|
|
1302
|
+
return this.longTaskCount;
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
this.observer.disconnect();
|
|
1306
|
+
} catch {
|
|
1307
|
+
}
|
|
1308
|
+
this.observer = null;
|
|
1309
|
+
this.isObserving = false;
|
|
1310
|
+
return this.longTaskCount;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Reset the long task counter
|
|
1314
|
+
*/
|
|
1315
|
+
reset() {
|
|
1316
|
+
this.longTaskCount = 0;
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Get current count without stopping observation
|
|
1320
|
+
*/
|
|
1321
|
+
getCount() {
|
|
1322
|
+
return this.longTaskCount;
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Check if currently observing
|
|
1326
|
+
*/
|
|
1327
|
+
isActive() {
|
|
1328
|
+
return this.isObserving;
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
var longTaskObserverInstance = null;
|
|
1332
|
+
function getLongTaskObserver() {
|
|
1333
|
+
if (!longTaskObserverInstance) {
|
|
1334
|
+
longTaskObserverInstance = new LongTaskObserver();
|
|
1335
|
+
}
|
|
1336
|
+
return longTaskObserverInstance;
|
|
1337
|
+
}
|
|
1338
|
+
function resetLongTaskObserver() {
|
|
1339
|
+
if (longTaskObserverInstance) {
|
|
1340
|
+
longTaskObserverInstance.stop();
|
|
1341
|
+
}
|
|
1342
|
+
longTaskObserverInstance = null;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
422
1345
|
// src/renderer.ts
|
|
423
1346
|
var COMPONENT_TO_TAG = {
|
|
424
1347
|
container: "div",
|
|
@@ -594,60 +1517,162 @@ function createElement(element, context, eventHandlers) {
|
|
|
594
1517
|
attachEventHandlers(domElement, element.i, eventHandlers, elementState);
|
|
595
1518
|
return elementState;
|
|
596
1519
|
}
|
|
597
|
-
|
|
1520
|
+
var globalRenderingStack = /* @__PURE__ */ new Set();
|
|
1521
|
+
function renderComponentRef(element, container, context, state) {
|
|
1522
|
+
const config = element.configuration;
|
|
1523
|
+
if (!config?.componentViewRef) return void 0;
|
|
1524
|
+
const refId = config.componentViewRef;
|
|
1525
|
+
const refVersion = config.componentViewVersion;
|
|
1526
|
+
if (globalRenderingStack.has(refId)) {
|
|
1527
|
+
console.warn(`Circular dependency detected: ${refId} is already being rendered`);
|
|
1528
|
+
const placeholder = document.createElement("div");
|
|
1529
|
+
placeholder.setAttribute("data-servly-circular", refId);
|
|
1530
|
+
placeholder.textContent = `[Circular: ${refId}]`;
|
|
1531
|
+
container.appendChild(placeholder);
|
|
1532
|
+
return void 0;
|
|
1533
|
+
}
|
|
1534
|
+
let component;
|
|
1535
|
+
if (state.componentRegistry) {
|
|
1536
|
+
component = state.componentRegistry.get(refId, refVersion);
|
|
1537
|
+
}
|
|
1538
|
+
if (!component) {
|
|
1539
|
+
const placeholder = document.createElement("div");
|
|
1540
|
+
placeholder.setAttribute("data-servly-loading", refId);
|
|
1541
|
+
placeholder.className = "servly-loading";
|
|
1542
|
+
container.appendChild(placeholder);
|
|
1543
|
+
if (state.onDependencyNeeded) {
|
|
1544
|
+
state.onDependencyNeeded(refId, refVersion).then((loaded) => {
|
|
1545
|
+
if (loaded && state.componentRegistry) {
|
|
1546
|
+
state.componentRegistry.set(refId, refVersion || "latest", loaded);
|
|
1547
|
+
container.innerHTML = "";
|
|
1548
|
+
renderComponentRef(element, container, context, state);
|
|
1549
|
+
}
|
|
1550
|
+
}).catch((err) => {
|
|
1551
|
+
console.error(`Failed to load dependency ${refId}:`, err);
|
|
1552
|
+
placeholder.textContent = `[Failed to load: ${refId}]`;
|
|
1553
|
+
placeholder.className = "servly-error";
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
return void 0;
|
|
1557
|
+
}
|
|
1558
|
+
const refProps = config.componentViewProps ? resolveTemplatesDeep(config.componentViewProps, context) : {};
|
|
1559
|
+
globalRenderingStack.add(refId);
|
|
1560
|
+
const refContext = {
|
|
1561
|
+
props: refProps,
|
|
1562
|
+
state: context.state,
|
|
1563
|
+
context: context.context
|
|
1564
|
+
};
|
|
1565
|
+
try {
|
|
1566
|
+
const result = render({
|
|
1567
|
+
container,
|
|
1568
|
+
elements: component.layout,
|
|
1569
|
+
context: refContext,
|
|
1570
|
+
componentRegistry: state.componentRegistry,
|
|
1571
|
+
onDependencyNeeded: state.onDependencyNeeded
|
|
1572
|
+
});
|
|
1573
|
+
return result;
|
|
1574
|
+
} finally {
|
|
1575
|
+
globalRenderingStack.delete(refId);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
function renderElement(element, tree, context, eventHandlers, elementStates, state) {
|
|
598
1579
|
const elementState = createElement(element, context, eventHandlers);
|
|
599
1580
|
elementStates.set(element.i, elementState);
|
|
1581
|
+
const config = element.configuration;
|
|
1582
|
+
if (config?.componentViewRef) {
|
|
1583
|
+
const nestedResult = renderComponentRef(element, elementState.domElement, context, state);
|
|
1584
|
+
if (nestedResult) {
|
|
1585
|
+
elementState.nestedRender = nestedResult;
|
|
1586
|
+
}
|
|
1587
|
+
return elementState.domElement;
|
|
1588
|
+
}
|
|
600
1589
|
const children = tree.get(element.i) || [];
|
|
601
1590
|
for (const child of children) {
|
|
602
|
-
const childElement = renderElement(child, tree, context, eventHandlers, elementStates);
|
|
1591
|
+
const childElement = renderElement(child, tree, context, eventHandlers, elementStates, state);
|
|
603
1592
|
elementState.domElement.appendChild(childElement);
|
|
604
1593
|
}
|
|
605
1594
|
return elementState.domElement;
|
|
606
1595
|
}
|
|
607
1596
|
function render(options) {
|
|
608
|
-
const { container, elements, context, eventHandlers } = options;
|
|
609
|
-
const
|
|
1597
|
+
const { container, elements, context, eventHandlers, componentRegistry, onDependencyNeeded } = options;
|
|
1598
|
+
const startTime = performance.now();
|
|
1599
|
+
const memorySampler = getMemorySampler();
|
|
1600
|
+
const longTaskObserver = getLongTaskObserver();
|
|
1601
|
+
const memoryBefore = memorySampler.sample();
|
|
1602
|
+
longTaskObserver.start();
|
|
610
1603
|
const rootElements = elements.filter((el) => !el.parent || el.parent === null);
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
};
|
|
619
|
-
container.innerHTML = "";
|
|
620
|
-
if (rootElements.length === 0) {
|
|
621
|
-
return {
|
|
622
|
-
rootElement: null,
|
|
623
|
-
update: (newContext) => update(state, newContext),
|
|
624
|
-
destroy: () => destroy(state)
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
if (rootElements.length === 1) {
|
|
628
|
-
state.rootElement = renderElement(
|
|
629
|
-
rootElements[0],
|
|
630
|
-
tree,
|
|
1604
|
+
const componentId = rootElements[0]?.componentId || "unknown";
|
|
1605
|
+
const version = "latest";
|
|
1606
|
+
try {
|
|
1607
|
+
const tree = buildTree(elements);
|
|
1608
|
+
const state = {
|
|
1609
|
+
container,
|
|
1610
|
+
elements,
|
|
631
1611
|
context,
|
|
632
1612
|
eventHandlers,
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
1613
|
+
elementStates: /* @__PURE__ */ new Map(),
|
|
1614
|
+
rootElement: null,
|
|
1615
|
+
componentRegistry,
|
|
1616
|
+
onDependencyNeeded,
|
|
1617
|
+
renderingStack: /* @__PURE__ */ new Set()
|
|
1618
|
+
};
|
|
1619
|
+
container.innerHTML = "";
|
|
1620
|
+
if (rootElements.length === 0) {
|
|
1621
|
+
const duration2 = performance.now() - startTime;
|
|
1622
|
+
const longTasks2 = longTaskObserver.stop();
|
|
1623
|
+
analytics.trackRender(componentId, version, duration2, {
|
|
1624
|
+
elementCount: 0,
|
|
1625
|
+
longTaskCount: longTasks2
|
|
1626
|
+
});
|
|
1627
|
+
return {
|
|
1628
|
+
rootElement: null,
|
|
1629
|
+
update: (newContext) => update(state, newContext),
|
|
1630
|
+
destroy: () => destroy(state)
|
|
1631
|
+
};
|
|
642
1632
|
}
|
|
643
|
-
|
|
644
|
-
|
|
1633
|
+
if (rootElements.length === 1) {
|
|
1634
|
+
state.rootElement = renderElement(
|
|
1635
|
+
rootElements[0],
|
|
1636
|
+
tree,
|
|
1637
|
+
context,
|
|
1638
|
+
eventHandlers,
|
|
1639
|
+
state.elementStates,
|
|
1640
|
+
state
|
|
1641
|
+
);
|
|
1642
|
+
container.appendChild(state.rootElement);
|
|
1643
|
+
} else {
|
|
1644
|
+
const wrapper = document.createElement("div");
|
|
1645
|
+
wrapper.setAttribute("data-servly-wrapper", "true");
|
|
1646
|
+
for (const root of rootElements) {
|
|
1647
|
+
const rootElement = renderElement(root, tree, context, eventHandlers, state.elementStates, state);
|
|
1648
|
+
wrapper.appendChild(rootElement);
|
|
1649
|
+
}
|
|
1650
|
+
state.rootElement = wrapper;
|
|
1651
|
+
container.appendChild(wrapper);
|
|
1652
|
+
}
|
|
1653
|
+
const duration = performance.now() - startTime;
|
|
1654
|
+
const memoryAfter = memorySampler.sample();
|
|
1655
|
+
const longTasks = longTaskObserver.stop();
|
|
1656
|
+
const heapDelta = memorySampler.calculateDelta(memoryBefore, memoryAfter);
|
|
1657
|
+
analytics.trackRender(componentId, version, duration, {
|
|
1658
|
+
elementCount: elements.length,
|
|
1659
|
+
heapDeltaKB: heapDelta ?? void 0,
|
|
1660
|
+
longTaskCount: longTasks,
|
|
1661
|
+
hasEventHandlers: !!eventHandlers && Object.keys(eventHandlers).length > 0,
|
|
1662
|
+
hasDependencies: !!componentRegistry
|
|
1663
|
+
});
|
|
1664
|
+
return {
|
|
1665
|
+
rootElement: state.rootElement,
|
|
1666
|
+
update: (newContext) => update(state, newContext),
|
|
1667
|
+
destroy: () => destroy(state)
|
|
1668
|
+
};
|
|
1669
|
+
} catch (error) {
|
|
1670
|
+
longTaskObserver.stop();
|
|
1671
|
+
analytics.trackError(componentId, version, error, {
|
|
1672
|
+
errorType: "render"
|
|
1673
|
+
});
|
|
1674
|
+
throw error;
|
|
645
1675
|
}
|
|
646
|
-
return {
|
|
647
|
-
rootElement: state.rootElement,
|
|
648
|
-
update: (newContext) => update(state, newContext),
|
|
649
|
-
destroy: () => destroy(state)
|
|
650
|
-
};
|
|
651
1676
|
}
|
|
652
1677
|
function update(state, newContext) {
|
|
653
1678
|
state.context = newContext;
|
|
@@ -675,6 +1700,9 @@ function update(state, newContext) {
|
|
|
675
1700
|
function destroy(state) {
|
|
676
1701
|
for (const elementState of state.elementStates.values()) {
|
|
677
1702
|
detachEventHandlers(elementState);
|
|
1703
|
+
if (elementState.nestedRender) {
|
|
1704
|
+
elementState.nestedRender.destroy();
|
|
1705
|
+
}
|
|
678
1706
|
}
|
|
679
1707
|
state.elementStates.clear();
|
|
680
1708
|
if (state.rootElement && state.rootElement.parentNode) {
|
|
@@ -682,6 +1710,66 @@ function destroy(state) {
|
|
|
682
1710
|
}
|
|
683
1711
|
state.rootElement = null;
|
|
684
1712
|
}
|
|
1713
|
+
function renderDynamicList(options) {
|
|
1714
|
+
const {
|
|
1715
|
+
targetContainer,
|
|
1716
|
+
blueprint,
|
|
1717
|
+
blueprintVersion,
|
|
1718
|
+
data,
|
|
1719
|
+
renderType = "renderInto",
|
|
1720
|
+
itemKey = "item",
|
|
1721
|
+
indexKey = "index",
|
|
1722
|
+
componentRegistry,
|
|
1723
|
+
context = { props: {} }
|
|
1724
|
+
} = options;
|
|
1725
|
+
let container;
|
|
1726
|
+
if (typeof targetContainer === "string") {
|
|
1727
|
+
container = document.querySelector(targetContainer);
|
|
1728
|
+
} else {
|
|
1729
|
+
container = targetContainer;
|
|
1730
|
+
}
|
|
1731
|
+
if (!container) {
|
|
1732
|
+
console.error(`renderDynamicList: Container not found: ${targetContainer}`);
|
|
1733
|
+
return [];
|
|
1734
|
+
}
|
|
1735
|
+
const blueprintComponent = componentRegistry.get(blueprint, blueprintVersion);
|
|
1736
|
+
if (!blueprintComponent) {
|
|
1737
|
+
console.error(`renderDynamicList: Blueprint not found: ${blueprint}`);
|
|
1738
|
+
return [];
|
|
1739
|
+
}
|
|
1740
|
+
if (renderType === "renderInto") {
|
|
1741
|
+
container.innerHTML = "";
|
|
1742
|
+
}
|
|
1743
|
+
const results = [];
|
|
1744
|
+
const fragment = document.createDocumentFragment();
|
|
1745
|
+
data.forEach((item, index) => {
|
|
1746
|
+
const itemContainer = document.createElement("div");
|
|
1747
|
+
itemContainer.setAttribute("data-servly-list-item", String(index));
|
|
1748
|
+
const itemContext = {
|
|
1749
|
+
props: {
|
|
1750
|
+
...context.props,
|
|
1751
|
+
[itemKey]: item,
|
|
1752
|
+
[indexKey]: index
|
|
1753
|
+
},
|
|
1754
|
+
state: context.state,
|
|
1755
|
+
context: context.context
|
|
1756
|
+
};
|
|
1757
|
+
const result = render({
|
|
1758
|
+
container: itemContainer,
|
|
1759
|
+
elements: blueprintComponent.layout,
|
|
1760
|
+
context: itemContext,
|
|
1761
|
+
componentRegistry
|
|
1762
|
+
});
|
|
1763
|
+
results.push(result);
|
|
1764
|
+
fragment.appendChild(itemContainer);
|
|
1765
|
+
});
|
|
1766
|
+
if (renderType === "prepend") {
|
|
1767
|
+
container.insertBefore(fragment, container.firstChild);
|
|
1768
|
+
} else {
|
|
1769
|
+
container.appendChild(fragment);
|
|
1770
|
+
}
|
|
1771
|
+
return results;
|
|
1772
|
+
}
|
|
685
1773
|
|
|
686
1774
|
// src/cache.ts
|
|
687
1775
|
var DEFAULT_CACHE_CONFIG = {
|
|
@@ -927,126 +2015,15 @@ function invalidateCache(id, version, config = DEFAULT_CACHE_CONFIG) {
|
|
|
927
2015
|
}
|
|
928
2016
|
}
|
|
929
2017
|
|
|
930
|
-
// src/version.ts
|
|
931
|
-
function parseVersion(version) {
|
|
932
|
-
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
933
|
-
if (!match) return null;
|
|
934
|
-
return {
|
|
935
|
-
major: parseInt(match[1], 10),
|
|
936
|
-
minor: parseInt(match[2], 10),
|
|
937
|
-
patch: parseInt(match[3], 10)
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
function compareVersions(a, b) {
|
|
941
|
-
const parsedA = parseVersion(a);
|
|
942
|
-
const parsedB = parseVersion(b);
|
|
943
|
-
if (!parsedA || !parsedB) return 0;
|
|
944
|
-
if (parsedA.major !== parsedB.major) {
|
|
945
|
-
return parsedA.major > parsedB.major ? 1 : -1;
|
|
946
|
-
}
|
|
947
|
-
if (parsedA.minor !== parsedB.minor) {
|
|
948
|
-
return parsedA.minor > parsedB.minor ? 1 : -1;
|
|
949
|
-
}
|
|
950
|
-
if (parsedA.patch !== parsedB.patch) {
|
|
951
|
-
return parsedA.patch > parsedB.patch ? 1 : -1;
|
|
952
|
-
}
|
|
953
|
-
return 0;
|
|
954
|
-
}
|
|
955
|
-
function satisfiesVersion(version, specifier) {
|
|
956
|
-
if (specifier === "latest" || specifier === "*") {
|
|
957
|
-
return true;
|
|
958
|
-
}
|
|
959
|
-
const parsed = parseVersion(version);
|
|
960
|
-
if (!parsed) return false;
|
|
961
|
-
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
962
|
-
return version === specifier;
|
|
963
|
-
}
|
|
964
|
-
if (specifier.startsWith("^")) {
|
|
965
|
-
const specParsed = parseVersion(specifier.slice(1));
|
|
966
|
-
if (!specParsed) return false;
|
|
967
|
-
if (parsed.major !== specParsed.major) return false;
|
|
968
|
-
if (parsed.major === 0) {
|
|
969
|
-
return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
|
|
970
|
-
}
|
|
971
|
-
return compareVersions(version, specifier.slice(1)) >= 0;
|
|
972
|
-
}
|
|
973
|
-
if (specifier.startsWith("~")) {
|
|
974
|
-
const specParsed = parseVersion(specifier.slice(1));
|
|
975
|
-
if (!specParsed) return false;
|
|
976
|
-
return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
|
|
977
|
-
}
|
|
978
|
-
if (specifier.startsWith(">=")) {
|
|
979
|
-
return compareVersions(version, specifier.slice(2)) >= 0;
|
|
980
|
-
}
|
|
981
|
-
if (specifier.startsWith(">")) {
|
|
982
|
-
return compareVersions(version, specifier.slice(1)) > 0;
|
|
983
|
-
}
|
|
984
|
-
if (specifier.startsWith("<=")) {
|
|
985
|
-
return compareVersions(version, specifier.slice(2)) <= 0;
|
|
986
|
-
}
|
|
987
|
-
if (specifier.startsWith("<")) {
|
|
988
|
-
return compareVersions(version, specifier.slice(1)) < 0;
|
|
989
|
-
}
|
|
990
|
-
return false;
|
|
991
|
-
}
|
|
992
|
-
function resolveVersion(versions, specifier = "latest") {
|
|
993
|
-
if (versions.length === 0) {
|
|
994
|
-
return null;
|
|
995
|
-
}
|
|
996
|
-
const sorted = [...versions].sort((a, b) => compareVersions(b, a));
|
|
997
|
-
if (specifier === "latest" || specifier === "*") {
|
|
998
|
-
return sorted[0];
|
|
999
|
-
}
|
|
1000
|
-
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
1001
|
-
return versions.includes(specifier) ? specifier : null;
|
|
1002
|
-
}
|
|
1003
|
-
for (const version of sorted) {
|
|
1004
|
-
if (satisfiesVersion(version, specifier)) {
|
|
1005
|
-
return version;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
return null;
|
|
1009
|
-
}
|
|
1010
|
-
function bumpVersion(currentVersion, bumpType) {
|
|
1011
|
-
const parsed = parseVersion(currentVersion);
|
|
1012
|
-
if (!parsed) return "1.0.0";
|
|
1013
|
-
switch (bumpType) {
|
|
1014
|
-
case "major":
|
|
1015
|
-
return `${parsed.major + 1}.0.0`;
|
|
1016
|
-
case "minor":
|
|
1017
|
-
return `${parsed.major}.${parsed.minor + 1}.0`;
|
|
1018
|
-
case "patch":
|
|
1019
|
-
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
|
|
1020
|
-
default:
|
|
1021
|
-
return currentVersion;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
function isValidSpecifier(specifier) {
|
|
1025
|
-
if (specifier === "latest" || specifier === "*") {
|
|
1026
|
-
return true;
|
|
1027
|
-
}
|
|
1028
|
-
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
1029
|
-
return true;
|
|
1030
|
-
}
|
|
1031
|
-
if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
|
|
1032
|
-
return true;
|
|
1033
|
-
}
|
|
1034
|
-
return false;
|
|
1035
|
-
}
|
|
1036
|
-
function formatVersion(version) {
|
|
1037
|
-
const parsed = parseVersion(version);
|
|
1038
|
-
if (!parsed) return version;
|
|
1039
|
-
return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
2018
|
// src/fetcher.ts
|
|
2019
|
+
init_registry();
|
|
1043
2020
|
var DEFAULT_RETRY_CONFIG = {
|
|
1044
2021
|
maxRetries: 3,
|
|
1045
2022
|
initialDelay: 1e3,
|
|
1046
2023
|
maxDelay: 1e4,
|
|
1047
2024
|
backoffMultiplier: 2
|
|
1048
2025
|
};
|
|
1049
|
-
var registryBaseUrl = "/api/
|
|
2026
|
+
var registryBaseUrl = "/api/views/registry";
|
|
1050
2027
|
function setRegistryUrl(url) {
|
|
1051
2028
|
registryBaseUrl = url;
|
|
1052
2029
|
}
|
|
@@ -1080,19 +2057,15 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
|
|
|
1080
2057
|
}
|
|
1081
2058
|
try {
|
|
1082
2059
|
const response = await fetch(
|
|
1083
|
-
`${baseUrl}/${id}/
|
|
2060
|
+
`${baseUrl}/${id}/resolve?specifier=${encodeURIComponent(specifier)}`,
|
|
1084
2061
|
{ headers }
|
|
1085
2062
|
);
|
|
1086
2063
|
if (!response.ok) {
|
|
1087
2064
|
throw new Error(`Failed to resolve version: ${response.statusText}`);
|
|
1088
2065
|
}
|
|
1089
2066
|
const data = await response.json();
|
|
1090
|
-
if (data.success && data.data?.
|
|
1091
|
-
return data.data.
|
|
1092
|
-
}
|
|
1093
|
-
if (data.data?.versions) {
|
|
1094
|
-
const resolved = resolveVersion(data.data.versions, specifier);
|
|
1095
|
-
if (resolved) return resolved;
|
|
2067
|
+
if (data.success && data.data?.resolved) {
|
|
2068
|
+
return data.data.resolved;
|
|
1096
2069
|
}
|
|
1097
2070
|
throw new Error(data.error || "Failed to resolve version");
|
|
1098
2071
|
} catch (error) {
|
|
@@ -1100,7 +2073,7 @@ async function resolveVersionFromApi(id, specifier, apiKey) {
|
|
|
1100
2073
|
return "latest";
|
|
1101
2074
|
}
|
|
1102
2075
|
}
|
|
1103
|
-
async function fetchFromRegistry(id, version, apiKey) {
|
|
2076
|
+
async function fetchFromRegistry(id, version, apiKey, includeBundle) {
|
|
1104
2077
|
const baseUrl = getRegistryUrl();
|
|
1105
2078
|
const headers = {
|
|
1106
2079
|
"Content-Type": "application/json"
|
|
@@ -1108,25 +2081,39 @@ async function fetchFromRegistry(id, version, apiKey) {
|
|
|
1108
2081
|
if (apiKey) {
|
|
1109
2082
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
1110
2083
|
}
|
|
1111
|
-
|
|
2084
|
+
let url;
|
|
2085
|
+
if (version && version !== "latest" && /^\d+\.\d+\.\d+/.test(version)) {
|
|
2086
|
+
url = `${baseUrl}/${id}/versions/${version}`;
|
|
2087
|
+
} else if (version && version !== "latest") {
|
|
2088
|
+
url = `${baseUrl}/${id}?version=${encodeURIComponent(version)}`;
|
|
2089
|
+
} else {
|
|
2090
|
+
url = `${baseUrl}/${id}`;
|
|
2091
|
+
}
|
|
2092
|
+
if (includeBundle) {
|
|
2093
|
+
url += (url.includes("?") ? "&" : "?") + "bundle=true";
|
|
2094
|
+
}
|
|
1112
2095
|
const response = await fetch(url, { headers });
|
|
1113
2096
|
if (!response.ok) {
|
|
1114
2097
|
if (response.status === 404) {
|
|
1115
|
-
throw new Error(`
|
|
2098
|
+
throw new Error(`View not found: ${id}@${version}`);
|
|
1116
2099
|
}
|
|
1117
2100
|
if (response.status === 401) {
|
|
1118
2101
|
throw new Error("Unauthorized: Invalid or missing API key");
|
|
1119
2102
|
}
|
|
1120
2103
|
if (response.status === 403) {
|
|
1121
|
-
throw new Error("Forbidden: Access denied to this
|
|
2104
|
+
throw new Error("Forbidden: Access denied to this view");
|
|
1122
2105
|
}
|
|
1123
|
-
throw new Error(`Failed to fetch
|
|
2106
|
+
throw new Error(`Failed to fetch view: ${response.statusText}`);
|
|
1124
2107
|
}
|
|
1125
2108
|
const data = await response.json();
|
|
1126
2109
|
if (!data.success || !data.data) {
|
|
1127
|
-
throw new Error(data.error || "Failed to fetch
|
|
2110
|
+
throw new Error(data.error || "Failed to fetch view data");
|
|
1128
2111
|
}
|
|
1129
|
-
|
|
2112
|
+
const result = data.data;
|
|
2113
|
+
if (result.viewId && !result.id) {
|
|
2114
|
+
result.id = result.viewId;
|
|
2115
|
+
}
|
|
2116
|
+
return result;
|
|
1130
2117
|
}
|
|
1131
2118
|
async function fetchComponent(id, options = {}) {
|
|
1132
2119
|
const {
|
|
@@ -1136,19 +2123,32 @@ async function fetchComponent(id, options = {}) {
|
|
|
1136
2123
|
cacheConfig = DEFAULT_CACHE_CONFIG,
|
|
1137
2124
|
retryConfig = {},
|
|
1138
2125
|
forceRefresh = false,
|
|
1139
|
-
signal
|
|
2126
|
+
signal,
|
|
2127
|
+
bundleStrategy = "eager",
|
|
2128
|
+
includeBundle = true
|
|
1140
2129
|
} = options;
|
|
1141
2130
|
const fullRetryConfig = {
|
|
1142
2131
|
...DEFAULT_RETRY_CONFIG,
|
|
1143
2132
|
...retryConfig
|
|
1144
2133
|
};
|
|
2134
|
+
const startTime = performance.now();
|
|
1145
2135
|
if (!forceRefresh) {
|
|
1146
2136
|
const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
|
|
1147
2137
|
if (cached) {
|
|
2138
|
+
let registry;
|
|
2139
|
+
if (cached.bundle) {
|
|
2140
|
+
registry = buildRegistryFromBundle(cached);
|
|
2141
|
+
}
|
|
2142
|
+
const duration = performance.now() - startTime;
|
|
2143
|
+
analytics.trackFetch(id, cached.version, duration, true, {
|
|
2144
|
+
cacheHit: true,
|
|
2145
|
+
fetchDuration: Math.round(duration)
|
|
2146
|
+
});
|
|
1148
2147
|
return {
|
|
1149
2148
|
data: cached,
|
|
1150
2149
|
fromCache: true,
|
|
1151
|
-
version: cached.version
|
|
2150
|
+
version: cached.version,
|
|
2151
|
+
registry
|
|
1152
2152
|
};
|
|
1153
2153
|
}
|
|
1154
2154
|
}
|
|
@@ -1156,28 +2156,52 @@ async function fetchComponent(id, options = {}) {
|
|
|
1156
2156
|
if (!forceRefresh && resolvedVersion !== version) {
|
|
1157
2157
|
const cached = getFromCache(id, resolvedVersion, cacheStrategy, cacheConfig);
|
|
1158
2158
|
if (cached) {
|
|
2159
|
+
let registry;
|
|
2160
|
+
if (cached.bundle) {
|
|
2161
|
+
registry = buildRegistryFromBundle(cached);
|
|
2162
|
+
}
|
|
1159
2163
|
return {
|
|
1160
2164
|
data: cached,
|
|
1161
2165
|
fromCache: true,
|
|
1162
|
-
version: resolvedVersion
|
|
2166
|
+
version: resolvedVersion,
|
|
2167
|
+
registry
|
|
1163
2168
|
};
|
|
1164
2169
|
}
|
|
1165
2170
|
}
|
|
1166
2171
|
let lastError = null;
|
|
2172
|
+
const shouldIncludeBundle = bundleStrategy !== "none" && includeBundle;
|
|
1167
2173
|
for (let attempt = 0; attempt <= fullRetryConfig.maxRetries; attempt++) {
|
|
1168
2174
|
if (signal?.aborted) {
|
|
1169
2175
|
throw new Error("Fetch aborted");
|
|
1170
2176
|
}
|
|
1171
2177
|
try {
|
|
1172
|
-
const data = await fetchFromRegistry(id, resolvedVersion, apiKey);
|
|
2178
|
+
const data = await fetchFromRegistry(id, resolvedVersion, apiKey, shouldIncludeBundle);
|
|
1173
2179
|
setInCache(id, resolvedVersion, data, cacheStrategy, cacheConfig);
|
|
1174
2180
|
if (version !== resolvedVersion) {
|
|
1175
2181
|
setInCache(id, version, data, cacheStrategy, cacheConfig);
|
|
1176
2182
|
}
|
|
2183
|
+
let registry;
|
|
2184
|
+
let pendingDependencies;
|
|
2185
|
+
if (data.bundle) {
|
|
2186
|
+
registry = buildRegistryFromBundle(data);
|
|
2187
|
+
} else if (data.dependencies && bundleStrategy === "lazy") {
|
|
2188
|
+
pendingDependencies = Object.entries(data.dependencies).map(([depId, entry]) => ({
|
|
2189
|
+
id: depId,
|
|
2190
|
+
version: entry.resolved || entry.version
|
|
2191
|
+
}));
|
|
2192
|
+
}
|
|
2193
|
+
const duration = performance.now() - startTime;
|
|
2194
|
+
analytics.trackFetch(id, resolvedVersion, duration, false, {
|
|
2195
|
+
cacheHit: false,
|
|
2196
|
+
fetchDuration: Math.round(duration),
|
|
2197
|
+
dependencyCount: data.dependencies ? Object.keys(data.dependencies).length : 0
|
|
2198
|
+
});
|
|
1177
2199
|
return {
|
|
1178
2200
|
data,
|
|
1179
2201
|
fromCache: false,
|
|
1180
|
-
version: resolvedVersion
|
|
2202
|
+
version: resolvedVersion,
|
|
2203
|
+
registry,
|
|
2204
|
+
pendingDependencies
|
|
1181
2205
|
};
|
|
1182
2206
|
} catch (error) {
|
|
1183
2207
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -1190,12 +2214,66 @@ async function fetchComponent(id, options = {}) {
|
|
|
1190
2214
|
}
|
|
1191
2215
|
}
|
|
1192
2216
|
}
|
|
1193
|
-
|
|
2217
|
+
const finalError = lastError || new Error("Failed to fetch component");
|
|
2218
|
+
analytics.trackError(id, version, finalError, {
|
|
2219
|
+
errorType: "fetch"
|
|
2220
|
+
});
|
|
2221
|
+
throw finalError;
|
|
2222
|
+
}
|
|
2223
|
+
async function fetchComponentWithDependencies(id, options = {}) {
|
|
2224
|
+
const result = await fetchComponent(id, { ...options, includeBundle: true });
|
|
2225
|
+
if (result.pendingDependencies && result.pendingDependencies.length > 0) {
|
|
2226
|
+
const { createRegistry: createRegistry2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
2227
|
+
const registry = result.registry || createRegistry2();
|
|
2228
|
+
await Promise.all(
|
|
2229
|
+
result.pendingDependencies.map(async (dep) => {
|
|
2230
|
+
try {
|
|
2231
|
+
const depResult = await fetchComponent(dep.id, {
|
|
2232
|
+
...options,
|
|
2233
|
+
version: dep.version,
|
|
2234
|
+
bundleStrategy: "none"
|
|
2235
|
+
// Don't recursively bundle
|
|
2236
|
+
});
|
|
2237
|
+
registry.set(dep.id, depResult.version, {
|
|
2238
|
+
layout: depResult.data.layout,
|
|
2239
|
+
propsInterface: depResult.data.propsInterface
|
|
2240
|
+
});
|
|
2241
|
+
} catch (err) {
|
|
2242
|
+
console.warn(`Failed to fetch dependency ${dep.id}:`, err);
|
|
2243
|
+
}
|
|
2244
|
+
})
|
|
2245
|
+
);
|
|
2246
|
+
result.registry = registry;
|
|
2247
|
+
result.pendingDependencies = void 0;
|
|
2248
|
+
}
|
|
2249
|
+
return result;
|
|
2250
|
+
}
|
|
2251
|
+
async function batchFetchComponents(components, options = {}) {
|
|
2252
|
+
const baseUrl = getRegistryUrl();
|
|
2253
|
+
const headers = {
|
|
2254
|
+
"Content-Type": "application/json"
|
|
2255
|
+
};
|
|
2256
|
+
if (options.apiKey) {
|
|
2257
|
+
headers["Authorization"] = `Bearer ${options.apiKey}`;
|
|
2258
|
+
}
|
|
2259
|
+
const response = await fetch(`${baseUrl}/batch`, {
|
|
2260
|
+
method: "POST",
|
|
2261
|
+
headers,
|
|
2262
|
+
body: JSON.stringify({ components })
|
|
2263
|
+
});
|
|
2264
|
+
if (!response.ok) {
|
|
2265
|
+
throw new Error(`Batch fetch failed: ${response.statusText}`);
|
|
2266
|
+
}
|
|
2267
|
+
const data = await response.json();
|
|
2268
|
+
if (!data.success || !data.data) {
|
|
2269
|
+
throw new Error(data.error || "Batch fetch failed");
|
|
2270
|
+
}
|
|
2271
|
+
return data.data;
|
|
1194
2272
|
}
|
|
1195
2273
|
async function prefetchComponents(ids, options = {}) {
|
|
1196
2274
|
const promises = ids.map(
|
|
1197
2275
|
({ id, version }) => fetchComponent(id, { ...options, version }).catch((error) => {
|
|
1198
|
-
console.warn(`Failed to prefetch
|
|
2276
|
+
console.warn(`Failed to prefetch view ${id}:`, error);
|
|
1199
2277
|
})
|
|
1200
2278
|
);
|
|
1201
2279
|
await Promise.all(promises);
|
|
@@ -1204,19 +2282,164 @@ async function isComponentAvailable(id, version = "latest", options = {}) {
|
|
|
1204
2282
|
const { cacheStrategy = "memory", cacheConfig = DEFAULT_CACHE_CONFIG } = options;
|
|
1205
2283
|
const cached = getFromCache(id, version, cacheStrategy, cacheConfig);
|
|
1206
2284
|
if (cached) {
|
|
1207
|
-
return { available: true, cached: true };
|
|
2285
|
+
return { available: true, cached: true, version: cached.version };
|
|
2286
|
+
}
|
|
2287
|
+
const baseUrl = getRegistryUrl();
|
|
2288
|
+
const headers = {
|
|
2289
|
+
"Content-Type": "application/json"
|
|
2290
|
+
};
|
|
2291
|
+
if (options.apiKey) {
|
|
2292
|
+
headers["Authorization"] = `Bearer ${options.apiKey}`;
|
|
1208
2293
|
}
|
|
1209
2294
|
try {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
|
|
2295
|
+
const url = version && version !== "latest" ? `${baseUrl}/${id}/available?version=${encodeURIComponent(version)}` : `${baseUrl}/${id}/available`;
|
|
2296
|
+
const response = await fetch(url, { headers });
|
|
2297
|
+
if (!response.ok) {
|
|
2298
|
+
return { available: false, cached: false };
|
|
2299
|
+
}
|
|
2300
|
+
const data = await response.json();
|
|
2301
|
+
if (data.success && data.data) {
|
|
2302
|
+
return data.data;
|
|
2303
|
+
}
|
|
2304
|
+
return { available: false, cached: false };
|
|
1216
2305
|
} catch {
|
|
1217
2306
|
return { available: false, cached: false };
|
|
1218
2307
|
}
|
|
1219
2308
|
}
|
|
2309
|
+
async function getDependencyTree(id, options = {}) {
|
|
2310
|
+
const baseUrl = getRegistryUrl();
|
|
2311
|
+
const headers = {
|
|
2312
|
+
"Content-Type": "application/json"
|
|
2313
|
+
};
|
|
2314
|
+
if (options.apiKey) {
|
|
2315
|
+
headers["Authorization"] = `Bearer ${options.apiKey}`;
|
|
2316
|
+
}
|
|
2317
|
+
const params = new URLSearchParams();
|
|
2318
|
+
if (options.version) params.set("version", options.version);
|
|
2319
|
+
if (options.depth) params.set("depth", String(options.depth));
|
|
2320
|
+
const url = `${baseUrl}/${id}/dependencies${params.toString() ? "?" + params.toString() : ""}`;
|
|
2321
|
+
const response = await fetch(url, { headers });
|
|
2322
|
+
if (!response.ok) {
|
|
2323
|
+
throw new Error(`Failed to get dependency tree: ${response.statusText}`);
|
|
2324
|
+
}
|
|
2325
|
+
const data = await response.json();
|
|
2326
|
+
if (!data.success || !data.data) {
|
|
2327
|
+
throw new Error(data.error || "Failed to get dependency tree");
|
|
2328
|
+
}
|
|
2329
|
+
return data.data;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
// src/version.ts
|
|
2333
|
+
function parseVersion(version) {
|
|
2334
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
2335
|
+
if (!match) return null;
|
|
2336
|
+
return {
|
|
2337
|
+
major: parseInt(match[1], 10),
|
|
2338
|
+
minor: parseInt(match[2], 10),
|
|
2339
|
+
patch: parseInt(match[3], 10)
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function compareVersions(a, b) {
|
|
2343
|
+
const parsedA = parseVersion(a);
|
|
2344
|
+
const parsedB = parseVersion(b);
|
|
2345
|
+
if (!parsedA || !parsedB) return 0;
|
|
2346
|
+
if (parsedA.major !== parsedB.major) {
|
|
2347
|
+
return parsedA.major > parsedB.major ? 1 : -1;
|
|
2348
|
+
}
|
|
2349
|
+
if (parsedA.minor !== parsedB.minor) {
|
|
2350
|
+
return parsedA.minor > parsedB.minor ? 1 : -1;
|
|
2351
|
+
}
|
|
2352
|
+
if (parsedA.patch !== parsedB.patch) {
|
|
2353
|
+
return parsedA.patch > parsedB.patch ? 1 : -1;
|
|
2354
|
+
}
|
|
2355
|
+
return 0;
|
|
2356
|
+
}
|
|
2357
|
+
function satisfiesVersion(version, specifier) {
|
|
2358
|
+
if (specifier === "latest" || specifier === "*") {
|
|
2359
|
+
return true;
|
|
2360
|
+
}
|
|
2361
|
+
const parsed = parseVersion(version);
|
|
2362
|
+
if (!parsed) return false;
|
|
2363
|
+
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
2364
|
+
return version === specifier;
|
|
2365
|
+
}
|
|
2366
|
+
if (specifier.startsWith("^")) {
|
|
2367
|
+
const specParsed = parseVersion(specifier.slice(1));
|
|
2368
|
+
if (!specParsed) return false;
|
|
2369
|
+
if (parsed.major !== specParsed.major) return false;
|
|
2370
|
+
if (parsed.major === 0) {
|
|
2371
|
+
return parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
|
|
2372
|
+
}
|
|
2373
|
+
return compareVersions(version, specifier.slice(1)) >= 0;
|
|
2374
|
+
}
|
|
2375
|
+
if (specifier.startsWith("~")) {
|
|
2376
|
+
const specParsed = parseVersion(specifier.slice(1));
|
|
2377
|
+
if (!specParsed) return false;
|
|
2378
|
+
return parsed.major === specParsed.major && parsed.minor === specParsed.minor && parsed.patch >= specParsed.patch;
|
|
2379
|
+
}
|
|
2380
|
+
if (specifier.startsWith(">=")) {
|
|
2381
|
+
return compareVersions(version, specifier.slice(2)) >= 0;
|
|
2382
|
+
}
|
|
2383
|
+
if (specifier.startsWith(">")) {
|
|
2384
|
+
return compareVersions(version, specifier.slice(1)) > 0;
|
|
2385
|
+
}
|
|
2386
|
+
if (specifier.startsWith("<=")) {
|
|
2387
|
+
return compareVersions(version, specifier.slice(2)) <= 0;
|
|
2388
|
+
}
|
|
2389
|
+
if (specifier.startsWith("<")) {
|
|
2390
|
+
return compareVersions(version, specifier.slice(1)) < 0;
|
|
2391
|
+
}
|
|
2392
|
+
return false;
|
|
2393
|
+
}
|
|
2394
|
+
function resolveVersion(versions, specifier = "latest") {
|
|
2395
|
+
if (versions.length === 0) {
|
|
2396
|
+
return null;
|
|
2397
|
+
}
|
|
2398
|
+
const sorted = [...versions].sort((a, b) => compareVersions(b, a));
|
|
2399
|
+
if (specifier === "latest" || specifier === "*") {
|
|
2400
|
+
return sorted[0];
|
|
2401
|
+
}
|
|
2402
|
+
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
2403
|
+
return versions.includes(specifier) ? specifier : null;
|
|
2404
|
+
}
|
|
2405
|
+
for (const version of sorted) {
|
|
2406
|
+
if (satisfiesVersion(version, specifier)) {
|
|
2407
|
+
return version;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return null;
|
|
2411
|
+
}
|
|
2412
|
+
function bumpVersion(currentVersion, bumpType) {
|
|
2413
|
+
const parsed = parseVersion(currentVersion);
|
|
2414
|
+
if (!parsed) return "1.0.0";
|
|
2415
|
+
switch (bumpType) {
|
|
2416
|
+
case "major":
|
|
2417
|
+
return `${parsed.major + 1}.0.0`;
|
|
2418
|
+
case "minor":
|
|
2419
|
+
return `${parsed.major}.${parsed.minor + 1}.0`;
|
|
2420
|
+
case "patch":
|
|
2421
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
|
|
2422
|
+
default:
|
|
2423
|
+
return currentVersion;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
function isValidSpecifier(specifier) {
|
|
2427
|
+
if (specifier === "latest" || specifier === "*") {
|
|
2428
|
+
return true;
|
|
2429
|
+
}
|
|
2430
|
+
if (/^\d+\.\d+\.\d+$/.test(specifier)) {
|
|
2431
|
+
return true;
|
|
2432
|
+
}
|
|
2433
|
+
if (/^[\^~><]=?\d+\.\d+\.\d+$/.test(specifier)) {
|
|
2434
|
+
return true;
|
|
2435
|
+
}
|
|
2436
|
+
return false;
|
|
2437
|
+
}
|
|
2438
|
+
function formatVersion(version) {
|
|
2439
|
+
const parsed = parseVersion(version);
|
|
2440
|
+
if (!parsed) return version;
|
|
2441
|
+
return `v${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
|
2442
|
+
}
|
|
1220
2443
|
|
|
1221
2444
|
// src/testRunner.ts
|
|
1222
2445
|
function runTestCase(elements, testCase, container) {
|
|
@@ -1472,29 +2695,51 @@ function getSampleValue(def) {
|
|
|
1472
2695
|
return def.defaultValue;
|
|
1473
2696
|
}
|
|
1474
2697
|
}
|
|
2698
|
+
|
|
2699
|
+
// src/index.ts
|
|
2700
|
+
init_registry();
|
|
1475
2701
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1476
2702
|
0 && (module.exports = {
|
|
2703
|
+
AnalyticsCollector,
|
|
1477
2704
|
DEFAULT_CACHE_CONFIG,
|
|
1478
2705
|
DEFAULT_RETRY_CONFIG,
|
|
2706
|
+
LongTaskObserver,
|
|
2707
|
+
MemorySampler,
|
|
2708
|
+
SessionManager,
|
|
2709
|
+
analytics,
|
|
1479
2710
|
applyStyles,
|
|
2711
|
+
batchFetchComponents,
|
|
1480
2712
|
buildClassName,
|
|
1481
2713
|
buildElementStyles,
|
|
2714
|
+
buildRegistryFromBundle,
|
|
1482
2715
|
bumpVersion,
|
|
1483
2716
|
camelToKebab,
|
|
1484
2717
|
clearAllCaches,
|
|
1485
2718
|
clearLocalStorageCache,
|
|
1486
2719
|
clearMemoryCache,
|
|
1487
2720
|
clearStyles,
|
|
2721
|
+
collectAllDependencies,
|
|
1488
2722
|
compareVersions,
|
|
2723
|
+
configureAnalytics,
|
|
2724
|
+
createRegistry,
|
|
2725
|
+
detectCircularDependencies,
|
|
1489
2726
|
extractBindingKeys,
|
|
2727
|
+
extractDependencies,
|
|
2728
|
+
extractDependenciesFromCode,
|
|
1490
2729
|
fetchComponent,
|
|
2730
|
+
fetchComponentWithDependencies,
|
|
1491
2731
|
formatStyleValue,
|
|
1492
2732
|
formatVersion,
|
|
1493
2733
|
generateTestCases,
|
|
2734
|
+
getAnalytics,
|
|
1494
2735
|
getCacheKey,
|
|
2736
|
+
getDependencyTree,
|
|
1495
2737
|
getFromCache,
|
|
2738
|
+
getLongTaskObserver,
|
|
1496
2739
|
getMemoryCacheSize,
|
|
2740
|
+
getMemorySampler,
|
|
1497
2741
|
getRegistryUrl,
|
|
2742
|
+
getSessionManager,
|
|
1498
2743
|
hasTemplateSyntax,
|
|
1499
2744
|
invalidateCache,
|
|
1500
2745
|
isComponentAvailable,
|
|
@@ -1503,6 +2748,11 @@ function getSampleValue(def) {
|
|
|
1503
2748
|
prefetchComponents,
|
|
1504
2749
|
processStyles,
|
|
1505
2750
|
render,
|
|
2751
|
+
renderDynamicList,
|
|
2752
|
+
resetAnalytics,
|
|
2753
|
+
resetLongTaskObserver,
|
|
2754
|
+
resetMemorySampler,
|
|
2755
|
+
resetSessionManager,
|
|
1506
2756
|
resolveBindingPath,
|
|
1507
2757
|
resolveTemplate,
|
|
1508
2758
|
resolveTemplateValue,
|