@coherent.js/devtools 1.0.0-beta.3 → 1.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/index.js +1272 -23
- package/dist/index.js.map +4 -4
- package/package.json +47 -8
package/dist/index.js
CHANGED
|
@@ -2074,44 +2074,1293 @@ function createDevTools(coherentInstance) {
|
|
|
2074
2074
|
return new DevTools(coherentInstance);
|
|
2075
2075
|
}
|
|
2076
2076
|
|
|
2077
|
-
// src/
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2077
|
+
// ../core/src/core/object-utils.js
|
|
2078
|
+
function isCoherentObject2(obj) {
|
|
2079
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
const keys = Object.keys(obj);
|
|
2083
|
+
if (keys.length === 0) {
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
return keys.every((key) => {
|
|
2087
|
+
if (key === "text") return true;
|
|
2088
|
+
return /^[a-zA-Z][a-zA-Z0-9-]*$/.test(key);
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
function hasChildren(component) {
|
|
2092
|
+
if (Array.isArray(component)) {
|
|
2093
|
+
return component.length > 0;
|
|
2094
|
+
}
|
|
2095
|
+
if (isCoherentObject2(component)) {
|
|
2096
|
+
if (component.children !== void 0 && component.children !== null) {
|
|
2097
|
+
return Array.isArray(component.children) ? component.children.length > 0 : true;
|
|
2098
|
+
}
|
|
2099
|
+
const keys = Object.keys(component);
|
|
2100
|
+
return keys.some((key) => {
|
|
2101
|
+
const value = component[key];
|
|
2102
|
+
return value && typeof value === "object" && value.children;
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
return false;
|
|
2106
|
+
}
|
|
2107
|
+
function normalizeChildren(children) {
|
|
2108
|
+
if (children === null || children === void 0) {
|
|
2109
|
+
return [];
|
|
2110
|
+
}
|
|
2111
|
+
if (Array.isArray(children)) {
|
|
2112
|
+
return children.flat().filter((child) => child !== null && child !== void 0);
|
|
2113
|
+
}
|
|
2114
|
+
return [children];
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// src/component-visualizer.js
|
|
2118
|
+
var ComponentVisualizer = class {
|
|
2119
|
+
constructor(options = {}) {
|
|
2120
|
+
this.options = {
|
|
2121
|
+
maxDepth: options.maxDepth || 50,
|
|
2122
|
+
showProps: options.showProps !== false,
|
|
2123
|
+
showMetadata: options.showMetadata !== false,
|
|
2124
|
+
colorOutput: options.colorOutput !== false,
|
|
2125
|
+
compactMode: options.compactMode || false,
|
|
2126
|
+
...options
|
|
2127
|
+
};
|
|
2128
|
+
this.stats = {
|
|
2129
|
+
totalComponents: 0,
|
|
2130
|
+
totalDepth: 0,
|
|
2131
|
+
staticComponents: 0,
|
|
2132
|
+
dynamicComponents: 0,
|
|
2133
|
+
renderTime: 0
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Visualize a component tree
|
|
2138
|
+
*/
|
|
2139
|
+
visualize(component, name = "Root") {
|
|
2140
|
+
const startTime = performance.now();
|
|
2141
|
+
this.stats = { totalComponents: 0, totalDepth: 0, staticComponents: 0, dynamicComponents: 0, renderTime: 0 };
|
|
2142
|
+
const tree = this.buildTree(component, name, 0);
|
|
2143
|
+
const visualization = this.renderTree(tree);
|
|
2144
|
+
this.stats.renderTime = performance.now() - startTime;
|
|
2145
|
+
return {
|
|
2146
|
+
visualization,
|
|
2147
|
+
stats: { ...this.stats },
|
|
2148
|
+
tree
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Build component tree structure
|
|
2153
|
+
*/
|
|
2154
|
+
buildTree(component, name, depth) {
|
|
2155
|
+
if (depth > this.options.maxDepth) {
|
|
2156
|
+
return {
|
|
2157
|
+
name: "MAX_DEPTH_REACHED",
|
|
2158
|
+
type: "warning",
|
|
2159
|
+
depth,
|
|
2160
|
+
children: [],
|
|
2161
|
+
metadata: { message: `Maximum depth ${this.options.maxDepth} exceeded` }
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
this.stats.totalComponents++;
|
|
2165
|
+
this.stats.totalDepth = Math.max(this.stats.totalDepth, depth);
|
|
2166
|
+
const node = {
|
|
2167
|
+
name,
|
|
2168
|
+
depth,
|
|
2169
|
+
children: [],
|
|
2170
|
+
metadata: {}
|
|
2171
|
+
};
|
|
2172
|
+
if (component === null || component === void 0) {
|
|
2173
|
+
node.type = "empty";
|
|
2174
|
+
node.value = "";
|
|
2175
|
+
this.stats.staticComponents++;
|
|
2176
|
+
} else if (typeof component === "string") {
|
|
2177
|
+
node.type = "text";
|
|
2178
|
+
node.value = component;
|
|
2179
|
+
node.metadata.length = component.length;
|
|
2180
|
+
this.stats.staticComponents++;
|
|
2181
|
+
} else if (typeof component === "number") {
|
|
2182
|
+
node.type = "number";
|
|
2183
|
+
node.value = component;
|
|
2184
|
+
this.stats.staticComponents++;
|
|
2185
|
+
} else if (typeof component === "boolean") {
|
|
2186
|
+
node.type = "boolean";
|
|
2187
|
+
node.value = component;
|
|
2188
|
+
this.stats.staticComponents++;
|
|
2189
|
+
} else if (typeof component === "function") {
|
|
2190
|
+
node.type = "function";
|
|
2191
|
+
node.value = `Function: ${component.name || "anonymous"}`;
|
|
2192
|
+
node.metadata.arity = component.length;
|
|
2193
|
+
node.metadata.isAsync = component.constructor.name === "AsyncFunction";
|
|
2194
|
+
this.stats.dynamicComponents++;
|
|
2195
|
+
} else if (Array.isArray(component)) {
|
|
2196
|
+
node.type = "array";
|
|
2197
|
+
node.metadata.length = component.length;
|
|
2198
|
+
component.forEach((item, _index) => {
|
|
2199
|
+
const childNode = this.buildTree(item, `[${_index}]`, depth + 1);
|
|
2200
|
+
node.children.push(childNode);
|
|
2201
|
+
});
|
|
2202
|
+
this.stats.dynamicComponents++;
|
|
2203
|
+
} else if (isCoherentObject2(component)) {
|
|
2204
|
+
const entries = Object.entries(component);
|
|
2205
|
+
if (entries.length === 1) {
|
|
2206
|
+
const [tagName, props] = entries;
|
|
2207
|
+
node.type = "element";
|
|
2208
|
+
node.tagName = tagName;
|
|
2209
|
+
node.props = this.options.showProps ? this.analyzeProps(props) : {};
|
|
2210
|
+
if (hasChildren(props)) {
|
|
2211
|
+
const children = normalizeChildren(props.children);
|
|
2212
|
+
children.forEach((child, _index) => {
|
|
2213
|
+
const childNode = this.buildTree(child, `${tagName}[${_index}]`, depth + 1);
|
|
2214
|
+
node.children.push(childNode);
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
if (this.hasDynamicContent(props)) {
|
|
2218
|
+
this.stats.dynamicComponents++;
|
|
2219
|
+
node.metadata.dynamic = true;
|
|
2220
|
+
} else {
|
|
2221
|
+
this.stats.staticComponents++;
|
|
2222
|
+
node.metadata.dynamic = false;
|
|
2223
|
+
}
|
|
2224
|
+
} else {
|
|
2225
|
+
node.type = "complex";
|
|
2226
|
+
node.metadata.keys = entries.map(([key]) => key);
|
|
2227
|
+
this.stats.dynamicComponents++;
|
|
2228
|
+
}
|
|
2229
|
+
} else {
|
|
2230
|
+
node.type = "unknown";
|
|
2231
|
+
node.value = String(component);
|
|
2232
|
+
node.metadata.constructor = component.constructor?.name || "Object";
|
|
2233
|
+
this.stats.staticComponents++;
|
|
2234
|
+
}
|
|
2235
|
+
return node;
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Analyze component props
|
|
2239
|
+
*/
|
|
2240
|
+
analyzeProps(props) {
|
|
2241
|
+
const analyzed = {};
|
|
2242
|
+
if (!props || typeof props !== "object") {
|
|
2243
|
+
return analyzed;
|
|
2244
|
+
}
|
|
2245
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
2246
|
+
if (key === "children") return;
|
|
2247
|
+
if (typeof value === "function") {
|
|
2248
|
+
analyzed[key] = {
|
|
2249
|
+
type: "function",
|
|
2250
|
+
name: value.name || "anonymous",
|
|
2251
|
+
isEvent: /^on[A-Z]/.test(key)
|
|
2252
|
+
};
|
|
2253
|
+
} else if (typeof value === "string") {
|
|
2254
|
+
analyzed[key] = {
|
|
2255
|
+
type: "string",
|
|
2256
|
+
length: value.length,
|
|
2257
|
+
preview: value.length > 50 ? `${value.substring(0, 47)}...` : value
|
|
2258
|
+
};
|
|
2259
|
+
} else if (typeof value === "object" && value !== null) {
|
|
2260
|
+
analyzed[key] = {
|
|
2261
|
+
type: "object",
|
|
2262
|
+
keys: Object.keys(value),
|
|
2263
|
+
constructor: value.constructor?.name || "Object"
|
|
2264
|
+
};
|
|
2265
|
+
} else {
|
|
2266
|
+
analyzed[key] = {
|
|
2267
|
+
type: typeof value,
|
|
2268
|
+
value
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
});
|
|
2272
|
+
return analyzed;
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Check if component has dynamic content
|
|
2276
|
+
*/
|
|
2277
|
+
hasDynamicContent(props) {
|
|
2278
|
+
if (typeof props === "object" && props !== null) {
|
|
2279
|
+
for (const value of Object.values(props)) {
|
|
2280
|
+
if (typeof value === "function") return true;
|
|
2281
|
+
if (typeof value === "object" && this.hasDynamicContent(value)) return true;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
return false;
|
|
2285
|
+
}
|
|
2286
|
+
/**
|
|
2287
|
+
* Render tree as formatted text
|
|
2288
|
+
*/
|
|
2289
|
+
renderTree(tree) {
|
|
2290
|
+
const lines = [];
|
|
2291
|
+
if (this.options.colorOutput) {
|
|
2292
|
+
lines.push(this.colorize("\u{1F333} Coherent.js Component Tree", "cyan"));
|
|
2293
|
+
lines.push(this.colorize("\u2550".repeat(40), "cyan"));
|
|
2294
|
+
} else {
|
|
2295
|
+
lines.push("\u{1F333} Coherent.js Component Tree");
|
|
2296
|
+
lines.push("\u2550".repeat(40));
|
|
2297
|
+
}
|
|
2298
|
+
this.renderNode(tree, lines, "", true);
|
|
2299
|
+
if (this.options.showMetadata) {
|
|
2300
|
+
lines.push("");
|
|
2301
|
+
lines.push("\u{1F4CA} Tree Statistics:");
|
|
2302
|
+
lines.push(` Total Components: ${this.stats.totalComponents}`);
|
|
2303
|
+
lines.push(` Max Depth: ${this.stats.totalDepth}`);
|
|
2304
|
+
lines.push(` Static Components: ${this.stats.staticComponents}`);
|
|
2305
|
+
lines.push(` Dynamic Components: ${this.stats.dynamicComponents}`);
|
|
2306
|
+
lines.push(` Render Time: ${this.stats.renderTime.toFixed(2)}ms`);
|
|
2307
|
+
}
|
|
2308
|
+
return lines.join("\n");
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Render individual node
|
|
2312
|
+
*/
|
|
2313
|
+
renderNode(node, lines, prefix = "", isLast = true) {
|
|
2314
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
2315
|
+
const childPrefix = prefix + (isLast ? " " : "\u2502 ");
|
|
2316
|
+
let nodeLine = prefix + connector;
|
|
2317
|
+
if (this.options.colorOutput) {
|
|
2318
|
+
nodeLine += this.getNodeIcon(node.type);
|
|
2319
|
+
nodeLine += this.colorize(node.name, this.getNodeColor(node.type));
|
|
2320
|
+
} else {
|
|
2321
|
+
nodeLine += this.getNodeIcon(node.type) + node.name;
|
|
2322
|
+
}
|
|
2323
|
+
if (!this.options.compactMode) {
|
|
2324
|
+
nodeLine += ` (${node.type})`;
|
|
2325
|
+
if (node.type === "element") {
|
|
2326
|
+
nodeLine += ` <${node.tagName}>`;
|
|
2327
|
+
} else if (node.type === "text" && node.value) {
|
|
2328
|
+
nodeLine += `: "${node.value.substring(0, 30)}${node.value.length > 30 ? "..." : ""}"`;
|
|
2329
|
+
} else if (node.type === "function") {
|
|
2330
|
+
nodeLine += `(${node.metadata.arity || 0} args)`;
|
|
2331
|
+
}
|
|
2332
|
+
if (node.metadata.dynamic !== void 0) {
|
|
2333
|
+
nodeLine += node.metadata.dynamic ? " \u{1F504}" : " \u{1F4CC}";
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
lines.push(nodeLine);
|
|
2337
|
+
if (this.options.showProps && node.props && !this.options.compactMode) {
|
|
2338
|
+
Object.entries(node.props).forEach(([key, prop], _index) => {
|
|
2339
|
+
const isLastProp = _index === Object.keys(node.props).length - 1;
|
|
2340
|
+
const propConnector = isLastProp ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
2341
|
+
const _propPrefix = childPrefix + (isLastProp && node.children.length === 0 ? " " : "\u2502 ");
|
|
2342
|
+
let propLine = childPrefix + propConnector;
|
|
2343
|
+
if (this.options.colorOutput) {
|
|
2344
|
+
propLine += this.colorize(key, "yellow");
|
|
2345
|
+
} else {
|
|
2346
|
+
propLine += key;
|
|
2347
|
+
}
|
|
2348
|
+
propLine += `: ${this.formatPropValue(prop)}`;
|
|
2349
|
+
lines.push(propLine);
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
node.children.forEach((child, index) => {
|
|
2353
|
+
const isLastChild = index === node.children.length - 1;
|
|
2354
|
+
this.renderNode(child, lines, childPrefix, isLastChild);
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* Get node icon based on type
|
|
2359
|
+
*/
|
|
2360
|
+
getNodeIcon(type) {
|
|
2361
|
+
const icons = {
|
|
2362
|
+
element: "\u{1F3F7}\uFE0F ",
|
|
2363
|
+
text: "\u{1F4DD} ",
|
|
2364
|
+
function: "\u26A1 ",
|
|
2365
|
+
array: "\u{1F4CB} ",
|
|
2366
|
+
empty: "\u2B55 ",
|
|
2367
|
+
number: "\u{1F522} ",
|
|
2368
|
+
boolean: "\u2611\uFE0F ",
|
|
2369
|
+
complex: "\u{1F4E6} ",
|
|
2370
|
+
unknown: "\u2753 ",
|
|
2371
|
+
warning: "\u26A0\uFE0F "
|
|
2372
|
+
};
|
|
2373
|
+
return icons[type] || "\u{1F4C4} ";
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Get node color based on type
|
|
2377
|
+
*/
|
|
2378
|
+
getNodeColor(type) {
|
|
2379
|
+
const colors = {
|
|
2380
|
+
element: "green",
|
|
2381
|
+
text: "blue",
|
|
2382
|
+
function: "magenta",
|
|
2383
|
+
array: "cyan",
|
|
2384
|
+
empty: "gray",
|
|
2385
|
+
number: "yellow",
|
|
2386
|
+
boolean: "yellow",
|
|
2387
|
+
complex: "red",
|
|
2388
|
+
unknown: "red",
|
|
2389
|
+
warning: "red"
|
|
2390
|
+
};
|
|
2391
|
+
return colors[type] || "white";
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Format property value for display
|
|
2395
|
+
*/
|
|
2396
|
+
formatPropValue(prop) {
|
|
2397
|
+
if (prop.type === "function") {
|
|
2398
|
+
return `\u26A1 ${prop.name}${prop.isEvent ? " (event)" : ""}`;
|
|
2399
|
+
} else if (prop.type === "string") {
|
|
2400
|
+
return `"${prop.preview}"`;
|
|
2401
|
+
} else if (prop.type === "object") {
|
|
2402
|
+
return `${prop.constructor} {${prop.keys.join(", ")}}`;
|
|
2403
|
+
} else {
|
|
2404
|
+
return String(prop.value);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
/**
|
|
2408
|
+
* Add color to text (ANSI colors)
|
|
2409
|
+
*/
|
|
2410
|
+
colorize(text, color) {
|
|
2411
|
+
const colors = {
|
|
2412
|
+
black: "\x1B[30m",
|
|
2413
|
+
red: "\x1B[31m",
|
|
2414
|
+
green: "\x1B[32m",
|
|
2415
|
+
yellow: "\x1B[33m",
|
|
2416
|
+
blue: "\x1B[34m",
|
|
2417
|
+
magenta: "\x1B[35m",
|
|
2418
|
+
cyan: "\x1B[36m",
|
|
2419
|
+
white: "\x1B[37m",
|
|
2420
|
+
gray: "\x1B[90m"
|
|
2421
|
+
};
|
|
2422
|
+
const reset = "\x1B[0m";
|
|
2423
|
+
return `${colors[color] || ""}${text}${reset}`;
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Export tree as JSON for further analysis
|
|
2427
|
+
*/
|
|
2428
|
+
exportAsJSON(tree) {
|
|
2429
|
+
return JSON.stringify(tree, null, 2);
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Export tree as DOT format for Graphviz
|
|
2433
|
+
*/
|
|
2434
|
+
exportAsDOT(tree) {
|
|
2435
|
+
const lines = ["digraph ComponentTree {"];
|
|
2436
|
+
lines.push(" rankdir=TB;");
|
|
2437
|
+
lines.push(" node [shape=box, style=rounded];");
|
|
2438
|
+
this.generateDOTNodes(tree, lines, "root");
|
|
2439
|
+
lines.push("}");
|
|
2440
|
+
return lines.join("\n");
|
|
2441
|
+
}
|
|
2442
|
+
/**
|
|
2443
|
+
* Generate DOT nodes
|
|
2444
|
+
*/
|
|
2445
|
+
generateDOTNodes(node, lines, parentId) {
|
|
2446
|
+
const nodeId = `${parentId}_${node.name.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
2447
|
+
let label = node.name;
|
|
2448
|
+
if (node.type === "element") {
|
|
2449
|
+
label = `<${node.tagName}>\\n${node.name}`;
|
|
2450
|
+
}
|
|
2451
|
+
lines.push(` "${nodeId}" [label="${label}"];`);
|
|
2452
|
+
if (parentId !== "root") {
|
|
2453
|
+
lines.push(` "${parentId}" -> "${nodeId}";`);
|
|
2454
|
+
}
|
|
2455
|
+
node.children.forEach((child, _index) => {
|
|
2456
|
+
this.generateDOTNodes(child, lines, nodeId);
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
function createComponentVisualizer(options = {}) {
|
|
2461
|
+
return new ComponentVisualizer(options);
|
|
2462
|
+
}
|
|
2463
|
+
function visualizeComponent(component, name = "Root", options = {}) {
|
|
2464
|
+
const visualizer = createComponentVisualizer(options);
|
|
2465
|
+
return visualizer.visualize(component, name);
|
|
2466
|
+
}
|
|
2467
|
+
function logComponentTree(component, name = "Root", options = {}) {
|
|
2468
|
+
const result = visualizeComponent(component, name, options);
|
|
2469
|
+
console.log(result.visualization);
|
|
2470
|
+
return result;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
// src/performance-dashboard.js
|
|
2474
|
+
var PerformanceDashboard = class {
|
|
2475
|
+
constructor(options = {}) {
|
|
2476
|
+
this.options = {
|
|
2477
|
+
updateInterval: options.updateInterval || 5e3,
|
|
2478
|
+
maxHistoryPoints: options.maxHistoryPoints || 100,
|
|
2479
|
+
enableAlerts: options.enableAlerts !== false,
|
|
2480
|
+
enableRecommendations: options.enableRecommendations !== false,
|
|
2481
|
+
colorOutput: options.colorOutput !== false,
|
|
2482
|
+
...options
|
|
2483
|
+
};
|
|
2484
|
+
this.metrics = {
|
|
2485
|
+
api: {
|
|
2486
|
+
requests: 0,
|
|
2487
|
+
averageTime: 0,
|
|
2488
|
+
cacheHits: 0,
|
|
2489
|
+
cacheMisses: 0,
|
|
2490
|
+
staticRoutes: 0,
|
|
2491
|
+
dynamicRoutes: 0,
|
|
2492
|
+
history: []
|
|
2493
|
+
},
|
|
2494
|
+
components: {
|
|
2495
|
+
renders: 0,
|
|
2496
|
+
averageTime: 0,
|
|
2497
|
+
cacheHits: 0,
|
|
2498
|
+
cacheMisses: 0,
|
|
2499
|
+
staticComponents: 0,
|
|
2500
|
+
dynamicComponents: 0,
|
|
2501
|
+
memoryUsage: 0,
|
|
2502
|
+
history: []
|
|
2503
|
+
},
|
|
2504
|
+
fullstack: {
|
|
2505
|
+
totalRequests: 0,
|
|
2506
|
+
averageTime: 0,
|
|
2507
|
+
errors: 0,
|
|
2508
|
+
bottlenecks: [],
|
|
2509
|
+
history: []
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
this.alerts = [];
|
|
2513
|
+
this.recommendations = [];
|
|
2514
|
+
this.startTime = Date.now();
|
|
2515
|
+
this.updateTimer = null;
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Start monitoring performance
|
|
2519
|
+
*/
|
|
2520
|
+
startMonitoring() {
|
|
2521
|
+
if (this.updateTimer) return;
|
|
2522
|
+
this.updateTimer = setInterval(() => {
|
|
2523
|
+
this.updateMetrics();
|
|
2524
|
+
this.generateAlerts();
|
|
2525
|
+
this.generateRecommendations();
|
|
2526
|
+
}, this.options.updateInterval);
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Stop monitoring
|
|
2530
|
+
*/
|
|
2531
|
+
stopMonitoring() {
|
|
2532
|
+
if (this.updateTimer) {
|
|
2533
|
+
clearInterval(this.updateTimer);
|
|
2534
|
+
this.updateTimer = null;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
/**
|
|
2538
|
+
* Record API request metrics
|
|
2539
|
+
*/
|
|
2540
|
+
recordAPIRequest(duration, routeType, cacheHit = false) {
|
|
2541
|
+
this.metrics.api.requests++;
|
|
2542
|
+
this.metrics.api.averageTime = this.updateAverage(
|
|
2543
|
+
this.metrics.api.averageTime,
|
|
2544
|
+
duration,
|
|
2545
|
+
this.metrics.api.requests
|
|
2546
|
+
);
|
|
2547
|
+
if (cacheHit) {
|
|
2548
|
+
this.metrics.api.cacheHits++;
|
|
2549
|
+
} else {
|
|
2550
|
+
this.metrics.api.cacheMisses++;
|
|
2551
|
+
}
|
|
2552
|
+
if (routeType === "static") {
|
|
2553
|
+
this.metrics.api.staticRoutes++;
|
|
2554
|
+
} else {
|
|
2555
|
+
this.metrics.api.dynamicRoutes++;
|
|
2556
|
+
}
|
|
2557
|
+
this.addToHistory("api", {
|
|
2558
|
+
timestamp: Date.now(),
|
|
2559
|
+
duration,
|
|
2560
|
+
routeType,
|
|
2561
|
+
cacheHit
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* Record component render metrics
|
|
2566
|
+
*/
|
|
2567
|
+
recordComponentRender(duration, componentType, cacheHit = false, memoryDelta = 0) {
|
|
2568
|
+
this.metrics.components.renders++;
|
|
2569
|
+
this.metrics.components.averageTime = this.updateAverage(
|
|
2570
|
+
this.metrics.components.averageTime,
|
|
2571
|
+
duration,
|
|
2572
|
+
this.metrics.components.renders
|
|
2573
|
+
);
|
|
2574
|
+
if (cacheHit) {
|
|
2575
|
+
this.metrics.components.cacheHits++;
|
|
2576
|
+
} else {
|
|
2577
|
+
this.metrics.components.cacheMisses++;
|
|
2578
|
+
}
|
|
2579
|
+
if (componentType === "static") {
|
|
2580
|
+
this.metrics.components.staticComponents++;
|
|
2581
|
+
} else {
|
|
2582
|
+
this.metrics.components.dynamicComponents++;
|
|
2583
|
+
}
|
|
2584
|
+
this.metrics.components.memoryUsage += memoryDelta;
|
|
2585
|
+
this.addToHistory("components", {
|
|
2586
|
+
timestamp: Date.now(),
|
|
2587
|
+
duration,
|
|
2588
|
+
componentType,
|
|
2589
|
+
cacheHit,
|
|
2590
|
+
memoryDelta
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Record full-stack request metrics
|
|
2595
|
+
*/
|
|
2596
|
+
recordFullStackRequest(duration, error = null, bottlenecks = []) {
|
|
2597
|
+
this.metrics.fullstack.totalRequests++;
|
|
2598
|
+
this.metrics.fullstack.averageTime = this.updateAverage(
|
|
2599
|
+
this.metrics.fullstack.averageTime,
|
|
2600
|
+
duration,
|
|
2601
|
+
this.metrics.fullstack.totalRequests
|
|
2602
|
+
);
|
|
2603
|
+
if (error) {
|
|
2604
|
+
this.metrics.fullstack.errors++;
|
|
2605
|
+
}
|
|
2606
|
+
this.metrics.fullstack.bottlenecks = bottlenecks;
|
|
2607
|
+
this.addToHistory("fullstack", {
|
|
2608
|
+
timestamp: Date.now(),
|
|
2609
|
+
duration,
|
|
2610
|
+
error,
|
|
2611
|
+
bottlenecks
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* Update metrics from external sources
|
|
2616
|
+
*/
|
|
2617
|
+
updateMetrics() {
|
|
2618
|
+
const now = Date.now();
|
|
2619
|
+
const uptime = now - this.startTime;
|
|
2620
|
+
const apiRate = this.metrics.api.requests / (uptime / 1e3);
|
|
2621
|
+
const componentRate = this.metrics.components.renders / (uptime / 1e3);
|
|
2622
|
+
const fullStackRate = this.metrics.fullstack.totalRequests / (uptime / 1e3);
|
|
2623
|
+
return {
|
|
2624
|
+
apiRate,
|
|
2625
|
+
componentRate,
|
|
2626
|
+
fullStackRate,
|
|
2627
|
+
uptime
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Generate performance alerts
|
|
2632
|
+
*/
|
|
2633
|
+
generateAlerts() {
|
|
2634
|
+
this.alerts = [];
|
|
2635
|
+
if (this.metrics.api.averageTime > 50) {
|
|
2636
|
+
this.alerts.push({
|
|
2637
|
+
type: "warning",
|
|
2638
|
+
category: "api",
|
|
2639
|
+
message: `API response time is high: ${this.metrics.api.averageTime.toFixed(2)}ms`,
|
|
2640
|
+
threshold: 50,
|
|
2641
|
+
current: this.metrics.api.averageTime
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
const apiCacheHitRate = this.getCacheHitRate("api");
|
|
2645
|
+
if (apiCacheHitRate < 80) {
|
|
2646
|
+
this.alerts.push({
|
|
2647
|
+
type: "warning",
|
|
2648
|
+
category: "api",
|
|
2649
|
+
message: `API cache hit rate is low: ${apiCacheHitRate.toFixed(1)}%`,
|
|
2650
|
+
threshold: 80,
|
|
2651
|
+
current: apiCacheHitRate
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
if (this.metrics.components.averageTime > 20) {
|
|
2655
|
+
this.alerts.push({
|
|
2656
|
+
type: "warning",
|
|
2657
|
+
category: "components",
|
|
2658
|
+
message: `Component render time is high: ${this.metrics.components.averageTime.toFixed(2)}ms`,
|
|
2659
|
+
threshold: 20,
|
|
2660
|
+
current: this.metrics.components.averageTime
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
const componentCacheHitRate = this.getCacheHitRate("components");
|
|
2664
|
+
if (componentCacheHitRate < 90) {
|
|
2665
|
+
this.alerts.push({
|
|
2666
|
+
type: "warning",
|
|
2667
|
+
category: "components",
|
|
2668
|
+
message: `Component cache hit rate is low: ${componentCacheHitRate.toFixed(1)}%`,
|
|
2669
|
+
threshold: 90,
|
|
2670
|
+
current: componentCacheHitRate
|
|
2671
|
+
});
|
|
2672
|
+
}
|
|
2673
|
+
if (this.metrics.fullstack.errors > 0) {
|
|
2674
|
+
this.alerts.push({
|
|
2675
|
+
type: "error",
|
|
2676
|
+
category: "fullstack",
|
|
2677
|
+
message: `${this.metrics.fullstack.errors} errors detected`,
|
|
2678
|
+
threshold: 0,
|
|
2679
|
+
current: this.metrics.fullstack.errors
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
/**
|
|
2684
|
+
* Generate performance recommendations
|
|
2685
|
+
*/
|
|
2686
|
+
generateRecommendations() {
|
|
2687
|
+
this.recommendations = [];
|
|
2688
|
+
const staticRouteRatio = this.metrics.api.staticRoutes / Math.max(this.metrics.api.requests, 1);
|
|
2689
|
+
if (staticRouteRatio < 0.7) {
|
|
2690
|
+
this.recommendations.push({
|
|
2691
|
+
type: "optimization",
|
|
2692
|
+
category: "api",
|
|
2693
|
+
message: "Consider adding more static routes to improve smart routing efficiency",
|
|
2694
|
+
impact: "high",
|
|
2695
|
+
effort: "low"
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
const apiCacheHitRate = this.getCacheHitRate("api");
|
|
2699
|
+
if (apiCacheHitRate < 90) {
|
|
2700
|
+
this.recommendations.push({
|
|
2701
|
+
type: "optimization",
|
|
2702
|
+
category: "api",
|
|
2703
|
+
message: "Increase API cache size or TTL to improve cache hit rate",
|
|
2704
|
+
impact: "medium",
|
|
2705
|
+
effort: "low"
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2708
|
+
const staticComponentRatio = this.metrics.components.staticComponents / Math.max(this.metrics.components.renders, 1);
|
|
2709
|
+
if (staticComponentRatio < 0.8) {
|
|
2710
|
+
this.recommendations.push({
|
|
2711
|
+
type: "optimization",
|
|
2712
|
+
category: "components",
|
|
2713
|
+
message: "More components could be optimized as static for better caching",
|
|
2714
|
+
impact: "high",
|
|
2715
|
+
effort: "medium"
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
if (this.metrics.components.memoryUsage > 100 * 1024 * 1024) {
|
|
2719
|
+
this.recommendations.push({
|
|
2720
|
+
type: "optimization",
|
|
2721
|
+
category: "memory",
|
|
2722
|
+
message: "Memory usage is high. Consider reducing cache size or implementing memory cleanup",
|
|
2723
|
+
impact: "medium",
|
|
2724
|
+
effort: "medium"
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Get cache hit rate for category
|
|
2730
|
+
*/
|
|
2731
|
+
getCacheHitRate(category) {
|
|
2732
|
+
const metrics = this.metrics[category];
|
|
2733
|
+
if (!metrics || !metrics.cacheHits) return 0;
|
|
2734
|
+
const total = metrics.cacheHits + metrics.cacheMisses;
|
|
2735
|
+
return total > 0 ? metrics.cacheHits / total * 100 : 0;
|
|
2736
|
+
}
|
|
2737
|
+
/**
|
|
2738
|
+
* Update running average
|
|
2739
|
+
*/
|
|
2740
|
+
updateAverage(current, newValue, count) {
|
|
2741
|
+
return (current * (count - 1) + newValue) / count;
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Add data point to history
|
|
2745
|
+
*/
|
|
2746
|
+
addToHistory(category, data) {
|
|
2747
|
+
if (!this.metrics[category].history) {
|
|
2748
|
+
this.metrics[category].history = [];
|
|
2749
|
+
}
|
|
2750
|
+
this.metrics[category].history.push(data);
|
|
2751
|
+
if (this.metrics[category].history.length > this.options.maxHistoryPoints) {
|
|
2752
|
+
this.metrics[category].history = this.metrics[category].history.slice(-this.options.maxHistoryPoints);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
/**
|
|
2756
|
+
* Generate dashboard visualization
|
|
2757
|
+
*/
|
|
2758
|
+
generateDashboard() {
|
|
2759
|
+
const lines = [];
|
|
2760
|
+
if (this.options.colorOutput) {
|
|
2761
|
+
lines.push(this.colorize("\u{1F4CA} Coherent.js Performance Dashboard", "cyan"));
|
|
2762
|
+
lines.push(this.colorize("\u2550".repeat(50), "cyan"));
|
|
2763
|
+
} else {
|
|
2764
|
+
lines.push("\u{1F4CA} Coherent.js Performance Dashboard");
|
|
2765
|
+
lines.push("\u2550".repeat(50));
|
|
2766
|
+
}
|
|
2767
|
+
const uptime = (Date.now() - this.startTime) / 1e3;
|
|
2768
|
+
lines.push(`\u23F1\uFE0F Uptime: ${uptime.toFixed(1)}s`);
|
|
2769
|
+
lines.push("");
|
|
2770
|
+
lines.push("\u{1F680} API Performance");
|
|
2771
|
+
lines.push("\u2500".repeat(20));
|
|
2772
|
+
const apiCacheHitRate = this.getCacheHitRate("api");
|
|
2773
|
+
lines.push(` Requests: ${this.metrics.api.requests} (${(this.metrics.api.requests / uptime).toFixed(1)} req/s)`);
|
|
2774
|
+
lines.push(` Avg Time: ${this.metrics.api.averageTime.toFixed(2)}ms`);
|
|
2775
|
+
lines.push(` Cache Hit Rate: ${apiCacheHitRate.toFixed(1)}%`);
|
|
2776
|
+
lines.push(` Static Routes: ${this.metrics.api.staticRoutes}/${this.metrics.api.requests} (${(this.metrics.api.staticRoutes / Math.max(this.metrics.api.requests, 1) * 100).toFixed(1)}%)`);
|
|
2777
|
+
lines.push("");
|
|
2778
|
+
lines.push("\u{1F3D7}\uFE0F Component Performance");
|
|
2779
|
+
lines.push("\u2500".repeat(25));
|
|
2780
|
+
const componentCacheHitRate = this.getCacheHitRate("components");
|
|
2781
|
+
lines.push(` Renders: ${this.metrics.components.renders} (${(this.metrics.components.renders / uptime).toFixed(1)} renders/s)`);
|
|
2782
|
+
lines.push(` Avg Time: ${this.metrics.components.averageTime.toFixed(2)}ms`);
|
|
2783
|
+
lines.push(` Cache Hit Rate: ${componentCacheHitRate.toFixed(1)}%`);
|
|
2784
|
+
lines.push(` Static Components: ${this.metrics.components.staticComponents}/${this.metrics.components.renders} (${(this.metrics.components.staticComponents / Math.max(this.metrics.components.renders, 1) * 100).toFixed(1)}%)`);
|
|
2785
|
+
lines.push(` Memory Usage: ${(this.metrics.components.memoryUsage / 1024 / 1024).toFixed(1)}MB`);
|
|
2786
|
+
lines.push("");
|
|
2787
|
+
lines.push("\u{1F310} Full-Stack Performance");
|
|
2788
|
+
lines.push("\u2500".repeat(26));
|
|
2789
|
+
lines.push(` Total Requests: ${this.metrics.fullstack.totalRequests} (${(this.metrics.fullstack.totalRequests / uptime).toFixed(1)} req/s)`);
|
|
2790
|
+
lines.push(` Avg Time: ${this.metrics.fullstack.averageTime.toFixed(2)}ms`);
|
|
2791
|
+
lines.push(` Errors: ${this.metrics.fullstack.errors}`);
|
|
2792
|
+
lines.push("");
|
|
2793
|
+
if (this.alerts.length > 0) {
|
|
2794
|
+
lines.push("\u26A0\uFE0F Performance Alerts");
|
|
2795
|
+
lines.push("\u2500".repeat(22));
|
|
2796
|
+
this.alerts.forEach((alert) => {
|
|
2797
|
+
const icon = alert.type === "error" ? "\u274C" : "\u26A0\uFE0F";
|
|
2798
|
+
lines.push(` ${icon} ${alert.message}`);
|
|
2799
|
+
});
|
|
2800
|
+
lines.push("");
|
|
2801
|
+
}
|
|
2802
|
+
if (this.recommendations.length > 0) {
|
|
2803
|
+
lines.push("\u{1F4A1} Optimization Recommendations");
|
|
2804
|
+
lines.push("\u2500".repeat(30));
|
|
2805
|
+
this.recommendations.forEach((rec) => {
|
|
2806
|
+
const impact = rec.impact === "high" ? "\u{1F525}" : rec.impact === "medium" ? "\u26A1" : "\u{1F4A4}";
|
|
2807
|
+
lines.push(` ${impact} ${rec.message} (${rec.effort} effort)`);
|
|
2808
|
+
});
|
|
2809
|
+
lines.push("");
|
|
2810
|
+
}
|
|
2811
|
+
const score = this.calculatePerformanceScore();
|
|
2812
|
+
const scoreColor = score >= 90 ? "green" : score >= 70 ? "yellow" : "red";
|
|
2813
|
+
lines.push(`Performance Score: ${this.colorize(`${score.toFixed(1)}/100`, scoreColor)}`);
|
|
2814
|
+
return lines.join("\n");
|
|
2815
|
+
}
|
|
2816
|
+
/**
|
|
2817
|
+
* Calculate overall performance score
|
|
2818
|
+
*/
|
|
2819
|
+
calculatePerformanceScore() {
|
|
2820
|
+
let score = 100;
|
|
2821
|
+
if (this.metrics.api.averageTime > 50) score -= 10;
|
|
2822
|
+
if (this.metrics.api.averageTime > 100) score -= 10;
|
|
2823
|
+
if (this.getCacheHitRate("api") < 90) score -= 10;
|
|
2824
|
+
if (this.metrics.components.averageTime > 20) score -= 10;
|
|
2825
|
+
if (this.metrics.components.averageTime > 50) score -= 10;
|
|
2826
|
+
if (this.getCacheHitRate("components") < 95) score -= 10;
|
|
2827
|
+
if (this.metrics.fullstack.errors > 0) score -= Math.min(20, this.metrics.fullstack.errors * 5);
|
|
2828
|
+
return Math.max(0, score);
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Add color to text
|
|
2832
|
+
*/
|
|
2833
|
+
colorize(text, color) {
|
|
2834
|
+
if (!this.options.colorOutput) return text;
|
|
2835
|
+
const colors = {
|
|
2836
|
+
black: "\x1B[30m",
|
|
2837
|
+
red: "\x1B[31m",
|
|
2838
|
+
green: "\x1B[32m",
|
|
2839
|
+
yellow: "\x1B[33m",
|
|
2840
|
+
blue: "\x1B[34m",
|
|
2841
|
+
magenta: "\x1B[35m",
|
|
2842
|
+
cyan: "\x1B[36m",
|
|
2843
|
+
white: "\x1B[37m",
|
|
2844
|
+
gray: "\x1B[90m"
|
|
2845
|
+
};
|
|
2846
|
+
const reset = "\x1B[0m";
|
|
2847
|
+
return `${colors[color] || ""}${text}${reset}`;
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2850
|
+
* Export metrics as JSON
|
|
2851
|
+
*/
|
|
2852
|
+
exportMetrics() {
|
|
2853
|
+
return {
|
|
2854
|
+
timestamp: Date.now(),
|
|
2855
|
+
uptime: Date.now() - this.startTime,
|
|
2856
|
+
metrics: { ...this.metrics },
|
|
2857
|
+
alerts: [...this.alerts],
|
|
2858
|
+
recommendations: [...this.recommendations],
|
|
2859
|
+
performanceScore: this.calculatePerformanceScore()
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Reset all metrics
|
|
2864
|
+
*/
|
|
2865
|
+
reset() {
|
|
2866
|
+
this.metrics = {
|
|
2867
|
+
api: { requests: 0, averageTime: 0, cacheHits: 0, cacheMisses: 0, staticRoutes: 0, dynamicRoutes: 0, history: [] },
|
|
2868
|
+
components: { renders: 0, averageTime: 0, cacheHits: 0, cacheMisses: 0, staticComponents: 0, dynamicComponents: 0, memoryUsage: 0, history: [] },
|
|
2869
|
+
fullstack: { totalRequests: 0, averageTime: 0, errors: 0, bottlenecks: [], history: [] }
|
|
2870
|
+
};
|
|
2871
|
+
this.alerts = [];
|
|
2872
|
+
this.recommendations = [];
|
|
2873
|
+
this.startTime = Date.now();
|
|
2874
|
+
}
|
|
2098
2875
|
};
|
|
2876
|
+
function createPerformanceDashboard(options = {}) {
|
|
2877
|
+
return new PerformanceDashboard(options);
|
|
2878
|
+
}
|
|
2879
|
+
function showPerformanceDashboard(dashboard) {
|
|
2880
|
+
const output = dashboard.generateDashboard();
|
|
2881
|
+
console.log(output);
|
|
2882
|
+
return dashboard;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// src/enhanced-errors.js
|
|
2886
|
+
var EnhancedErrorHandler = class {
|
|
2887
|
+
constructor(options = {}) {
|
|
2888
|
+
this.options = {
|
|
2889
|
+
maxContextDepth: options.maxContextDepth || 5,
|
|
2890
|
+
includeStackTrace: options.includeStackTrace !== false,
|
|
2891
|
+
showSuggestions: options.showSuggestions !== false,
|
|
2892
|
+
colorOutput: options.colorOutput !== false,
|
|
2893
|
+
...options
|
|
2894
|
+
};
|
|
2895
|
+
this.errorHistory = [];
|
|
2896
|
+
this.commonPatterns = this.initializeCommonPatterns();
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* Handle and enhance an error with component context
|
|
2900
|
+
*/
|
|
2901
|
+
handleError(error, component = null, context = {}) {
|
|
2902
|
+
const enhancedError = {
|
|
2903
|
+
originalError: error,
|
|
2904
|
+
message: error.message,
|
|
2905
|
+
stack: error.stack,
|
|
2906
|
+
timestamp: Date.now(),
|
|
2907
|
+
component: component ? this.analyzeComponent(component) : null,
|
|
2908
|
+
context,
|
|
2909
|
+
suggestions: [],
|
|
2910
|
+
severity: this.determineSeverity(error),
|
|
2911
|
+
category: this.categorizeError(error)
|
|
2912
|
+
};
|
|
2913
|
+
if (component) {
|
|
2914
|
+
enhancedError.componentContext = this.getComponentContext(component, context.path || []);
|
|
2915
|
+
enhancedError.propValidation = this.validateProps(component);
|
|
2916
|
+
}
|
|
2917
|
+
if (this.options.showSuggestions) {
|
|
2918
|
+
enhancedError.suggestions = this.generateSuggestions(enhancedError);
|
|
2919
|
+
}
|
|
2920
|
+
this.errorHistory.push(enhancedError);
|
|
2921
|
+
if (this.errorHistory.length > 100) {
|
|
2922
|
+
this.errorHistory = this.errorHistory.slice(-100);
|
|
2923
|
+
}
|
|
2924
|
+
return enhancedError;
|
|
2925
|
+
}
|
|
2926
|
+
/**
|
|
2927
|
+
* Analyze component structure
|
|
2928
|
+
*/
|
|
2929
|
+
analyzeComponent(component) {
|
|
2930
|
+
const analysis = {
|
|
2931
|
+
type: this.getComponentType(component),
|
|
2932
|
+
isValid: this.isValidComponent(component),
|
|
2933
|
+
complexity: this.assessComplexity(component),
|
|
2934
|
+
hasDynamicContent: this.hasDynamicContent(component),
|
|
2935
|
+
estimatedSize: this.estimateSize(component)
|
|
2936
|
+
};
|
|
2937
|
+
if (isCoherentObject2(component)) {
|
|
2938
|
+
const entries = Object.entries(component);
|
|
2939
|
+
if (entries.length === 1) {
|
|
2940
|
+
const [_tagName, props] = entries;
|
|
2941
|
+
analysis.tagName = _tagName;
|
|
2942
|
+
analysis.propCount = Object.keys(props).length;
|
|
2943
|
+
analysis.hasChildren = hasChildren(props);
|
|
2944
|
+
analysis.eventHandlers = this.extractEventHandlers(props);
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
return analysis;
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Get component context tree
|
|
2951
|
+
*/
|
|
2952
|
+
getComponentContext(component, path2 = []) {
|
|
2953
|
+
const context = {
|
|
2954
|
+
path: path2.join("."),
|
|
2955
|
+
depth: path2.length,
|
|
2956
|
+
component: this.summarizeComponent(component),
|
|
2957
|
+
children: []
|
|
2958
|
+
};
|
|
2959
|
+
if (isCoherentObject2(component) && context.depth < this.options.maxContextDepth) {
|
|
2960
|
+
const entries = Object.entries(component);
|
|
2961
|
+
if (entries.length === 1) {
|
|
2962
|
+
const [tagName, props] = entries;
|
|
2963
|
+
if (hasChildren(props)) {
|
|
2964
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
2965
|
+
children.forEach((child, index) => {
|
|
2966
|
+
if (child && typeof child === "object") {
|
|
2967
|
+
const childContext = this.getComponentContext(child, [...path2, `${tagName}[${index}]`]);
|
|
2968
|
+
context.children.push(childContext);
|
|
2969
|
+
}
|
|
2970
|
+
});
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
return context;
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Validate component props
|
|
2978
|
+
*/
|
|
2979
|
+
validateProps(component) {
|
|
2980
|
+
if (!isCoherentObject2(component)) return { valid: true, issues: [] };
|
|
2981
|
+
const entries = Object.entries(component);
|
|
2982
|
+
if (entries.length !== 1) return { valid: false, issues: ["Component must have exactly one root element"] };
|
|
2983
|
+
const [tagName, props] = entries;
|
|
2984
|
+
const issues = [];
|
|
2985
|
+
const warnings = [];
|
|
2986
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
2987
|
+
if (value === void 0) {
|
|
2988
|
+
issues.push(`Prop '${key}' is undefined`);
|
|
2989
|
+
}
|
|
2990
|
+
if (value === null && key !== "children" && key !== "text") {
|
|
2991
|
+
warnings.push(`Prop '${key}' is null`);
|
|
2992
|
+
}
|
|
2993
|
+
if (typeof value === "function" && !/^on[A-Z]/.test(key)) {
|
|
2994
|
+
warnings.push(`Function prop '${key}' doesn't follow event handler naming convention (onXxx)`);
|
|
2995
|
+
}
|
|
2996
|
+
if (typeof value === "object" && value !== null) {
|
|
2997
|
+
const size = JSON.stringify(value).length;
|
|
2998
|
+
if (size > 1e4) {
|
|
2999
|
+
warnings.push(`Prop '${key}' is large (${size} bytes) - consider optimizing`);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
if (tagName === "img" && !props.src && !props["data-src"]) {
|
|
3004
|
+
issues.push("Image element missing required src or data-src prop");
|
|
3005
|
+
}
|
|
3006
|
+
if (tagName === "a" && !props.href && !props.onclick) {
|
|
3007
|
+
warnings.push("Link element missing href or onclick prop");
|
|
3008
|
+
}
|
|
3009
|
+
return {
|
|
3010
|
+
valid: issues.length === 0,
|
|
3011
|
+
issues,
|
|
3012
|
+
warnings,
|
|
3013
|
+
propCount: Object.keys(props).length
|
|
3014
|
+
};
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Generate fix suggestions based on error and context
|
|
3018
|
+
*/
|
|
3019
|
+
generateSuggestions(enhancedError) {
|
|
3020
|
+
const suggestions = [];
|
|
3021
|
+
const { originalError, component, category } = enhancedError;
|
|
3022
|
+
if (component && component.type === "element") {
|
|
3023
|
+
if (component.hasDynamicContent && category === "performance") {
|
|
3024
|
+
suggestions.push({
|
|
3025
|
+
type: "optimization",
|
|
3026
|
+
message: "Consider making this component static for better caching",
|
|
3027
|
+
code: "Remove functions from props to enable static optimization"
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
if (component.complexity > 10) {
|
|
3031
|
+
suggestions.push({
|
|
3032
|
+
type: "structure",
|
|
3033
|
+
message: "Component is complex - consider breaking it into smaller components",
|
|
3034
|
+
code: "Split complex components into reusable functional components"
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
if (originalError && originalError.message && originalError.message.includes("undefined")) {
|
|
3039
|
+
suggestions.push({
|
|
3040
|
+
type: "fix",
|
|
3041
|
+
message: "Check for undefined props or missing data",
|
|
3042
|
+
code: "Add prop validation: if (!props.required) return null;"
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
if (originalError && originalError.message && originalError.message.includes("Maximum render depth")) {
|
|
3046
|
+
suggestions.push({
|
|
3047
|
+
type: "fix",
|
|
3048
|
+
message: "Possible infinite recursion detected",
|
|
3049
|
+
code: "Check for circular references in component props"
|
|
3050
|
+
});
|
|
3051
|
+
}
|
|
3052
|
+
this.commonPatterns.forEach((pattern) => {
|
|
3053
|
+
if (originalError && originalError.message && pattern.matcher.test(originalError.message)) {
|
|
3054
|
+
suggestions.push(pattern.suggestion);
|
|
3055
|
+
}
|
|
3056
|
+
});
|
|
3057
|
+
return suggestions;
|
|
3058
|
+
}
|
|
3059
|
+
/**
|
|
3060
|
+
* Format enhanced error for display
|
|
3061
|
+
*/
|
|
3062
|
+
formatError(enhancedError) {
|
|
3063
|
+
const lines = [];
|
|
3064
|
+
if (this.options.colorOutput) {
|
|
3065
|
+
lines.push(this.colorize("\u274C Coherent.js Error", "red"));
|
|
3066
|
+
lines.push(this.colorize("\u2500".repeat(40), "red"));
|
|
3067
|
+
} else {
|
|
3068
|
+
lines.push("\u274C Coherent.js Error");
|
|
3069
|
+
lines.push("\u2500".repeat(40));
|
|
3070
|
+
}
|
|
3071
|
+
lines.push(`Message: ${enhancedError.message}`);
|
|
3072
|
+
lines.push(`Category: ${enhancedError.category} (${enhancedError.severity})`);
|
|
3073
|
+
lines.push(`Time: ${new Date(enhancedError.timestamp).toLocaleTimeString()}`);
|
|
3074
|
+
lines.push("");
|
|
3075
|
+
if (enhancedError.componentContext) {
|
|
3076
|
+
lines.push("\u{1F3D7}\uFE0F Component Context");
|
|
3077
|
+
lines.push("\u2500".repeat(20));
|
|
3078
|
+
lines.push(`Path: ${enhancedError.componentContext.path}`);
|
|
3079
|
+
lines.push(`Type: ${enhancedError.component.type}`);
|
|
3080
|
+
lines.push(`Depth: ${enhancedError.componentContext.depth}`);
|
|
3081
|
+
if (enhancedError.componentContext.component) {
|
|
3082
|
+
lines.push(`Summary: ${enhancedError.componentContext.component}`);
|
|
3083
|
+
}
|
|
3084
|
+
lines.push("");
|
|
3085
|
+
}
|
|
3086
|
+
if (enhancedError.propValidation) {
|
|
3087
|
+
const validation = enhancedError.propValidation;
|
|
3088
|
+
lines.push("\u{1F4DD} Prop Validation");
|
|
3089
|
+
lines.push("\u2500".repeat(18));
|
|
3090
|
+
lines.push(`Valid: ${validation.valid ? "\u2705" : "\u274C"}`);
|
|
3091
|
+
lines.push(`Props: ${validation.propCount}`);
|
|
3092
|
+
if (validation.issues.length > 0) {
|
|
3093
|
+
lines.push("Issues:");
|
|
3094
|
+
validation.issues.forEach((issue) => {
|
|
3095
|
+
lines.push(` \u274C ${issue}`);
|
|
3096
|
+
});
|
|
3097
|
+
}
|
|
3098
|
+
if (validation.warnings.length > 0) {
|
|
3099
|
+
lines.push("Warnings:");
|
|
3100
|
+
validation.warnings.forEach((warning) => {
|
|
3101
|
+
lines.push(` \u26A0\uFE0F ${warning}`);
|
|
3102
|
+
});
|
|
3103
|
+
}
|
|
3104
|
+
lines.push("");
|
|
3105
|
+
}
|
|
3106
|
+
if (enhancedError.suggestions.length > 0) {
|
|
3107
|
+
lines.push("\u{1F4A1} Suggestions");
|
|
3108
|
+
lines.push("\u2500".repeat(13));
|
|
3109
|
+
enhancedError.suggestions.forEach((suggestion, index) => {
|
|
3110
|
+
const icon = suggestion.type === "fix" ? "\u{1F527}" : suggestion.type === "optimization" ? "\u26A1" : suggestion.type === "structure" ? "\u{1F3D7}\uFE0F" : "\u{1F4A1}";
|
|
3111
|
+
lines.push(`${index + 1}. ${icon} ${suggestion.message}`);
|
|
3112
|
+
if (suggestion.code) {
|
|
3113
|
+
lines.push(` Code: ${suggestion.code}`);
|
|
3114
|
+
}
|
|
3115
|
+
});
|
|
3116
|
+
lines.push("");
|
|
3117
|
+
}
|
|
3118
|
+
if (this.options.includeStackTrace && enhancedError.stack) {
|
|
3119
|
+
lines.push("\u{1F4DA} Stack Trace");
|
|
3120
|
+
lines.push("\u2500".repeat(15));
|
|
3121
|
+
lines.push(enhancedError.stack.split("\n").slice(0, 10).join("\n"));
|
|
3122
|
+
if (enhancedError.stack.split("\n").length > 10) {
|
|
3123
|
+
lines.push("... (truncated)");
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
return lines.join("\n");
|
|
3127
|
+
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Get component type
|
|
3130
|
+
*/
|
|
3131
|
+
getComponentType(component) {
|
|
3132
|
+
if (component === null || component === void 0) return "empty";
|
|
3133
|
+
if (typeof component === "string") return "text";
|
|
3134
|
+
if (typeof component === "number") return "number";
|
|
3135
|
+
if (typeof component === "boolean") return "boolean";
|
|
3136
|
+
if (typeof component === "function") return "function";
|
|
3137
|
+
if (Array.isArray(component)) return "array";
|
|
3138
|
+
if (isCoherentObject2(component)) return "element";
|
|
3139
|
+
return "object";
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* Check if component is valid
|
|
3143
|
+
*/
|
|
3144
|
+
isValidComponent(component) {
|
|
3145
|
+
try {
|
|
3146
|
+
if (component === null || component === void 0) return true;
|
|
3147
|
+
if (typeof component === "string" || typeof component === "number") return true;
|
|
3148
|
+
if (typeof component === "function") return true;
|
|
3149
|
+
if (Array.isArray(component)) return component.every((child) => this.isValidComponent(child));
|
|
3150
|
+
if (isCoherentObject2(component)) {
|
|
3151
|
+
const entries = Object.entries(component);
|
|
3152
|
+
return entries.length === 1;
|
|
3153
|
+
}
|
|
3154
|
+
return false;
|
|
3155
|
+
} catch {
|
|
3156
|
+
return false;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
/**
|
|
3160
|
+
* Assess component complexity
|
|
3161
|
+
*/
|
|
3162
|
+
assessComplexity(component) {
|
|
3163
|
+
let complexity = 0;
|
|
3164
|
+
if (typeof component === "object" && component !== null) {
|
|
3165
|
+
if (isCoherentObject2(component)) {
|
|
3166
|
+
const entries = Object.entries(component);
|
|
3167
|
+
if (entries.length === 1) {
|
|
3168
|
+
const [_tagName, props] = entries;
|
|
3169
|
+
complexity += Object.keys(props).length;
|
|
3170
|
+
if (hasChildren(props)) {
|
|
3171
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
3172
|
+
children.forEach((child) => {
|
|
3173
|
+
complexity += this.assessComplexity(child);
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
} else {
|
|
3178
|
+
complexity += Object.keys(component).length;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
return complexity;
|
|
3182
|
+
}
|
|
3183
|
+
/**
|
|
3184
|
+
* Check if component has dynamic content
|
|
3185
|
+
*/
|
|
3186
|
+
hasDynamicContent(component) {
|
|
3187
|
+
if (typeof component === "function") return true;
|
|
3188
|
+
if (typeof component === "object" && component !== null) {
|
|
3189
|
+
for (const value of Object.values(component)) {
|
|
3190
|
+
if (typeof value === "function") return true;
|
|
3191
|
+
if (typeof value === "object" && this.hasDynamicContent(value)) return true;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
return false;
|
|
3195
|
+
}
|
|
3196
|
+
/**
|
|
3197
|
+
* Estimate component size
|
|
3198
|
+
*/
|
|
3199
|
+
estimateSize(component) {
|
|
3200
|
+
try {
|
|
3201
|
+
return JSON.stringify(component).length;
|
|
3202
|
+
} catch {
|
|
3203
|
+
return 0;
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Summarize component for context
|
|
3208
|
+
*/
|
|
3209
|
+
summarizeComponent(component) {
|
|
3210
|
+
const type = this.getComponentType(component);
|
|
3211
|
+
if (type === "element" && isCoherentObject2(component)) {
|
|
3212
|
+
const entries = Object.entries(component);
|
|
3213
|
+
if (entries.length === 1) {
|
|
3214
|
+
const [tagName, props] = entries;
|
|
3215
|
+
const propCount = Object.keys(props).length;
|
|
3216
|
+
const hasChildren2 = hasChildren2(props);
|
|
3217
|
+
return `<${tagName}> (${propCount} props, ${hasChildren2 ? "has" : "no"} children)`;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
if (type === "text") {
|
|
3221
|
+
const preview = String(component).substring(0, 30);
|
|
3222
|
+
return `Text: "${preview}${component.length > 30 ? "..." : ""}"`;
|
|
3223
|
+
}
|
|
3224
|
+
if (type === "function") {
|
|
3225
|
+
return `Function: ${component.name || "anonymous"}`;
|
|
3226
|
+
}
|
|
3227
|
+
return `${type} (${this.estimateSize(component)} bytes)`;
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Extract event handlers from props
|
|
3231
|
+
*/
|
|
3232
|
+
extractEventHandlers(props) {
|
|
3233
|
+
return Object.keys(props).filter((key) => /^on[A-Z]/.test(key));
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Determine error severity
|
|
3237
|
+
*/
|
|
3238
|
+
determineSeverity(error) {
|
|
3239
|
+
if (error.name === "TypeError" || error.name === "ReferenceError") return "critical";
|
|
3240
|
+
if (error.message.includes("Maximum render depth")) return "critical";
|
|
3241
|
+
if (error.message.includes("undefined")) return "high";
|
|
3242
|
+
if (error.message.includes("performance")) return "medium";
|
|
3243
|
+
return "low";
|
|
3244
|
+
}
|
|
3245
|
+
/**
|
|
3246
|
+
* Categorize error
|
|
3247
|
+
*/
|
|
3248
|
+
categorizeError(error) {
|
|
3249
|
+
if (error.message.includes("render") || error.message.includes("component")) return "rendering";
|
|
3250
|
+
if (error.message.includes("props") || error.message.includes("prop")) return "props";
|
|
3251
|
+
if (error.message.includes("cache") || error.message.includes("performance")) return "performance";
|
|
3252
|
+
if (error.message.includes("route") || error.message.includes("router")) return "routing";
|
|
3253
|
+
return "general";
|
|
3254
|
+
}
|
|
3255
|
+
/**
|
|
3256
|
+
* Initialize common error patterns and suggestions
|
|
3257
|
+
*/
|
|
3258
|
+
initializeCommonPatterns() {
|
|
3259
|
+
return [
|
|
3260
|
+
{
|
|
3261
|
+
matcher: /undefined.*property/gi,
|
|
3262
|
+
suggestion: {
|
|
3263
|
+
type: "fix",
|
|
3264
|
+
message: "Check for undefined properties in component props",
|
|
3265
|
+
code: 'Add default props: const { required = "default" } = props;'
|
|
3266
|
+
}
|
|
3267
|
+
},
|
|
3268
|
+
{
|
|
3269
|
+
matcher: /maximum.*depth/gi,
|
|
3270
|
+
suggestion: {
|
|
3271
|
+
type: "fix",
|
|
3272
|
+
message: "Infinite recursion detected in component tree",
|
|
3273
|
+
code: "Check for circular references in component children"
|
|
3274
|
+
}
|
|
3275
|
+
},
|
|
3276
|
+
{
|
|
3277
|
+
matcher: /cannot.*read.*property/gi,
|
|
3278
|
+
suggestion: {
|
|
3279
|
+
type: "fix",
|
|
3280
|
+
message: "Property access error - check object structure",
|
|
3281
|
+
code: "Use optional chaining: obj?.prop?.nested"
|
|
3282
|
+
}
|
|
3283
|
+
},
|
|
3284
|
+
{
|
|
3285
|
+
matcher: /performance/gi,
|
|
3286
|
+
suggestion: {
|
|
3287
|
+
type: "optimization",
|
|
3288
|
+
message: "Consider optimizing component for better performance",
|
|
3289
|
+
code: "Use memoization or static components where possible"
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
];
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* Add color to text
|
|
3296
|
+
*/
|
|
3297
|
+
colorize(text, color) {
|
|
3298
|
+
if (!this.options.colorOutput) return text;
|
|
3299
|
+
const colors = {
|
|
3300
|
+
black: "\x1B[30m",
|
|
3301
|
+
red: "\x1B[31m",
|
|
3302
|
+
green: "\x1B[32m",
|
|
3303
|
+
yellow: "\x1B[33m",
|
|
3304
|
+
blue: "\x1B[34m",
|
|
3305
|
+
magenta: "\x1B[35m",
|
|
3306
|
+
cyan: "\x1B[36m",
|
|
3307
|
+
white: "\x1B[37m",
|
|
3308
|
+
gray: "\x1B[90m"
|
|
3309
|
+
};
|
|
3310
|
+
const reset = "\x1B[0m";
|
|
3311
|
+
return `${colors[color] || ""}${text}${reset}`;
|
|
3312
|
+
}
|
|
3313
|
+
/**
|
|
3314
|
+
* Get error statistics
|
|
3315
|
+
*/
|
|
3316
|
+
getErrorStats() {
|
|
3317
|
+
const stats = {
|
|
3318
|
+
total: this.errorHistory.length,
|
|
3319
|
+
byCategory: {},
|
|
3320
|
+
bySeverity: {},
|
|
3321
|
+
recent: this.errorHistory.slice(-10)
|
|
3322
|
+
};
|
|
3323
|
+
this.errorHistory.forEach((error) => {
|
|
3324
|
+
stats.byCategory[error.category] = (stats.byCategory[error.category] || 0) + 1;
|
|
3325
|
+
stats.bySeverity[error.severity] = (stats.bySeverity[error.severity] || 0) + 1;
|
|
3326
|
+
});
|
|
3327
|
+
return stats;
|
|
3328
|
+
}
|
|
3329
|
+
};
|
|
3330
|
+
function createEnhancedErrorHandler(options = {}) {
|
|
3331
|
+
return new EnhancedErrorHandler(options);
|
|
3332
|
+
}
|
|
3333
|
+
function handleEnhancedError(error, component = null, context = {}) {
|
|
3334
|
+
const handler = createEnhancedErrorHandler();
|
|
3335
|
+
const enhancedError = handler.handleError(error, component, context);
|
|
3336
|
+
console.error(handler.formatError(enhancedError));
|
|
3337
|
+
return enhancedError;
|
|
3338
|
+
}
|
|
2099
3339
|
export {
|
|
2100
3340
|
ComponentInspector,
|
|
3341
|
+
ComponentVisualizer,
|
|
2101
3342
|
DevLogger,
|
|
2102
3343
|
DevTools,
|
|
3344
|
+
EnhancedErrorHandler,
|
|
2103
3345
|
LogLevel,
|
|
3346
|
+
PerformanceDashboard,
|
|
2104
3347
|
PerformanceProfiler,
|
|
2105
3348
|
createComponentLogger,
|
|
3349
|
+
createComponentVisualizer,
|
|
2106
3350
|
createConsoleLogger,
|
|
2107
3351
|
createDevTools,
|
|
3352
|
+
createEnhancedErrorHandler,
|
|
2108
3353
|
createInspector,
|
|
2109
3354
|
createLogger,
|
|
3355
|
+
createPerformanceDashboard,
|
|
2110
3356
|
createProfiler,
|
|
2111
|
-
|
|
3357
|
+
handleEnhancedError,
|
|
2112
3358
|
inspect,
|
|
3359
|
+
logComponentTree,
|
|
2113
3360
|
measure,
|
|
2114
3361
|
profile,
|
|
2115
|
-
|
|
3362
|
+
showPerformanceDashboard,
|
|
3363
|
+
validateComponent,
|
|
3364
|
+
visualizeComponent
|
|
2116
3365
|
};
|
|
2117
3366
|
//# sourceMappingURL=index.js.map
|