@coherent.js/koa 1.0.0-beta.5 → 1.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +5 -1519
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +6 -1515
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
- package/types/index.d.ts +31 -4
package/dist/index.cjs
CHANGED
|
@@ -28,1516 +28,8 @@ __export(index_exports, {
|
|
|
28
28
|
});
|
|
29
29
|
module.exports = __toCommonJS(index_exports);
|
|
30
30
|
|
|
31
|
-
// ../core/src/utils/dependency-utils.js
|
|
32
|
-
async function importPeerDependency(packageName, integrationName) {
|
|
33
|
-
try {
|
|
34
|
-
return await import(packageName);
|
|
35
|
-
} catch {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`${integrationName} integration requires the '${packageName}' package to be installed.
|
|
38
|
-
Please install it with: npm install ${packageName}
|
|
39
|
-
Or with pnpm: pnpm add ${packageName}
|
|
40
|
-
Or with yarn: yarn add ${packageName}`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ../core/src/core/object-utils.js
|
|
46
|
-
function validateComponent(component, path = "root") {
|
|
47
|
-
if (component === null || component === void 0) {
|
|
48
|
-
throw new Error(`Invalid component at ${path}: null or undefined`);
|
|
49
|
-
}
|
|
50
|
-
if (["string", "number", "boolean"].includes(typeof component)) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
if (typeof component === "function") {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
if (Array.isArray(component)) {
|
|
57
|
-
component.forEach((child, index) => {
|
|
58
|
-
validateComponent(child, `${path}[${index}]`);
|
|
59
|
-
});
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
if (typeof component === "object") {
|
|
63
|
-
const keys = Object.keys(component);
|
|
64
|
-
if (keys.length === 0) {
|
|
65
|
-
throw new Error(`Empty object at ${path}`);
|
|
66
|
-
}
|
|
67
|
-
keys.forEach((key) => {
|
|
68
|
-
const value = component[key];
|
|
69
|
-
if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(key) && key !== "text") {
|
|
70
|
-
console.warn(`Potentially invalid tag name at ${path}: ${key}`);
|
|
71
|
-
}
|
|
72
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
73
|
-
if (value.children) {
|
|
74
|
-
validateComponent(value.children, `${path}.${key}.children`);
|
|
75
|
-
}
|
|
76
|
-
} else if (value && typeof value !== "string" && typeof value !== "number" && typeof value !== "function") {
|
|
77
|
-
throw new Error(`Invalid value type at ${path}.${key}: ${typeof value}`);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
throw new Error(`Invalid component type at ${path}: ${typeof component}`);
|
|
83
|
-
}
|
|
84
|
-
function isCoherentObject(obj) {
|
|
85
|
-
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
const keys = Object.keys(obj);
|
|
89
|
-
if (keys.length === 0) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
return keys.every((key) => {
|
|
93
|
-
if (key === "text") return true;
|
|
94
|
-
return /^[a-zA-Z][a-zA-Z0-9-]*$/.test(key);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
function extractProps(coherentObj) {
|
|
98
|
-
if (!isCoherentObject(coherentObj)) {
|
|
99
|
-
return {};
|
|
100
|
-
}
|
|
101
|
-
const props = {};
|
|
102
|
-
const keys = Object.keys(coherentObj);
|
|
103
|
-
keys.forEach((tag) => {
|
|
104
|
-
const value = coherentObj[tag];
|
|
105
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
106
|
-
props[tag] = { ...value };
|
|
107
|
-
} else {
|
|
108
|
-
props[tag] = { text: value };
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
return props;
|
|
112
|
-
}
|
|
113
|
-
function hasChildren(component) {
|
|
114
|
-
if (Array.isArray(component)) {
|
|
115
|
-
return component.length > 0;
|
|
116
|
-
}
|
|
117
|
-
if (isCoherentObject(component)) {
|
|
118
|
-
if (component.children !== void 0 && component.children !== null) {
|
|
119
|
-
return Array.isArray(component.children) ? component.children.length > 0 : true;
|
|
120
|
-
}
|
|
121
|
-
const keys = Object.keys(component);
|
|
122
|
-
return keys.some((key) => {
|
|
123
|
-
const value = component[key];
|
|
124
|
-
return value && typeof value === "object" && value.children;
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
function normalizeChildren(children) {
|
|
130
|
-
if (children === null || children === void 0) {
|
|
131
|
-
return [];
|
|
132
|
-
}
|
|
133
|
-
if (Array.isArray(children)) {
|
|
134
|
-
return children.flat().filter((child) => child !== null && child !== void 0);
|
|
135
|
-
}
|
|
136
|
-
return [children];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ../core/src/performance/monitor.js
|
|
140
|
-
function createPerformanceMonitor(options = {}) {
|
|
141
|
-
const opts = {
|
|
142
|
-
enabled: true,
|
|
143
|
-
metrics: {
|
|
144
|
-
custom: {}
|
|
145
|
-
},
|
|
146
|
-
sampling: {
|
|
147
|
-
enabled: false,
|
|
148
|
-
rate: 1,
|
|
149
|
-
strategy: "random"
|
|
150
|
-
},
|
|
151
|
-
reporting: {
|
|
152
|
-
enabled: false,
|
|
153
|
-
interval: 6e4,
|
|
154
|
-
format: "json",
|
|
155
|
-
batch: {
|
|
156
|
-
enabled: false,
|
|
157
|
-
maxSize: 100,
|
|
158
|
-
flushInterval: 5e3
|
|
159
|
-
},
|
|
160
|
-
onReport: null
|
|
161
|
-
},
|
|
162
|
-
alerts: {
|
|
163
|
-
enabled: true,
|
|
164
|
-
debounceMs: 5e3,
|
|
165
|
-
rules: []
|
|
166
|
-
},
|
|
167
|
-
resources: {
|
|
168
|
-
enabled: false,
|
|
169
|
-
track: ["memory"],
|
|
170
|
-
interval: 1e3
|
|
171
|
-
},
|
|
172
|
-
profiling: {
|
|
173
|
-
enabled: false,
|
|
174
|
-
mode: "production",
|
|
175
|
-
flamegraph: false,
|
|
176
|
-
tracing: {
|
|
177
|
-
enabled: false,
|
|
178
|
-
sampleRate: 0.01
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
...options
|
|
182
|
-
};
|
|
183
|
-
opts.reporting.batch = {
|
|
184
|
-
enabled: false,
|
|
185
|
-
maxSize: 100,
|
|
186
|
-
flushInterval: 5e3,
|
|
187
|
-
...options.reporting?.batch || {}
|
|
188
|
-
};
|
|
189
|
-
const metrics = {
|
|
190
|
-
builtin: {
|
|
191
|
-
renderTime: { type: "histogram", unit: "ms", values: [] },
|
|
192
|
-
componentCount: { type: "counter", unit: "renders", value: 0 },
|
|
193
|
-
errorCount: { type: "counter", unit: "errors", value: 0 },
|
|
194
|
-
memoryUsage: { type: "gauge", unit: "MB", values: [] }
|
|
195
|
-
},
|
|
196
|
-
custom: {}
|
|
197
|
-
};
|
|
198
|
-
Object.entries(opts.metrics.custom).forEach(([name, config]) => {
|
|
199
|
-
metrics.custom[name] = {
|
|
200
|
-
type: config.type || "counter",
|
|
201
|
-
unit: config.unit || "",
|
|
202
|
-
threshold: config.threshold,
|
|
203
|
-
values: config.type === "histogram" ? [] : void 0,
|
|
204
|
-
value: config.type === "counter" || config.type === "gauge" ? 0 : void 0
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
const samplingState = {
|
|
208
|
-
count: 0,
|
|
209
|
-
sampled: 0,
|
|
210
|
-
adaptiveRate: opts.sampling.rate
|
|
211
|
-
};
|
|
212
|
-
const reportingState = {
|
|
213
|
-
batch: [],
|
|
214
|
-
lastReport: Date.now(),
|
|
215
|
-
reportTimer: null,
|
|
216
|
-
flushTimer: null
|
|
217
|
-
};
|
|
218
|
-
const alertState = {
|
|
219
|
-
triggered: /* @__PURE__ */ new Map(),
|
|
220
|
-
history: []
|
|
221
|
-
};
|
|
222
|
-
const resourceState = {
|
|
223
|
-
samples: [],
|
|
224
|
-
timer: null
|
|
225
|
-
};
|
|
226
|
-
const profilingState = {
|
|
227
|
-
traces: [],
|
|
228
|
-
flamegraphData: []
|
|
229
|
-
};
|
|
230
|
-
const stats = {
|
|
231
|
-
metricsRecorded: 0,
|
|
232
|
-
sampleRate: opts.sampling.rate,
|
|
233
|
-
reportsGenerated: 0,
|
|
234
|
-
alertsTriggered: 0
|
|
235
|
-
};
|
|
236
|
-
function shouldSample() {
|
|
237
|
-
if (!opts.sampling.enabled) return true;
|
|
238
|
-
samplingState.count++;
|
|
239
|
-
if (opts.sampling.strategy === "random") {
|
|
240
|
-
return Math.random() < samplingState.adaptiveRate;
|
|
241
|
-
} else if (opts.sampling.strategy === "deterministic") {
|
|
242
|
-
return samplingState.count % Math.ceil(1 / samplingState.adaptiveRate) === 0;
|
|
243
|
-
} else if (opts.sampling.strategy === "adaptive") {
|
|
244
|
-
const recentRenderTimes = metrics.builtin.renderTime.values.slice(-10);
|
|
245
|
-
if (recentRenderTimes.length > 0) {
|
|
246
|
-
const avgTime = recentRenderTimes.reduce((a, b) => a + b, 0) / recentRenderTimes.length;
|
|
247
|
-
samplingState.adaptiveRate = avgTime > 16 ? Math.min(1, opts.sampling.rate * 2) : opts.sampling.rate;
|
|
248
|
-
}
|
|
249
|
-
return Math.random() < samplingState.adaptiveRate;
|
|
250
|
-
}
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
function recordMetric(name, value, metadata = {}) {
|
|
254
|
-
if (!opts.enabled) return;
|
|
255
|
-
if (!shouldSample()) return;
|
|
256
|
-
stats.metricsRecorded++;
|
|
257
|
-
const builtinMetric = metrics.builtin[name];
|
|
258
|
-
if (builtinMetric) {
|
|
259
|
-
if (builtinMetric.type === "histogram") {
|
|
260
|
-
builtinMetric.values.push(value);
|
|
261
|
-
if (builtinMetric.values.length > 1e3) {
|
|
262
|
-
builtinMetric.values = builtinMetric.values.slice(-1e3);
|
|
263
|
-
}
|
|
264
|
-
} else if (builtinMetric.type === "counter") {
|
|
265
|
-
builtinMetric.value += value;
|
|
266
|
-
} else if (builtinMetric.type === "gauge") {
|
|
267
|
-
builtinMetric.values.push(value);
|
|
268
|
-
if (builtinMetric.values.length > 100) {
|
|
269
|
-
builtinMetric.values = builtinMetric.values.slice(-100);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
const customMetric = metrics.custom[name];
|
|
274
|
-
if (customMetric) {
|
|
275
|
-
if (customMetric.type === "histogram") {
|
|
276
|
-
customMetric.values = customMetric.values || [];
|
|
277
|
-
customMetric.values.push(value);
|
|
278
|
-
if (customMetric.values.length > 1e3) {
|
|
279
|
-
customMetric.values = customMetric.values.slice(-1e3);
|
|
280
|
-
}
|
|
281
|
-
} else if (customMetric.type === "counter") {
|
|
282
|
-
customMetric.value = (customMetric.value || 0) + value;
|
|
283
|
-
} else if (customMetric.type === "gauge") {
|
|
284
|
-
customMetric.values = customMetric.values || [];
|
|
285
|
-
customMetric.values.push(value);
|
|
286
|
-
if (customMetric.values.length > 100) {
|
|
287
|
-
customMetric.values = customMetric.values.slice(-100);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
if (customMetric.threshold) {
|
|
291
|
-
const currentValue = customMetric.type === "histogram" || customMetric.type === "gauge" ? customMetric.values[customMetric.values.length - 1] : customMetric.value;
|
|
292
|
-
if (currentValue > customMetric.threshold) {
|
|
293
|
-
checkAlerts(name, currentValue);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
if (opts.reporting.enabled && opts.reporting.batch.enabled) {
|
|
298
|
-
reportingState.batch.push({
|
|
299
|
-
metric: name,
|
|
300
|
-
value,
|
|
301
|
-
metadata,
|
|
302
|
-
timestamp: Date.now()
|
|
303
|
-
});
|
|
304
|
-
if (reportingState.batch.length >= opts.reporting.batch.maxSize) {
|
|
305
|
-
flushBatch();
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
checkAlerts(name, value);
|
|
309
|
-
}
|
|
310
|
-
function checkAlerts(metric, value) {
|
|
311
|
-
if (!opts.alerts.enabled) return;
|
|
312
|
-
opts.alerts.rules.forEach((rule) => {
|
|
313
|
-
if (rule.metric !== metric) return;
|
|
314
|
-
let triggered = false;
|
|
315
|
-
if (rule.condition === "exceeds" && value > rule.threshold) {
|
|
316
|
-
triggered = true;
|
|
317
|
-
} else if (rule.condition === "below" && value < rule.threshold) {
|
|
318
|
-
triggered = true;
|
|
319
|
-
} else if (rule.condition === "equals" && value === rule.threshold) {
|
|
320
|
-
triggered = true;
|
|
321
|
-
}
|
|
322
|
-
if (triggered) {
|
|
323
|
-
const alertKey = `${rule.metric}-${rule.condition}-${rule.threshold}`;
|
|
324
|
-
const lastTriggered = alertState.triggered.get(alertKey);
|
|
325
|
-
const now = Date.now();
|
|
326
|
-
if (!lastTriggered || now - lastTriggered > opts.alerts.debounceMs) {
|
|
327
|
-
alertState.triggered.set(alertKey, now);
|
|
328
|
-
alertState.history.push({
|
|
329
|
-
rule,
|
|
330
|
-
value,
|
|
331
|
-
timestamp: now
|
|
332
|
-
});
|
|
333
|
-
stats.alertsTriggered++;
|
|
334
|
-
if (rule.action) {
|
|
335
|
-
rule.action(value, rule);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
function flushBatch() {
|
|
342
|
-
if (reportingState.batch.length === 0) return;
|
|
343
|
-
const batch = [...reportingState.batch];
|
|
344
|
-
reportingState.batch = [];
|
|
345
|
-
if (opts.reporting.onReport) {
|
|
346
|
-
opts.reporting.onReport({ type: "batch", data: batch });
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
function generateReport() {
|
|
350
|
-
const report = {
|
|
351
|
-
timestamp: Date.now(),
|
|
352
|
-
statistics: { ...stats },
|
|
353
|
-
metrics: {}
|
|
354
|
-
};
|
|
355
|
-
Object.entries(metrics.builtin).forEach(([name, metric]) => {
|
|
356
|
-
if (metric.type === "histogram") {
|
|
357
|
-
report.metrics[name] = {
|
|
358
|
-
type: "histogram",
|
|
359
|
-
unit: metric.unit,
|
|
360
|
-
count: metric.values.length,
|
|
361
|
-
min: metric.values.length > 0 ? Math.min(...metric.values) : 0,
|
|
362
|
-
max: metric.values.length > 0 ? Math.max(...metric.values) : 0,
|
|
363
|
-
avg: metric.values.length > 0 ? metric.values.reduce((a, b) => a + b, 0) / metric.values.length : 0,
|
|
364
|
-
p50: percentile(metric.values, 0.5),
|
|
365
|
-
p95: percentile(metric.values, 0.95),
|
|
366
|
-
p99: percentile(metric.values, 0.99)
|
|
367
|
-
};
|
|
368
|
-
} else if (metric.type === "counter") {
|
|
369
|
-
report.metrics[name] = {
|
|
370
|
-
type: "counter",
|
|
371
|
-
unit: metric.unit,
|
|
372
|
-
value: metric.value
|
|
373
|
-
};
|
|
374
|
-
} else if (metric.type === "gauge") {
|
|
375
|
-
report.metrics[name] = {
|
|
376
|
-
type: "gauge",
|
|
377
|
-
unit: metric.unit,
|
|
378
|
-
current: metric.values.length > 0 ? metric.values[metric.values.length - 1] : 0,
|
|
379
|
-
avg: metric.values.length > 0 ? metric.values.reduce((a, b) => a + b, 0) / metric.values.length : 0
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
Object.entries(metrics.custom).forEach(([name, metric]) => {
|
|
384
|
-
if (metric.type === "histogram") {
|
|
385
|
-
report.metrics[name] = {
|
|
386
|
-
type: "histogram",
|
|
387
|
-
unit: metric.unit,
|
|
388
|
-
count: metric.values?.length || 0,
|
|
389
|
-
min: metric.values?.length > 0 ? Math.min(...metric.values) : 0,
|
|
390
|
-
max: metric.values?.length > 0 ? Math.max(...metric.values) : 0,
|
|
391
|
-
avg: metric.values?.length > 0 ? metric.values.reduce((a, b) => a + b, 0) / metric.values.length : 0,
|
|
392
|
-
p95: percentile(metric.values || [], 0.95),
|
|
393
|
-
p99: percentile(metric.values || [], 0.99)
|
|
394
|
-
};
|
|
395
|
-
} else if (metric.type === "counter") {
|
|
396
|
-
report.metrics[name] = {
|
|
397
|
-
type: "counter",
|
|
398
|
-
unit: metric.unit,
|
|
399
|
-
value: metric.value || 0
|
|
400
|
-
};
|
|
401
|
-
} else if (metric.type === "gauge") {
|
|
402
|
-
report.metrics[name] = {
|
|
403
|
-
type: "gauge",
|
|
404
|
-
unit: metric.unit,
|
|
405
|
-
current: metric.values?.length > 0 ? metric.values[metric.values.length - 1] : 0,
|
|
406
|
-
avg: metric.values?.length > 0 ? metric.values.reduce((a, b) => a + b, 0) / metric.values.length : 0
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
report.alerts = {
|
|
411
|
-
total: alertState.history.length,
|
|
412
|
-
recent: alertState.history.slice(-10)
|
|
413
|
-
};
|
|
414
|
-
if (opts.resources.enabled) {
|
|
415
|
-
report.resources = {
|
|
416
|
-
samples: resourceState.samples.slice(-20)
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
stats.reportsGenerated++;
|
|
420
|
-
if (opts.reporting.onReport) {
|
|
421
|
-
opts.reporting.onReport({ type: "report", data: report });
|
|
422
|
-
}
|
|
423
|
-
return report;
|
|
424
|
-
}
|
|
425
|
-
function percentile(values, p) {
|
|
426
|
-
if (values.length === 0) return 0;
|
|
427
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
428
|
-
const index = Math.ceil(sorted.length * p) - 1;
|
|
429
|
-
return sorted[Math.max(0, index)];
|
|
430
|
-
}
|
|
431
|
-
function startResourceMonitoring() {
|
|
432
|
-
if (!opts.resources.enabled) return;
|
|
433
|
-
const collectResources = () => {
|
|
434
|
-
const sample = {
|
|
435
|
-
timestamp: Date.now()
|
|
436
|
-
};
|
|
437
|
-
if (opts.resources.track.includes("memory")) {
|
|
438
|
-
if (typeof process !== "undefined" && process.memoryUsage) {
|
|
439
|
-
const mem = process.memoryUsage();
|
|
440
|
-
sample.memory = {
|
|
441
|
-
heapUsed: mem.heapUsed / 1024 / 1024,
|
|
442
|
-
heapTotal: mem.heapTotal / 1024 / 1024,
|
|
443
|
-
external: mem.external / 1024 / 1024,
|
|
444
|
-
rss: mem.rss / 1024 / 1024
|
|
445
|
-
};
|
|
446
|
-
} else if (typeof performance !== "undefined" && performance.memory) {
|
|
447
|
-
sample.memory = {
|
|
448
|
-
heapUsed: performance.memory.usedJSHeapSize / 1024 / 1024,
|
|
449
|
-
heapTotal: performance.memory.totalJSHeapSize / 1024 / 1024
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
resourceState.samples.push(sample);
|
|
454
|
-
if (resourceState.samples.length > 100) {
|
|
455
|
-
resourceState.samples = resourceState.samples.slice(-100);
|
|
456
|
-
}
|
|
457
|
-
resourceState.timer = setTimeout(collectResources, opts.resources.interval);
|
|
458
|
-
};
|
|
459
|
-
collectResources();
|
|
460
|
-
}
|
|
461
|
-
function stopResourceMonitoring() {
|
|
462
|
-
if (resourceState.timer) {
|
|
463
|
-
clearTimeout(resourceState.timer);
|
|
464
|
-
resourceState.timer = null;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
function startReporting() {
|
|
468
|
-
if (!opts.reporting.enabled) return;
|
|
469
|
-
reportingState.reportTimer = setInterval(() => {
|
|
470
|
-
generateReport();
|
|
471
|
-
}, opts.reporting.interval);
|
|
472
|
-
if (opts.reporting.batch.enabled) {
|
|
473
|
-
reportingState.flushTimer = setInterval(() => {
|
|
474
|
-
flushBatch();
|
|
475
|
-
}, opts.reporting.batch.flushInterval);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
function stopReporting() {
|
|
479
|
-
if (reportingState.reportTimer) {
|
|
480
|
-
clearInterval(reportingState.reportTimer);
|
|
481
|
-
reportingState.reportTimer = null;
|
|
482
|
-
}
|
|
483
|
-
if (reportingState.flushTimer) {
|
|
484
|
-
clearInterval(reportingState.flushTimer);
|
|
485
|
-
reportingState.flushTimer = null;
|
|
486
|
-
}
|
|
487
|
-
flushBatch();
|
|
488
|
-
}
|
|
489
|
-
function startProfiling() {
|
|
490
|
-
if (!opts.profiling.enabled) return;
|
|
491
|
-
}
|
|
492
|
-
function recordTrace(name, duration, metadata = {}) {
|
|
493
|
-
if (!opts.profiling.enabled || !opts.profiling.tracing.enabled) return;
|
|
494
|
-
if (Math.random() < opts.profiling.tracing.sampleRate) {
|
|
495
|
-
profilingState.traces.push({
|
|
496
|
-
name,
|
|
497
|
-
duration,
|
|
498
|
-
metadata,
|
|
499
|
-
timestamp: Date.now()
|
|
500
|
-
});
|
|
501
|
-
if (profilingState.traces.length > 1e3) {
|
|
502
|
-
profilingState.traces = profilingState.traces.slice(-1e3);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
function measure(name, fn, metadata = {}) {
|
|
507
|
-
if (!opts.enabled) return fn();
|
|
508
|
-
const start = performance.now();
|
|
509
|
-
try {
|
|
510
|
-
const result = fn();
|
|
511
|
-
const duration = performance.now() - start;
|
|
512
|
-
recordMetric("renderTime", duration, { name, ...metadata });
|
|
513
|
-
recordTrace(name, duration, metadata);
|
|
514
|
-
return result;
|
|
515
|
-
} catch (error) {
|
|
516
|
-
recordMetric("errorCount", 1, { name, error: error.message });
|
|
517
|
-
throw error;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
async function measureAsync(name, fn, metadata = {}) {
|
|
521
|
-
if (!opts.enabled) return fn();
|
|
522
|
-
const start = performance.now();
|
|
523
|
-
try {
|
|
524
|
-
const result = await fn();
|
|
525
|
-
const duration = performance.now() - start;
|
|
526
|
-
recordMetric("renderTime", duration, { name, ...metadata });
|
|
527
|
-
recordTrace(name, duration, metadata);
|
|
528
|
-
return result;
|
|
529
|
-
} catch (error) {
|
|
530
|
-
recordMetric("errorCount", 1, { name, error: error.message });
|
|
531
|
-
throw error;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
function addMetric(name, config) {
|
|
535
|
-
metrics.custom[name] = {
|
|
536
|
-
type: config.type || "counter",
|
|
537
|
-
unit: config.unit || "",
|
|
538
|
-
threshold: config.threshold,
|
|
539
|
-
values: config.type === "histogram" ? [] : void 0,
|
|
540
|
-
value: config.type === "counter" || config.type === "gauge" ? 0 : void 0
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
function addAlertRule(rule) {
|
|
544
|
-
opts.alerts.rules.push(rule);
|
|
545
|
-
}
|
|
546
|
-
function getStats() {
|
|
547
|
-
return {
|
|
548
|
-
...stats,
|
|
549
|
-
sampleRate: samplingState.adaptiveRate,
|
|
550
|
-
batchSize: reportingState.batch.length,
|
|
551
|
-
resourceSamples: resourceState.samples.length,
|
|
552
|
-
traces: profilingState.traces.length,
|
|
553
|
-
alerts: {
|
|
554
|
-
total: alertState.history.length,
|
|
555
|
-
unique: alertState.triggered.size
|
|
556
|
-
}
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
function reset() {
|
|
560
|
-
Object.values(metrics.builtin).forEach((metric) => {
|
|
561
|
-
if (metric.type === "histogram" || metric.type === "gauge") {
|
|
562
|
-
metric.values = [];
|
|
563
|
-
} else if (metric.type === "counter") {
|
|
564
|
-
metric.value = 0;
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
Object.values(metrics.custom).forEach((metric) => {
|
|
568
|
-
if (metric.type === "histogram" || metric.type === "gauge") {
|
|
569
|
-
metric.values = [];
|
|
570
|
-
} else if (metric.type === "counter") {
|
|
571
|
-
metric.value = 0;
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
samplingState.count = 0;
|
|
575
|
-
samplingState.sampled = 0;
|
|
576
|
-
reportingState.batch = [];
|
|
577
|
-
alertState.history = [];
|
|
578
|
-
alertState.triggered.clear();
|
|
579
|
-
resourceState.samples = [];
|
|
580
|
-
profilingState.traces = [];
|
|
581
|
-
stats.metricsRecorded = 0;
|
|
582
|
-
stats.reportsGenerated = 0;
|
|
583
|
-
stats.alertsTriggered = 0;
|
|
584
|
-
}
|
|
585
|
-
if (opts.enabled) {
|
|
586
|
-
startResourceMonitoring();
|
|
587
|
-
startReporting();
|
|
588
|
-
startProfiling();
|
|
589
|
-
}
|
|
590
|
-
return {
|
|
591
|
-
recordMetric,
|
|
592
|
-
measure,
|
|
593
|
-
measureAsync,
|
|
594
|
-
addMetric,
|
|
595
|
-
addAlertRule,
|
|
596
|
-
generateReport,
|
|
597
|
-
getStats,
|
|
598
|
-
reset,
|
|
599
|
-
start() {
|
|
600
|
-
opts.enabled = true;
|
|
601
|
-
startResourceMonitoring();
|
|
602
|
-
startReporting();
|
|
603
|
-
startProfiling();
|
|
604
|
-
},
|
|
605
|
-
stop() {
|
|
606
|
-
opts.enabled = false;
|
|
607
|
-
stopResourceMonitoring();
|
|
608
|
-
stopReporting();
|
|
609
|
-
return generateReport();
|
|
610
|
-
}
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
var performanceMonitor = createPerformanceMonitor();
|
|
614
|
-
|
|
615
|
-
// ../core/src/rendering/base-renderer.js
|
|
616
|
-
var DEFAULT_RENDERER_CONFIG = {
|
|
617
|
-
// Core rendering options
|
|
618
|
-
maxDepth: 100,
|
|
619
|
-
enableValidation: true,
|
|
620
|
-
enableMonitoring: false,
|
|
621
|
-
validateInput: true,
|
|
622
|
-
// HTML Renderer specific options
|
|
623
|
-
enableCache: true,
|
|
624
|
-
minify: false,
|
|
625
|
-
cacheSize: 1e3,
|
|
626
|
-
cacheTTL: 3e5,
|
|
627
|
-
// 5 minutes
|
|
628
|
-
// Streaming Renderer specific options
|
|
629
|
-
chunkSize: 1024,
|
|
630
|
-
// Size of each chunk in bytes
|
|
631
|
-
bufferSize: 4096,
|
|
632
|
-
// Internal buffer size
|
|
633
|
-
enableMetrics: false,
|
|
634
|
-
// Track streaming metrics
|
|
635
|
-
yieldThreshold: 100,
|
|
636
|
-
// Yield control after N elements
|
|
637
|
-
encoding: "utf8",
|
|
638
|
-
// Output encoding
|
|
639
|
-
// DOM Renderer specific options
|
|
640
|
-
enableHydration: true,
|
|
641
|
-
// Enable hydration support
|
|
642
|
-
namespace: null,
|
|
643
|
-
// SVG namespace support
|
|
644
|
-
// Performance options
|
|
645
|
-
enablePerformanceTracking: false,
|
|
646
|
-
performanceThreshold: 10,
|
|
647
|
-
// ms threshold for slow renders
|
|
648
|
-
// Development options
|
|
649
|
-
enableDevWarnings: typeof process !== "undefined" && process.env && true,
|
|
650
|
-
enableDebugLogging: false,
|
|
651
|
-
// Error handling options
|
|
652
|
-
errorFallback: "",
|
|
653
|
-
// Fallback content on errors
|
|
654
|
-
throwOnError: true
|
|
655
|
-
// Whether to throw or return fallback
|
|
656
|
-
};
|
|
657
|
-
var BaseRenderer = class {
|
|
658
|
-
constructor(options = {}) {
|
|
659
|
-
this.config = this.validateAndMergeConfig(options);
|
|
660
|
-
this.metrics = {
|
|
661
|
-
startTime: null,
|
|
662
|
-
endTime: null,
|
|
663
|
-
elementsProcessed: 0
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Validate and merge configuration options
|
|
668
|
-
*/
|
|
669
|
-
validateAndMergeConfig(options) {
|
|
670
|
-
const config = { ...DEFAULT_RENDERER_CONFIG, ...options };
|
|
671
|
-
if (typeof config.maxDepth !== "number") {
|
|
672
|
-
throw new Error("maxDepth must be a number");
|
|
673
|
-
}
|
|
674
|
-
if (config.maxDepth <= 0) {
|
|
675
|
-
throw new Error("maxDepth must be a positive number");
|
|
676
|
-
}
|
|
677
|
-
if (typeof config.chunkSize !== "number") {
|
|
678
|
-
throw new Error("chunkSize must be a number");
|
|
679
|
-
}
|
|
680
|
-
if (config.chunkSize <= 0) {
|
|
681
|
-
throw new Error("chunkSize must be a positive number");
|
|
682
|
-
}
|
|
683
|
-
if (typeof config.yieldThreshold !== "number") {
|
|
684
|
-
throw new Error("yieldThreshold must be a number");
|
|
685
|
-
}
|
|
686
|
-
if (config.yieldThreshold <= 0) {
|
|
687
|
-
throw new Error("yieldThreshold must be a positive number");
|
|
688
|
-
}
|
|
689
|
-
if (config.enableDevWarnings) {
|
|
690
|
-
if (config.maxDepth > 1e3) {
|
|
691
|
-
console.warn("Coherent.js: maxDepth > 1000 may cause performance issues");
|
|
692
|
-
}
|
|
693
|
-
if (config.chunkSize > 16384) {
|
|
694
|
-
console.warn("Coherent.js: Large chunkSize may increase memory usage");
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
return config;
|
|
698
|
-
}
|
|
699
|
-
/**
|
|
700
|
-
* Get configuration for specific renderer type
|
|
701
|
-
*/
|
|
702
|
-
getRendererConfig(rendererType) {
|
|
703
|
-
const baseConfig = { ...this.config };
|
|
704
|
-
switch (rendererType) {
|
|
705
|
-
case "html":
|
|
706
|
-
return {
|
|
707
|
-
...baseConfig,
|
|
708
|
-
// HTML-specific defaults
|
|
709
|
-
enableCache: baseConfig.enableCache !== false,
|
|
710
|
-
enableMonitoring: baseConfig.enableMonitoring !== false
|
|
711
|
-
};
|
|
712
|
-
case "streaming":
|
|
713
|
-
return {
|
|
714
|
-
...baseConfig,
|
|
715
|
-
// Streaming-specific defaults
|
|
716
|
-
enableMetrics: baseConfig.enableMetrics ?? false,
|
|
717
|
-
maxDepth: baseConfig.maxDepth ?? 1e3
|
|
718
|
-
// Higher default for streaming
|
|
719
|
-
};
|
|
720
|
-
case "dom":
|
|
721
|
-
return {
|
|
722
|
-
...baseConfig,
|
|
723
|
-
// DOM-specific defaults
|
|
724
|
-
enableHydration: baseConfig.enableHydration !== false
|
|
725
|
-
};
|
|
726
|
-
default:
|
|
727
|
-
return baseConfig;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
/**
|
|
731
|
-
* Validate component structure
|
|
732
|
-
*/
|
|
733
|
-
validateComponent(component) {
|
|
734
|
-
if (this.config.validateInput !== false) {
|
|
735
|
-
return validateComponent(component);
|
|
736
|
-
}
|
|
737
|
-
return true;
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* Check if component is valid for rendering
|
|
741
|
-
*/
|
|
742
|
-
isValidComponent(component) {
|
|
743
|
-
if (component === null || component === void 0) return true;
|
|
744
|
-
if (typeof component === "string" || typeof component === "number") return true;
|
|
745
|
-
if (typeof component === "function") return true;
|
|
746
|
-
if (Array.isArray(component)) return component.every((child) => this.isValidComponent(child));
|
|
747
|
-
if (isCoherentObject(component)) return true;
|
|
748
|
-
return false;
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Validate rendering depth to prevent stack overflow
|
|
752
|
-
*/
|
|
753
|
-
validateDepth(depth) {
|
|
754
|
-
if (depth > this.config.maxDepth) {
|
|
755
|
-
throw new Error(`Maximum render depth (${this.config.maxDepth}) exceeded`);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Handle different component types with consistent logic
|
|
760
|
-
*/
|
|
761
|
-
processComponentType(component) {
|
|
762
|
-
if (component === null || component === void 0) {
|
|
763
|
-
return { type: "empty", value: "" };
|
|
764
|
-
}
|
|
765
|
-
if (typeof component === "string") {
|
|
766
|
-
return { type: "text", value: component };
|
|
767
|
-
}
|
|
768
|
-
if (typeof component === "number" || typeof component === "boolean") {
|
|
769
|
-
return { type: "text", value: String(component) };
|
|
770
|
-
}
|
|
771
|
-
if (typeof component === "function") {
|
|
772
|
-
return { type: "function", value: component };
|
|
773
|
-
}
|
|
774
|
-
if (Array.isArray(component)) {
|
|
775
|
-
return { type: "array", value: component };
|
|
776
|
-
}
|
|
777
|
-
if (isCoherentObject(component)) {
|
|
778
|
-
return { type: "element", value: component };
|
|
779
|
-
}
|
|
780
|
-
return { type: "unknown", value: component };
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Execute function components with _error handling
|
|
784
|
-
*/
|
|
785
|
-
executeFunctionComponent(func, depth = 0) {
|
|
786
|
-
try {
|
|
787
|
-
const isContextProvider = func.length > 0 || func.isContextProvider;
|
|
788
|
-
let result;
|
|
789
|
-
if (isContextProvider) {
|
|
790
|
-
result = func((children) => {
|
|
791
|
-
return this.renderComponent(children, this.config, depth + 1);
|
|
792
|
-
});
|
|
793
|
-
} else {
|
|
794
|
-
result = func();
|
|
795
|
-
}
|
|
796
|
-
if (typeof result === "function") {
|
|
797
|
-
return this.executeFunctionComponent(result, depth);
|
|
798
|
-
}
|
|
799
|
-
return result;
|
|
800
|
-
} catch (_error) {
|
|
801
|
-
if (this.config.enableMonitoring) {
|
|
802
|
-
performanceMonitor.recordError("functionComponent", _error);
|
|
803
|
-
}
|
|
804
|
-
if (typeof process !== "undefined" && process.env && true) {
|
|
805
|
-
console.warn("Coherent.js Function Component Error:", _error.message);
|
|
806
|
-
}
|
|
807
|
-
return null;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Process element children consistently
|
|
812
|
-
*/
|
|
813
|
-
processChildren(children, options, depth) {
|
|
814
|
-
if (!hasChildren({ children })) {
|
|
815
|
-
return [];
|
|
816
|
-
}
|
|
817
|
-
const normalizedChildren = normalizeChildren(children);
|
|
818
|
-
return normalizedChildren.map(
|
|
819
|
-
(child) => this.renderComponent(child, options, depth + 1)
|
|
820
|
-
);
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* Extract and process element attributes
|
|
824
|
-
*/
|
|
825
|
-
extractElementAttributes(props) {
|
|
826
|
-
if (!props || typeof props !== "object") return {};
|
|
827
|
-
const attributes = { ...props };
|
|
828
|
-
delete attributes.children;
|
|
829
|
-
delete attributes.text;
|
|
830
|
-
return attributes;
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Record performance metrics
|
|
834
|
-
*/
|
|
835
|
-
recordPerformance(operation, startTime, fromCache = false, metadata = {}) {
|
|
836
|
-
if (this.config.enableMonitoring) {
|
|
837
|
-
performanceMonitor.recordRender(
|
|
838
|
-
operation,
|
|
839
|
-
this.getCurrentTime() - startTime,
|
|
840
|
-
fromCache,
|
|
841
|
-
metadata
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Record _error for monitoring
|
|
847
|
-
*/
|
|
848
|
-
recordError(operation, _error, metadata = {}) {
|
|
849
|
-
if (this.config.enableMonitoring) {
|
|
850
|
-
performanceMonitor.recordError(operation, _error, metadata);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Get current timestamp with fallback
|
|
855
|
-
*/
|
|
856
|
-
getCurrentTime() {
|
|
857
|
-
if (typeof performance !== "undefined" && performance.now) {
|
|
858
|
-
return performance.now();
|
|
859
|
-
}
|
|
860
|
-
return Date.now();
|
|
861
|
-
}
|
|
862
|
-
/**
|
|
863
|
-
* Start performance timing
|
|
864
|
-
*/
|
|
865
|
-
startTiming() {
|
|
866
|
-
this.metrics.startTime = this.getCurrentTime();
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* End performance timing
|
|
870
|
-
*/
|
|
871
|
-
endTiming() {
|
|
872
|
-
this.metrics.endTime = this.getCurrentTime();
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* Get performance metrics
|
|
876
|
-
*/
|
|
877
|
-
getMetrics() {
|
|
878
|
-
const duration = this.metrics.endTime ? this.metrics.endTime - this.metrics.startTime : this.getCurrentTime() - this.metrics.startTime;
|
|
879
|
-
return {
|
|
880
|
-
...this.metrics,
|
|
881
|
-
duration,
|
|
882
|
-
elementsPerSecond: this.metrics.elementsProcessed / (duration / 1e3)
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* Reset metrics for new render
|
|
887
|
-
*/
|
|
888
|
-
resetMetrics() {
|
|
889
|
-
this.metrics = {
|
|
890
|
-
startTime: null,
|
|
891
|
-
endTime: null,
|
|
892
|
-
elementsProcessed: 0
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* Abstract method - must be implemented by subclasses
|
|
897
|
-
*/
|
|
898
|
-
renderComponent() {
|
|
899
|
-
throw new Error("renderComponent must be implemented by subclass");
|
|
900
|
-
}
|
|
901
|
-
/**
|
|
902
|
-
* Abstract method - must be implemented by subclasses
|
|
903
|
-
*/
|
|
904
|
-
render() {
|
|
905
|
-
throw new Error("render must be implemented by subclass");
|
|
906
|
-
}
|
|
907
|
-
};
|
|
908
|
-
var RendererUtils = {
|
|
909
|
-
/**
|
|
910
|
-
* Check if element is static (no functions)
|
|
911
|
-
*/
|
|
912
|
-
isStaticElement(element) {
|
|
913
|
-
if (!element || typeof element !== "object") {
|
|
914
|
-
return typeof element === "string" || typeof element === "number";
|
|
915
|
-
}
|
|
916
|
-
for (const [key, value] of Object.entries(element)) {
|
|
917
|
-
if (typeof value === "function") return false;
|
|
918
|
-
if (key === "children" && Array.isArray(value)) {
|
|
919
|
-
return value.every((child) => RendererUtils.isStaticElement(child));
|
|
920
|
-
}
|
|
921
|
-
if (key === "children" && typeof value === "object" && value !== null) {
|
|
922
|
-
return RendererUtils.isStaticElement(value);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
return true;
|
|
926
|
-
},
|
|
927
|
-
/**
|
|
928
|
-
* Check if object has functions (for caching decisions)
|
|
929
|
-
*/
|
|
930
|
-
hasFunctions(obj, visited = /* @__PURE__ */ new WeakSet()) {
|
|
931
|
-
if (visited.has(obj)) return false;
|
|
932
|
-
visited.add(obj);
|
|
933
|
-
for (const value of Object.values(obj)) {
|
|
934
|
-
if (typeof value === "function") return true;
|
|
935
|
-
if (typeof value === "object" && value !== null && RendererUtils.hasFunctions(value, visited)) {
|
|
936
|
-
return true;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
return false;
|
|
940
|
-
},
|
|
941
|
-
/**
|
|
942
|
-
* Get element complexity score
|
|
943
|
-
*/
|
|
944
|
-
getElementComplexity(element) {
|
|
945
|
-
if (!element || typeof element !== "object") return 1;
|
|
946
|
-
let complexity = Object.keys(element).length;
|
|
947
|
-
if (element.children && Array.isArray(element.children)) {
|
|
948
|
-
complexity += element.children.reduce(
|
|
949
|
-
(sum, child) => sum + RendererUtils.getElementComplexity(child),
|
|
950
|
-
0
|
|
951
|
-
);
|
|
952
|
-
}
|
|
953
|
-
return complexity;
|
|
954
|
-
},
|
|
955
|
-
/**
|
|
956
|
-
* Generate cache key for element
|
|
957
|
-
*/
|
|
958
|
-
generateCacheKey(tagName, element) {
|
|
959
|
-
try {
|
|
960
|
-
const keyData = {
|
|
961
|
-
tag: tagName,
|
|
962
|
-
props: extractProps(element),
|
|
963
|
-
hasChildren: hasChildren(element),
|
|
964
|
-
childrenType: Array.isArray(element.children) ? "array" : typeof element.children
|
|
965
|
-
};
|
|
966
|
-
return `element:${JSON.stringify(keyData)}`;
|
|
967
|
-
} catch (_error) {
|
|
968
|
-
if (typeof process !== "undefined" && process.env && true) {
|
|
969
|
-
console.warn("Failed to generate cache key:", _error);
|
|
970
|
-
}
|
|
971
|
-
return null;
|
|
972
|
-
}
|
|
973
|
-
},
|
|
974
|
-
/**
|
|
975
|
-
* Check if element is cacheable
|
|
976
|
-
*/
|
|
977
|
-
isCacheable(element, options) {
|
|
978
|
-
if (!options.enableCache) return false;
|
|
979
|
-
if (RendererUtils.hasFunctions(element)) return false;
|
|
980
|
-
if (RendererUtils.getElementComplexity(element) > 1e3) return false;
|
|
981
|
-
const cacheKey = RendererUtils.generateCacheKey(element.tagName || "unknown", element);
|
|
982
|
-
if (!cacheKey) return false;
|
|
983
|
-
return true;
|
|
984
|
-
}
|
|
985
|
-
};
|
|
986
|
-
|
|
987
|
-
// ../core/src/core/html-utils.js
|
|
988
|
-
function escapeHtml(text) {
|
|
989
|
-
if (typeof text !== "string") return text;
|
|
990
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
991
|
-
}
|
|
992
|
-
function isVoidElement(tagName) {
|
|
993
|
-
if (typeof tagName !== "string") {
|
|
994
|
-
return false;
|
|
995
|
-
}
|
|
996
|
-
const voidElements = /* @__PURE__ */ new Set([
|
|
997
|
-
"area",
|
|
998
|
-
"base",
|
|
999
|
-
"br",
|
|
1000
|
-
"col",
|
|
1001
|
-
"embed",
|
|
1002
|
-
"hr",
|
|
1003
|
-
"img",
|
|
1004
|
-
"input",
|
|
1005
|
-
"link",
|
|
1006
|
-
"meta",
|
|
1007
|
-
"param",
|
|
1008
|
-
"source",
|
|
1009
|
-
"track",
|
|
1010
|
-
"wbr"
|
|
1011
|
-
]);
|
|
1012
|
-
return voidElements.has(tagName.toLowerCase());
|
|
1013
|
-
}
|
|
1014
|
-
function formatAttributes(props) {
|
|
1015
|
-
let formatted = "";
|
|
1016
|
-
for (const key in props) {
|
|
1017
|
-
if (props.hasOwnProperty(key)) {
|
|
1018
|
-
let value = props[key];
|
|
1019
|
-
const attributeName = key === "className" ? "class" : key;
|
|
1020
|
-
if (typeof value === "function") {
|
|
1021
|
-
if (attributeName.startsWith("on")) {
|
|
1022
|
-
const actionId = `__coherent_action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1023
|
-
const DEBUG = typeof process !== "undefined" && process && process.env && (process.env.COHERENT_DEBUG === "1" || true) || typeof window !== "undefined" && window && window.COHERENT_DEBUG === true;
|
|
1024
|
-
if (typeof global !== "undefined") {
|
|
1025
|
-
if (!global.__coherentActionRegistry) {
|
|
1026
|
-
global.__coherentActionRegistry = {};
|
|
1027
|
-
if (DEBUG) console.log("Initialized global action registry");
|
|
1028
|
-
}
|
|
1029
|
-
global.__coherentActionRegistry[actionId] = value;
|
|
1030
|
-
if (DEBUG) console.log(`Added action ${actionId} to global registry, total: ${Object.keys(global.__coherentActionRegistry).length}`);
|
|
1031
|
-
if (DEBUG) console.log(`Global registry keys: ${Object.keys(global.__coherentActionRegistry).join(", ")}`);
|
|
1032
|
-
if (DEBUG) {
|
|
1033
|
-
if (typeof global.__coherentActionRegistryLog === "undefined") {
|
|
1034
|
-
global.__coherentActionRegistryLog = [];
|
|
1035
|
-
}
|
|
1036
|
-
global.__coherentActionRegistryLog.push({
|
|
1037
|
-
action: "add",
|
|
1038
|
-
actionId,
|
|
1039
|
-
timestamp: Date.now(),
|
|
1040
|
-
registrySize: Object.keys(global.__coherentActionRegistry).length
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
} else if (typeof window !== "undefined") {
|
|
1044
|
-
if (!window.__coherentActionRegistry) {
|
|
1045
|
-
window.__coherentActionRegistry = {};
|
|
1046
|
-
if (DEBUG) console.log("Initialized window action registry");
|
|
1047
|
-
}
|
|
1048
|
-
window.__coherentActionRegistry[actionId] = value;
|
|
1049
|
-
if (DEBUG) console.log(`Added action ${actionId} to window registry, total: ${Object.keys(window.__coherentActionRegistry).length}`);
|
|
1050
|
-
if (DEBUG) console.log(`Window registry keys: ${Object.keys(window.__coherentActionRegistry).join(", ")}`);
|
|
1051
|
-
}
|
|
1052
|
-
const eventType = attributeName.substring(2);
|
|
1053
|
-
formatted += ` data-action="${actionId}" data-event="${eventType}"`;
|
|
1054
|
-
continue;
|
|
1055
|
-
} else {
|
|
1056
|
-
try {
|
|
1057
|
-
value = value();
|
|
1058
|
-
} catch (_error) {
|
|
1059
|
-
console.warn(`Error executing function for attribute '${key}':`, {
|
|
1060
|
-
_error: _error.message,
|
|
1061
|
-
stack: _error.stack,
|
|
1062
|
-
attributeKey: key
|
|
1063
|
-
});
|
|
1064
|
-
value = "";
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
if (value === true) {
|
|
1069
|
-
formatted += ` ${attributeName}`;
|
|
1070
|
-
} else if (value !== false && value !== null && value !== void 0) {
|
|
1071
|
-
formatted += ` ${attributeName}="${escapeHtml(String(value))}"`;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
return formatted.trim();
|
|
1076
|
-
}
|
|
1077
|
-
function minifyHtml(html, options = {}) {
|
|
1078
|
-
if (!options.minify) return html;
|
|
1079
|
-
return html.replace(/<!--[\s\S]*?-->/g, "").replace(/\s+/g, " ").replace(/>\s+</g, "><").trim();
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
// ../core/src/performance/cache-manager.js
|
|
1083
|
-
function createCacheManager(options = {}) {
|
|
1084
|
-
const {
|
|
1085
|
-
maxCacheSize = 1e3,
|
|
1086
|
-
maxMemoryMB = 100,
|
|
1087
|
-
ttlMs = 1e3 * 60 * 5,
|
|
1088
|
-
// 5 minutes
|
|
1089
|
-
enableStatistics = true
|
|
1090
|
-
} = options;
|
|
1091
|
-
const caches = {
|
|
1092
|
-
static: /* @__PURE__ */ new Map(),
|
|
1093
|
-
// Never-changing components
|
|
1094
|
-
component: /* @__PURE__ */ new Map(),
|
|
1095
|
-
// Component results with deps
|
|
1096
|
-
template: /* @__PURE__ */ new Map(),
|
|
1097
|
-
// Template strings
|
|
1098
|
-
data: /* @__PURE__ */ new Map()
|
|
1099
|
-
// General purpose data
|
|
1100
|
-
};
|
|
1101
|
-
let memoryUsage = 0;
|
|
1102
|
-
const stats = {
|
|
1103
|
-
hits: 0,
|
|
1104
|
-
misses: 0,
|
|
1105
|
-
hitRate: {
|
|
1106
|
-
static: 0,
|
|
1107
|
-
component: 0,
|
|
1108
|
-
template: 0,
|
|
1109
|
-
data: 0
|
|
1110
|
-
},
|
|
1111
|
-
accessCount: {
|
|
1112
|
-
static: 0,
|
|
1113
|
-
component: 0,
|
|
1114
|
-
template: 0,
|
|
1115
|
-
data: 0
|
|
1116
|
-
}
|
|
1117
|
-
};
|
|
1118
|
-
let cleanupInterval;
|
|
1119
|
-
if (typeof setInterval === "function") {
|
|
1120
|
-
cleanupInterval = setInterval(() => cleanup(), 3e4);
|
|
1121
|
-
if (cleanupInterval.unref) {
|
|
1122
|
-
cleanupInterval.unref();
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
function generateCacheKey(component, props = {}, context = {}) {
|
|
1126
|
-
const componentStr = typeof component === "function" ? component.name || component.toString() : JSON.stringify(component);
|
|
1127
|
-
const propsStr = JSON.stringify(props, Object.keys(props).sort());
|
|
1128
|
-
const contextStr = JSON.stringify(context);
|
|
1129
|
-
const hash = simpleHash(componentStr + propsStr + contextStr);
|
|
1130
|
-
return `${extractComponentName(component)}_${hash}`;
|
|
1131
|
-
}
|
|
1132
|
-
function get(key, type = "component") {
|
|
1133
|
-
const cache = caches[type] || caches.component;
|
|
1134
|
-
const entry = cache.get(key);
|
|
1135
|
-
if (!entry) {
|
|
1136
|
-
stats.misses++;
|
|
1137
|
-
if (enableStatistics) stats.accessCount[type]++;
|
|
1138
|
-
return null;
|
|
1139
|
-
}
|
|
1140
|
-
if (Date.now() - entry.timestamp > ttlMs) {
|
|
1141
|
-
cache.delete(key);
|
|
1142
|
-
updateMemoryUsage(-entry.size);
|
|
1143
|
-
stats.misses++;
|
|
1144
|
-
if (enableStatistics) stats.accessCount[type]++;
|
|
1145
|
-
return null;
|
|
1146
|
-
}
|
|
1147
|
-
entry.lastAccess = Date.now();
|
|
1148
|
-
entry.accessCount++;
|
|
1149
|
-
stats.hits++;
|
|
1150
|
-
if (enableStatistics) {
|
|
1151
|
-
stats.accessCount[type]++;
|
|
1152
|
-
stats.hitRate[type] = stats.hits / (stats.hits + stats.misses) * 100;
|
|
1153
|
-
}
|
|
1154
|
-
return entry.value;
|
|
1155
|
-
}
|
|
1156
|
-
function set(key, value, type = "component", metadata = {}) {
|
|
1157
|
-
const cache = caches[type] || caches.component;
|
|
1158
|
-
const size = calculateSize(value);
|
|
1159
|
-
if (memoryUsage + size > maxMemoryMB * 1024 * 1024) {
|
|
1160
|
-
optimize(type, size);
|
|
1161
|
-
}
|
|
1162
|
-
const entry = {
|
|
1163
|
-
value,
|
|
1164
|
-
timestamp: Date.now(),
|
|
1165
|
-
lastAccess: Date.now(),
|
|
1166
|
-
size,
|
|
1167
|
-
metadata,
|
|
1168
|
-
accessCount: 0
|
|
1169
|
-
};
|
|
1170
|
-
const existing = cache.get(key);
|
|
1171
|
-
if (existing) {
|
|
1172
|
-
updateMemoryUsage(-existing.size);
|
|
1173
|
-
}
|
|
1174
|
-
cache.set(key, entry);
|
|
1175
|
-
updateMemoryUsage(size);
|
|
1176
|
-
if (cache.size > maxCacheSize) {
|
|
1177
|
-
optimize(type);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
function remove(key, type) {
|
|
1181
|
-
if (type) {
|
|
1182
|
-
const cache = caches[type];
|
|
1183
|
-
if (!cache) return false;
|
|
1184
|
-
const entry = cache.get(key);
|
|
1185
|
-
if (entry) {
|
|
1186
|
-
updateMemoryUsage(-entry.size);
|
|
1187
|
-
return cache.delete(key);
|
|
1188
|
-
}
|
|
1189
|
-
return false;
|
|
1190
|
-
}
|
|
1191
|
-
for (const [, cache] of Object.entries(caches)) {
|
|
1192
|
-
const entry = cache.get(key);
|
|
1193
|
-
if (entry) {
|
|
1194
|
-
updateMemoryUsage(-entry.size);
|
|
1195
|
-
return cache.delete(key);
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
return false;
|
|
1199
|
-
}
|
|
1200
|
-
function clear(type) {
|
|
1201
|
-
if (type) {
|
|
1202
|
-
const cache = caches[type];
|
|
1203
|
-
if (cache) {
|
|
1204
|
-
cache.clear();
|
|
1205
|
-
}
|
|
1206
|
-
} else {
|
|
1207
|
-
Object.values(caches).forEach((cache) => cache.clear());
|
|
1208
|
-
}
|
|
1209
|
-
memoryUsage = 0;
|
|
1210
|
-
}
|
|
1211
|
-
function getStats() {
|
|
1212
|
-
const entries = Object.values(caches).reduce((sum, cache) => sum + cache.size, 0);
|
|
1213
|
-
return {
|
|
1214
|
-
hits: stats.hits,
|
|
1215
|
-
misses: stats.misses,
|
|
1216
|
-
size: memoryUsage,
|
|
1217
|
-
entries,
|
|
1218
|
-
hitRate: stats.hitRate,
|
|
1219
|
-
accessCount: stats.accessCount
|
|
1220
|
-
};
|
|
1221
|
-
}
|
|
1222
|
-
function cleanup() {
|
|
1223
|
-
const now = Date.now();
|
|
1224
|
-
let freed = 0;
|
|
1225
|
-
for (const [, cache] of Object.entries(caches)) {
|
|
1226
|
-
for (const [key, entry] of cache.entries()) {
|
|
1227
|
-
if (now - entry.timestamp > ttlMs) {
|
|
1228
|
-
cache.delete(key);
|
|
1229
|
-
updateMemoryUsage(-entry.size);
|
|
1230
|
-
freed++;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
return { freed };
|
|
1235
|
-
}
|
|
1236
|
-
function calculateSize(value) {
|
|
1237
|
-
if (value === null || value === void 0) return 0;
|
|
1238
|
-
if (typeof value === "string") return value.length * 2;
|
|
1239
|
-
if (typeof value === "number") return 8;
|
|
1240
|
-
if (typeof value === "boolean") return 4;
|
|
1241
|
-
if (Array.isArray(value)) {
|
|
1242
|
-
return value.reduce((sum, item) => sum + calculateSize(item), 0);
|
|
1243
|
-
}
|
|
1244
|
-
if (typeof value === "object") {
|
|
1245
|
-
return Object.values(value).reduce((sum, val) => sum + calculateSize(val), 0);
|
|
1246
|
-
}
|
|
1247
|
-
return 0;
|
|
1248
|
-
}
|
|
1249
|
-
function updateMemoryUsage(delta) {
|
|
1250
|
-
memoryUsage = Math.max(0, memoryUsage + delta);
|
|
1251
|
-
}
|
|
1252
|
-
function optimize(type, requiredSpace = 0) {
|
|
1253
|
-
const cache = caches[type] || caches.component;
|
|
1254
|
-
const entries = Array.from(cache.entries()).sort(([, a], [, b]) => a.lastAccess - b.lastAccess);
|
|
1255
|
-
let freed = 0;
|
|
1256
|
-
for (const [key, entry] of entries) {
|
|
1257
|
-
if (freed >= requiredSpace) break;
|
|
1258
|
-
cache.delete(key);
|
|
1259
|
-
updateMemoryUsage(-entry.size);
|
|
1260
|
-
freed += entry.size;
|
|
1261
|
-
}
|
|
1262
|
-
return { freed };
|
|
1263
|
-
}
|
|
1264
|
-
function simpleHash(str) {
|
|
1265
|
-
let hash = 0;
|
|
1266
|
-
for (let i = 0; i < str.length; i++) {
|
|
1267
|
-
const char = str.charCodeAt(i);
|
|
1268
|
-
hash = (hash << 5) - hash + char;
|
|
1269
|
-
hash = hash & hash;
|
|
1270
|
-
}
|
|
1271
|
-
return Math.abs(hash).toString(36);
|
|
1272
|
-
}
|
|
1273
|
-
function extractComponentName(component) {
|
|
1274
|
-
if (typeof component === "function") {
|
|
1275
|
-
return component.name || "AnonymousComponent";
|
|
1276
|
-
}
|
|
1277
|
-
if (component && typeof component === "object") {
|
|
1278
|
-
const keys = Object.keys(component);
|
|
1279
|
-
return keys.length > 0 ? keys[0] : "ObjectComponent";
|
|
1280
|
-
}
|
|
1281
|
-
return "UnknownComponent";
|
|
1282
|
-
}
|
|
1283
|
-
function destroy() {
|
|
1284
|
-
if (cleanupInterval) {
|
|
1285
|
-
clearInterval(cleanupInterval);
|
|
1286
|
-
}
|
|
1287
|
-
clear();
|
|
1288
|
-
}
|
|
1289
|
-
return {
|
|
1290
|
-
get,
|
|
1291
|
-
set,
|
|
1292
|
-
remove,
|
|
1293
|
-
clear,
|
|
1294
|
-
getStats,
|
|
1295
|
-
cleanup,
|
|
1296
|
-
destroy,
|
|
1297
|
-
generateCacheKey,
|
|
1298
|
-
get memoryUsage() {
|
|
1299
|
-
return memoryUsage;
|
|
1300
|
-
},
|
|
1301
|
-
get maxMemory() {
|
|
1302
|
-
return maxMemoryMB * 1024 * 1024;
|
|
1303
|
-
}
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
var cacheManager = createCacheManager();
|
|
1307
|
-
|
|
1308
|
-
// ../core/src/rendering/html-renderer.js
|
|
1309
|
-
var rendererCache = createCacheManager({
|
|
1310
|
-
maxSize: 1e3,
|
|
1311
|
-
ttlMs: 3e5
|
|
1312
|
-
// 5 minutes
|
|
1313
|
-
});
|
|
1314
|
-
var HTMLRenderer = class extends BaseRenderer {
|
|
1315
|
-
constructor(options = {}) {
|
|
1316
|
-
super({
|
|
1317
|
-
enableCache: options.enableCache !== false,
|
|
1318
|
-
enableMonitoring: options.enableMonitoring !== false,
|
|
1319
|
-
minify: options.minify || false,
|
|
1320
|
-
streaming: options.streaming || false,
|
|
1321
|
-
maxDepth: options.maxDepth || 100,
|
|
1322
|
-
...options
|
|
1323
|
-
});
|
|
1324
|
-
if (this.config.enableCache && !this.cache) {
|
|
1325
|
-
this.cache = rendererCache;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
/**
|
|
1329
|
-
* Main render method - converts components to HTML string
|
|
1330
|
-
*
|
|
1331
|
-
* @param {Object|Array|string|Function} component - Component to render
|
|
1332
|
-
* @param {Object} [options={}] - Rendering options
|
|
1333
|
-
* @param {Object} [options.context] - Rendering context
|
|
1334
|
-
* @param {boolean} [options.enableCache] - Override cache setting
|
|
1335
|
-
* @param {number} [options.depth=0] - Current rendering depth
|
|
1336
|
-
* @returns {string} Rendered HTML string
|
|
1337
|
-
*
|
|
1338
|
-
* @example
|
|
1339
|
-
* const html = renderer.render({
|
|
1340
|
-
* div: {
|
|
1341
|
-
* className: 'container',
|
|
1342
|
-
* children: [
|
|
1343
|
-
* { h1: { text: 'Title' } },
|
|
1344
|
-
* { p: { text: 'Content' } }
|
|
1345
|
-
* ]
|
|
1346
|
-
* }
|
|
1347
|
-
* });
|
|
1348
|
-
*/
|
|
1349
|
-
render(component, options = {}) {
|
|
1350
|
-
const config = { ...this.config, ...options };
|
|
1351
|
-
this.startTiming();
|
|
1352
|
-
try {
|
|
1353
|
-
if (config.validateInput && !this.isValidComponent(component)) {
|
|
1354
|
-
throw new Error("Invalid component structure");
|
|
1355
|
-
}
|
|
1356
|
-
const html = this.renderComponent(component, config, 0);
|
|
1357
|
-
const finalHtml = config.minify ? minifyHtml(html, config) : html;
|
|
1358
|
-
this.endTiming();
|
|
1359
|
-
this.recordPerformance("render", this.metrics.startTime, false, {
|
|
1360
|
-
cacheEnabled: config.enableCache
|
|
1361
|
-
});
|
|
1362
|
-
return finalHtml;
|
|
1363
|
-
} catch (_error) {
|
|
1364
|
-
this.recordError("render", _error);
|
|
1365
|
-
throw _error;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
/**
|
|
1369
|
-
* Render a single component with full optimization pipeline
|
|
1370
|
-
*/
|
|
1371
|
-
renderComponent(component, options, depth = 0) {
|
|
1372
|
-
this.validateDepth(depth);
|
|
1373
|
-
const { type, value } = this.processComponentType(component);
|
|
1374
|
-
switch (type) {
|
|
1375
|
-
case "empty":
|
|
1376
|
-
return "";
|
|
1377
|
-
case "text":
|
|
1378
|
-
return escapeHtml(value);
|
|
1379
|
-
case "function":
|
|
1380
|
-
const result = this.executeFunctionComponent(value, depth);
|
|
1381
|
-
return this.renderComponent(result, options, depth + 1);
|
|
1382
|
-
case "array":
|
|
1383
|
-
return value.map((child) => this.renderComponent(child, options, depth + 1)).join("");
|
|
1384
|
-
case "element":
|
|
1385
|
-
const tagName = Object.keys(value)[0];
|
|
1386
|
-
const elementContent = value[tagName];
|
|
1387
|
-
return this.renderElement(tagName, elementContent, options, depth);
|
|
1388
|
-
default:
|
|
1389
|
-
this.recordError("renderComponent", new Error(`Unknown component type: ${type}`));
|
|
1390
|
-
return "";
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
/**
|
|
1394
|
-
* Render an HTML element with advanced caching and optimization
|
|
1395
|
-
*/
|
|
1396
|
-
renderElement(tagName, element, options, depth = 0) {
|
|
1397
|
-
const startTime = performance.now();
|
|
1398
|
-
if (options.enableMonitoring && this.cache) {
|
|
1399
|
-
}
|
|
1400
|
-
if (options.enableCache && this.cache && RendererUtils.isStaticElement(element)) {
|
|
1401
|
-
const cacheKey = `static:${tagName}:${JSON.stringify(element)}`;
|
|
1402
|
-
const cached = this.cache.get("static", cacheKey);
|
|
1403
|
-
if (cached) {
|
|
1404
|
-
this.recordPerformance(tagName, startTime, true);
|
|
1405
|
-
return cached.value;
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
if (typeof element === "string" || typeof element === "number" || typeof element === "boolean") {
|
|
1409
|
-
const html2 = isVoidElement(tagName) ? `<${tagName}>` : `<${tagName}>${escapeHtml(String(element))}</${tagName}>`;
|
|
1410
|
-
this.cacheIfStatic(tagName, element, html2, options);
|
|
1411
|
-
this.recordPerformance(tagName, startTime, false);
|
|
1412
|
-
return html2;
|
|
1413
|
-
}
|
|
1414
|
-
if (typeof element === "function") {
|
|
1415
|
-
const result = this.executeFunctionComponent(element, depth);
|
|
1416
|
-
return this.renderElement(tagName, result, options, depth);
|
|
1417
|
-
}
|
|
1418
|
-
if (element && typeof element === "object") {
|
|
1419
|
-
return this.renderObjectElement(tagName, element, options, depth);
|
|
1420
|
-
}
|
|
1421
|
-
if (element === null || element === void 0) {
|
|
1422
|
-
const html2 = isVoidElement(tagName) ? `<${tagName}>` : `<${tagName}></${tagName}>`;
|
|
1423
|
-
this.recordPerformance(tagName, startTime, false);
|
|
1424
|
-
return html2;
|
|
1425
|
-
}
|
|
1426
|
-
const html = `<${tagName}>${escapeHtml(String(element))}</${tagName}>`;
|
|
1427
|
-
this.recordPerformance(tagName, startTime, false);
|
|
1428
|
-
return html;
|
|
1429
|
-
}
|
|
1430
|
-
/**
|
|
1431
|
-
* Cache element if it's static
|
|
1432
|
-
*/
|
|
1433
|
-
cacheIfStatic(tagName, element, html) {
|
|
1434
|
-
if (this.config.enableCache && this.cache && RendererUtils.isStaticElement(element)) {
|
|
1435
|
-
const cacheKey = `static:${tagName}:${JSON.stringify(element)}`;
|
|
1436
|
-
this.cache.set("static", cacheKey, html, {
|
|
1437
|
-
ttlMs: this.config.cacheTTL || 5 * 60 * 1e3,
|
|
1438
|
-
// 5 minutes default
|
|
1439
|
-
size: html.length
|
|
1440
|
-
// Approximate size
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
/**
|
|
1445
|
-
* Render complex object elements with attributes and children
|
|
1446
|
-
*/
|
|
1447
|
-
renderObjectElement(tagName, element, options, depth = 0) {
|
|
1448
|
-
const startTime = performance.now();
|
|
1449
|
-
if (options.enableCache && this.cache) {
|
|
1450
|
-
const cacheKey = RendererUtils.generateCacheKey(tagName, element);
|
|
1451
|
-
if (cacheKey) {
|
|
1452
|
-
const cached = this.cache.get(cacheKey);
|
|
1453
|
-
if (cached) {
|
|
1454
|
-
this.recordPerformance(tagName, startTime, true);
|
|
1455
|
-
return cached;
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
const { children, text, ...attributes } = element || {};
|
|
1460
|
-
const attributeString = formatAttributes(attributes);
|
|
1461
|
-
const openingTag = attributeString ? `<${tagName} ${attributeString}>` : `<${tagName}>`;
|
|
1462
|
-
let textContent = "";
|
|
1463
|
-
if (text !== void 0) {
|
|
1464
|
-
const isScript = tagName === "script";
|
|
1465
|
-
const isStyle = tagName === "style";
|
|
1466
|
-
const isRawTag = isScript || isStyle;
|
|
1467
|
-
const raw = typeof text === "function" ? String(text()) : String(text);
|
|
1468
|
-
if (isRawTag) {
|
|
1469
|
-
const safe = raw.replace(/<\/(script)/gi, "<\\/$1").replace(/<\/(style)/gi, "<\\/$1").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1470
|
-
textContent = safe;
|
|
1471
|
-
} else {
|
|
1472
|
-
textContent = escapeHtml(raw);
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
let childrenHtml = "";
|
|
1476
|
-
if (hasChildren(element)) {
|
|
1477
|
-
const normalizedChildren = normalizeChildren(children);
|
|
1478
|
-
childrenHtml = normalizedChildren.map((child) => this.renderComponent(child, options, depth + 1)).join("");
|
|
1479
|
-
}
|
|
1480
|
-
const html = `${openingTag}${textContent}${childrenHtml}</${tagName}>`;
|
|
1481
|
-
if (options.enableCache && this.cache && RendererUtils.isCacheable(element, options)) {
|
|
1482
|
-
const cacheKey = RendererUtils.generateCacheKey(tagName, element);
|
|
1483
|
-
if (cacheKey) {
|
|
1484
|
-
this.cache.set(cacheKey, html);
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
this.recordPerformance(tagName, startTime, false);
|
|
1488
|
-
return html;
|
|
1489
|
-
}
|
|
1490
|
-
};
|
|
1491
|
-
function render(component, options = {}) {
|
|
1492
|
-
const mergedOptions = {
|
|
1493
|
-
enableCache: true,
|
|
1494
|
-
enableMonitoring: false,
|
|
1495
|
-
...options
|
|
1496
|
-
};
|
|
1497
|
-
const renderer = new HTMLRenderer(mergedOptions);
|
|
1498
|
-
return renderer.render(component, mergedOptions);
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
// ../core/src/utils/render-utils.js
|
|
1502
|
-
function renderWithMonitoring(component, options = {}) {
|
|
1503
|
-
const {
|
|
1504
|
-
enablePerformanceMonitoring = false
|
|
1505
|
-
} = options;
|
|
1506
|
-
let html;
|
|
1507
|
-
if (enablePerformanceMonitoring) {
|
|
1508
|
-
const renderId = performanceMonitor.startRender();
|
|
1509
|
-
html = render(component);
|
|
1510
|
-
performanceMonitor.endRender(renderId);
|
|
1511
|
-
} else {
|
|
1512
|
-
html = render(component);
|
|
1513
|
-
}
|
|
1514
|
-
return html;
|
|
1515
|
-
}
|
|
1516
|
-
function renderWithTemplate(component, options = {}) {
|
|
1517
|
-
const {
|
|
1518
|
-
template = "<!DOCTYPE html>\n{{content}}"
|
|
1519
|
-
} = options;
|
|
1520
|
-
const html = renderWithMonitoring(component, options);
|
|
1521
|
-
return template.replace("{{content}}", html);
|
|
1522
|
-
}
|
|
1523
|
-
async function renderComponentFactory(componentFactory, factoryArgs, options = {}) {
|
|
1524
|
-
const component = await Promise.resolve(
|
|
1525
|
-
componentFactory(...factoryArgs)
|
|
1526
|
-
);
|
|
1527
|
-
if (!component) {
|
|
1528
|
-
throw new Error("Component factory returned null/undefined");
|
|
1529
|
-
}
|
|
1530
|
-
return renderWithTemplate(component, options);
|
|
1531
|
-
}
|
|
1532
|
-
function isCoherentComponent(obj) {
|
|
1533
|
-
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
1534
|
-
return false;
|
|
1535
|
-
}
|
|
1536
|
-
const keys = Object.keys(obj);
|
|
1537
|
-
return keys.length === 1;
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
31
|
// src/coherent-koa.js
|
|
32
|
+
var import_core = require("@coherent.js/core");
|
|
1541
33
|
function coherentKoaMiddleware(options = {}) {
|
|
1542
34
|
const {
|
|
1543
35
|
enablePerformanceMonitoring = false,
|
|
@@ -1545,9 +37,9 @@ function coherentKoaMiddleware(options = {}) {
|
|
|
1545
37
|
} = options;
|
|
1546
38
|
return async (ctx, next) => {
|
|
1547
39
|
await next();
|
|
1548
|
-
if (isCoherentComponent(ctx.body)) {
|
|
40
|
+
if ((0, import_core.isCoherentComponent)(ctx.body)) {
|
|
1549
41
|
try {
|
|
1550
|
-
const finalHtml = renderWithTemplate(ctx.body, { enablePerformanceMonitoring, template });
|
|
42
|
+
const finalHtml = (0, import_core.renderWithTemplate)(ctx.body, { enablePerformanceMonitoring, template });
|
|
1551
43
|
ctx.type = "text/html";
|
|
1552
44
|
ctx.body = finalHtml;
|
|
1553
45
|
} catch (_error) {
|
|
@@ -1560,7 +52,7 @@ function coherentKoaMiddleware(options = {}) {
|
|
|
1560
52
|
function createHandler(componentFactory, options = {}) {
|
|
1561
53
|
return async (ctx, next) => {
|
|
1562
54
|
try {
|
|
1563
|
-
const finalHtml = await renderComponentFactory(
|
|
55
|
+
const finalHtml = await (0, import_core.renderComponentFactory)(
|
|
1564
56
|
componentFactory,
|
|
1565
57
|
[ctx, next],
|
|
1566
58
|
options
|
|
@@ -1584,7 +76,7 @@ function setupCoherent(app, options = {}) {
|
|
|
1584
76
|
}
|
|
1585
77
|
async function createKoaIntegration(options = {}) {
|
|
1586
78
|
try {
|
|
1587
|
-
await importPeerDependency("koa", "Koa.js");
|
|
79
|
+
await (0, import_core.importPeerDependency)("koa", "Koa.js");
|
|
1588
80
|
return function(app) {
|
|
1589
81
|
if (!app || typeof app.use !== "function") {
|
|
1590
82
|
throw new Error("Invalid Koa app instance provided");
|
|
@@ -1609,10 +101,4 @@ var coherent_koa_default = {
|
|
|
1609
101
|
createKoaIntegration,
|
|
1610
102
|
setupCoherent
|
|
1611
103
|
});
|
|
1612
|
-
/**
|
|
1613
|
-
* Advanced caching system with memory management and smart invalidation for Coherent.js
|
|
1614
|
-
*
|
|
1615
|
-
* @module @coherent/performance/cache-manager
|
|
1616
|
-
* @license MIT
|
|
1617
|
-
*/
|
|
1618
104
|
//# sourceMappingURL=index.cjs.map
|