@coherent.js/devtools 1.0.0-rc.6 → 1.0.1
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/package.json +4 -3
- package/types/index.d.ts +579 -360
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coherent.js/devtools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Developer tools for Coherent.js applications - tree-shakable modular exports",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"author": "Coherent.js Team",
|
|
63
63
|
"license": "MIT",
|
|
64
64
|
"peerDependencies": {
|
|
65
|
-
"@coherent.js/core": "1.0.
|
|
65
|
+
"@coherent.js/core": "1.0.1"
|
|
66
66
|
},
|
|
67
67
|
"repository": {
|
|
68
68
|
"type": "git",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"build": "node build.mjs",
|
|
84
84
|
"clean": "rm -rf dist",
|
|
85
85
|
"test": "vitest run",
|
|
86
|
-
"test:watch": "vitest"
|
|
86
|
+
"test:watch": "vitest",
|
|
87
|
+
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit"
|
|
87
88
|
}
|
|
88
89
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -3,540 +3,759 @@
|
|
|
3
3
|
* @module @coherent.js/devtools
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { CoherentNode, ComponentInstance, ComponentProps } from '@coherent.js/core';
|
|
7
|
-
|
|
8
6
|
// ============================================================================
|
|
9
|
-
// Logger
|
|
7
|
+
// Logger
|
|
10
8
|
// ============================================================================
|
|
11
9
|
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
10
|
+
/** Severity levels, ordered from most to least verbose. */
|
|
11
|
+
export const LogLevel: {
|
|
12
|
+
readonly TRACE: 0;
|
|
13
|
+
readonly DEBUG: 1;
|
|
14
|
+
readonly INFO: 2;
|
|
15
|
+
readonly WARN: 3;
|
|
16
|
+
readonly ERROR: 4;
|
|
17
|
+
readonly FATAL: 5;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** One of the {@link LogLevel} values. */
|
|
21
|
+
export type LogLevelValue = 0 | 1 | 2 | 3 | 4 | 5;
|
|
23
22
|
|
|
24
|
-
/**
|
|
25
|
-
* Logger configuration options
|
|
26
|
-
*/
|
|
27
23
|
export interface LoggerOptions {
|
|
28
|
-
/** Minimum
|
|
29
|
-
level?:
|
|
30
|
-
/** Prefix
|
|
24
|
+
/** Minimum level to emit; defaults to `LogLevel.INFO` */
|
|
25
|
+
level?: LogLevelValue;
|
|
26
|
+
/** Prefix on every line; defaults to `'[Coherent]'` */
|
|
31
27
|
prefix?: string;
|
|
32
|
-
/** Include timestamp
|
|
28
|
+
/** Include a timestamp; defaults to `true` */
|
|
33
29
|
timestamp?: boolean;
|
|
34
|
-
/**
|
|
30
|
+
/** Colorize output; defaults to `true` */
|
|
35
31
|
colors?: boolean;
|
|
36
|
-
/**
|
|
32
|
+
/** Cap on retained entries; defaults to `1000` */
|
|
37
33
|
maxLogs?: number;
|
|
38
|
-
/**
|
|
34
|
+
/** Cap on buffered entries; defaults to `1000` */
|
|
39
35
|
maxBufferSize?: number;
|
|
40
|
-
/**
|
|
36
|
+
/** Allow `group()`/`groupEnd()`; defaults to `true` */
|
|
41
37
|
grouping?: boolean;
|
|
42
|
-
/** Buffer
|
|
38
|
+
/** Buffer instead of writing immediately; defaults to `false` */
|
|
43
39
|
buffer?: boolean;
|
|
44
|
-
/**
|
|
40
|
+
/** Fraction of entries to keep, 0 to 1; defaults to `1.0` */
|
|
45
41
|
sampleRate?: number;
|
|
46
|
-
/**
|
|
42
|
+
/** Record without writing anywhere; defaults to `false` */
|
|
47
43
|
silent?: boolean;
|
|
48
|
-
/**
|
|
49
|
-
output?: ((
|
|
50
|
-
/**
|
|
44
|
+
/** Write here instead of the console */
|
|
45
|
+
output?: ((entry: LogEntry) => void) | null;
|
|
46
|
+
/** Only emit these categories */
|
|
51
47
|
categories?: string[] | null;
|
|
52
|
-
/**
|
|
53
|
-
filter?: ((
|
|
48
|
+
/** Drop entries this rejects */
|
|
49
|
+
filter?: ((entry: LogEntry) => boolean) | null;
|
|
50
|
+
[option: string]: unknown;
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
/**
|
|
57
|
-
* Log entry structure
|
|
58
|
-
*/
|
|
59
53
|
export interface LogEntry {
|
|
60
|
-
|
|
61
|
-
level:
|
|
62
|
-
|
|
54
|
+
id: string;
|
|
55
|
+
level: LogLevelValue;
|
|
56
|
+
levelName: string;
|
|
63
57
|
message: string;
|
|
64
|
-
|
|
58
|
+
data: Record<string, unknown>;
|
|
65
59
|
timestamp: number;
|
|
66
|
-
/** Additional data */
|
|
67
|
-
data?: unknown[];
|
|
68
|
-
/** Log category */
|
|
69
60
|
category?: string;
|
|
70
|
-
|
|
61
|
+
group?: string;
|
|
71
62
|
context?: Record<string, unknown>;
|
|
72
63
|
}
|
|
73
64
|
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
/** Filter applied to entries before they are emitted. */
|
|
66
|
+
export interface LogFilter {
|
|
67
|
+
(entry: LogEntry): boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface LogQuery {
|
|
71
|
+
level?: LogLevelValue;
|
|
72
|
+
category?: string;
|
|
73
|
+
/** Substring match against the message */
|
|
74
|
+
search?: string;
|
|
75
|
+
since?: number;
|
|
76
|
+
limit?: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Structured logger with levels, filters, grouping and buffering. */
|
|
77
80
|
export class DevLogger {
|
|
78
81
|
constructor(options?: LoggerOptions);
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
options: LoggerOptions;
|
|
84
|
+
logs: LogEntry[];
|
|
85
|
+
filters: LogFilter[];
|
|
86
|
+
handlers: Array<(entry: LogEntry) => void>;
|
|
87
|
+
context: Record<string, unknown>;
|
|
85
88
|
|
|
86
|
-
/**
|
|
87
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Log at an explicit level, or under a category when the first argument is
|
|
91
|
+
* a category name.
|
|
92
|
+
*/
|
|
93
|
+
log(categoryOrLevel: string | LogLevelValue, messageOrData: string | Record<string, unknown>, data?: Record<string, unknown>): void;
|
|
88
94
|
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
trace(message: string, data?: Record<string, unknown>): void;
|
|
96
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
97
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
98
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
99
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
100
|
+
fatal(message: string, data?: Record<string, unknown>): void;
|
|
91
101
|
|
|
92
|
-
/** Log at
|
|
93
|
-
|
|
102
|
+
/** Log at an explicit level */
|
|
103
|
+
logWithLevel(level: LogLevelValue, message: string, data?: Record<string, unknown>): void;
|
|
94
104
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
105
|
+
/** Whether an entry passes the level, sampling and filters */
|
|
106
|
+
shouldLog(level: LogLevelValue, message: string, data?: Record<string, unknown>): boolean;
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
addFilter(filter: LogFilter): void;
|
|
109
|
+
removeFilter(filter: LogFilter): void;
|
|
100
110
|
|
|
101
|
-
/**
|
|
102
|
-
|
|
111
|
+
/** Add a sink invoked for every emitted entry */
|
|
112
|
+
addHandler(handler: (entry: LogEntry) => void): void;
|
|
113
|
+
removeHandler(handler: (entry: LogEntry) => void): void;
|
|
103
114
|
|
|
104
|
-
/**
|
|
115
|
+
/** Open a named group; nest by calling again */
|
|
116
|
+
group(name: string): void;
|
|
105
117
|
groupEnd(): void;
|
|
106
118
|
|
|
107
|
-
/**
|
|
119
|
+
/** Retained entries, optionally narrowed */
|
|
120
|
+
getLogs(filter?: LogQuery): LogEntry[];
|
|
121
|
+
|
|
122
|
+
/** Entry counts by level and category */
|
|
123
|
+
getStats(): Record<string, unknown>;
|
|
124
|
+
|
|
125
|
+
/** Discard retained entries */
|
|
108
126
|
clear(): void;
|
|
109
127
|
|
|
110
|
-
/**
|
|
111
|
-
|
|
128
|
+
/** Raise or lower the minimum level */
|
|
129
|
+
setLevel(level: LogLevelValue): void;
|
|
130
|
+
|
|
131
|
+
/** Serialize retained entries */
|
|
132
|
+
export(format?: 'array' | 'json' | 'csv' | 'text'): unknown;
|
|
112
133
|
|
|
113
|
-
/**
|
|
114
|
-
|
|
134
|
+
/** A logger that stamps every entry with extra context */
|
|
135
|
+
withContext(context: Record<string, unknown>): DevLogger;
|
|
115
136
|
|
|
116
|
-
/**
|
|
117
|
-
|
|
137
|
+
/** Render tabular data */
|
|
138
|
+
table(data: unknown): void;
|
|
118
139
|
|
|
119
|
-
/**
|
|
120
|
-
|
|
140
|
+
/** Start a named timer */
|
|
141
|
+
time(label: string): void;
|
|
142
|
+
/** Stop a named timer and log the elapsed time */
|
|
143
|
+
timeEnd(label: string): void;
|
|
144
|
+
|
|
145
|
+
/** Entries held back by `buffer` */
|
|
146
|
+
getBuffer(): LogEntry[];
|
|
147
|
+
/** Emit and clear the buffer */
|
|
148
|
+
flush(): void;
|
|
149
|
+
/** Drop the buffer without emitting */
|
|
150
|
+
clearBuffer(): void;
|
|
121
151
|
}
|
|
122
152
|
|
|
123
|
-
/**
|
|
124
|
-
* Create a logger instance
|
|
125
|
-
*/
|
|
153
|
+
/** Create a {@link DevLogger}. */
|
|
126
154
|
export function createLogger(options?: LoggerOptions): DevLogger;
|
|
127
155
|
|
|
128
|
-
/**
|
|
129
|
-
* Create a logger for a specific component
|
|
130
|
-
*/
|
|
156
|
+
/** A logger prefixed with a component name. */
|
|
131
157
|
export function createComponentLogger(componentName: string, options?: LoggerOptions): DevLogger;
|
|
132
158
|
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
*/
|
|
136
|
-
export function createConsoleLogger(): DevLogger;
|
|
159
|
+
/** A logger that writes straight to the console. */
|
|
160
|
+
export function createConsoleLogger(prefix?: string): DevLogger;
|
|
137
161
|
|
|
138
162
|
// ============================================================================
|
|
139
|
-
// Inspector
|
|
163
|
+
// Inspector
|
|
140
164
|
// ============================================================================
|
|
141
165
|
|
|
142
|
-
/**
|
|
143
|
-
* Inspector configuration options
|
|
144
|
-
*/
|
|
145
166
|
export interface InspectorOptions {
|
|
146
|
-
/**
|
|
167
|
+
/** Retain past inspections; defaults to `true` */
|
|
147
168
|
trackHistory?: boolean;
|
|
148
|
-
/**
|
|
169
|
+
/** Cap on retained inspections; defaults to `100` */
|
|
149
170
|
maxHistory?: number;
|
|
150
|
-
/**
|
|
171
|
+
/** Log each inspection; defaults to `false` */
|
|
151
172
|
verbose?: boolean;
|
|
173
|
+
[option: string]: unknown;
|
|
152
174
|
}
|
|
153
175
|
|
|
154
|
-
/**
|
|
155
|
-
* Component analysis result
|
|
156
|
-
*/
|
|
157
|
-
export interface ComponentAnalysis {
|
|
158
|
-
/** Component type */
|
|
159
|
-
type: string;
|
|
160
|
-
/** Whether component is valid */
|
|
161
|
-
valid: boolean;
|
|
162
|
-
/** Validation issues */
|
|
163
|
-
issues: string[];
|
|
164
|
-
/** Warnings */
|
|
165
|
-
warnings: string[];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Component tree node
|
|
170
|
-
*/
|
|
171
176
|
export interface ComponentTreeNode {
|
|
172
|
-
/** Component type/name */
|
|
173
177
|
type: string;
|
|
174
|
-
/** Tag name (if element) */
|
|
175
|
-
tagName?: string;
|
|
176
|
-
/** Child nodes */
|
|
177
|
-
children: ComponentTreeNode[];
|
|
178
|
-
/** Tree depth */
|
|
179
|
-
depth: number;
|
|
180
|
-
/** Component name */
|
|
181
178
|
name?: string;
|
|
182
|
-
/** Component ID */
|
|
183
|
-
id?: string;
|
|
184
|
-
/** Props */
|
|
185
179
|
props?: Record<string, unknown>;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
renderCount?: number;
|
|
180
|
+
children?: ComponentTreeNode[];
|
|
181
|
+
depth?: number;
|
|
182
|
+
[key: string]: unknown;
|
|
190
183
|
}
|
|
191
184
|
|
|
192
|
-
/**
|
|
193
|
-
* Component statistics
|
|
194
|
-
*/
|
|
195
185
|
export interface ComponentStats {
|
|
196
|
-
/** Maximum depth */
|
|
197
186
|
depth: number;
|
|
198
|
-
|
|
187
|
+
nodeCount: number;
|
|
199
188
|
elementCount: number;
|
|
200
|
-
/** Complexity score */
|
|
201
189
|
complexity: number;
|
|
202
|
-
|
|
203
|
-
nodeCount: number;
|
|
190
|
+
[key: string]: unknown;
|
|
204
191
|
}
|
|
205
192
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
/** Component props */
|
|
213
|
-
props: Record<string, unknown>;
|
|
214
|
-
/** Component state */
|
|
215
|
-
state: Record<string, unknown>;
|
|
216
|
-
/** Rendered output */
|
|
217
|
-
rendered: CoherentNode;
|
|
218
|
-
/** Render time (ms) */
|
|
219
|
-
renderTime: number;
|
|
220
|
-
/** Update count */
|
|
221
|
-
updateCount: number;
|
|
193
|
+
export interface ComponentAnalysis {
|
|
194
|
+
type: string;
|
|
195
|
+
valid: boolean;
|
|
196
|
+
issues: string[];
|
|
197
|
+
warnings: string[];
|
|
198
|
+
[key: string]: unknown;
|
|
222
199
|
}
|
|
223
200
|
|
|
224
|
-
/**
|
|
225
|
-
* Full inspection result
|
|
226
|
-
*/
|
|
201
|
+
/** What one `inspect()` call produced. */
|
|
227
202
|
export interface InspectionResult {
|
|
228
|
-
/** Unique inspection ID */
|
|
229
203
|
id: string;
|
|
230
|
-
/** Inspection timestamp */
|
|
231
204
|
timestamp: number;
|
|
232
|
-
/** Time
|
|
205
|
+
/** Time the inspection itself took, in ms */
|
|
233
206
|
inspectionTime: number;
|
|
234
|
-
/** Inspected component */
|
|
235
207
|
component: unknown;
|
|
236
|
-
/** Additional metadata */
|
|
237
208
|
metadata: Record<string, unknown>;
|
|
238
|
-
/** Component type */
|
|
239
209
|
type: string;
|
|
240
|
-
/** Component structure */
|
|
241
210
|
structure: unknown;
|
|
242
|
-
/** Component props */
|
|
243
211
|
props: Record<string, unknown>;
|
|
244
|
-
/** Tree depth */
|
|
245
212
|
depth: number;
|
|
246
|
-
/** Direct child count */
|
|
247
213
|
childCount: number;
|
|
248
|
-
/** Complexity score */
|
|
249
214
|
complexity: number;
|
|
250
|
-
/** Total node count */
|
|
251
215
|
nodeCount: number;
|
|
252
|
-
/** Analysis result */
|
|
253
216
|
analysis: ComponentAnalysis;
|
|
254
|
-
/** Component tree */
|
|
255
217
|
tree: ComponentTreeNode;
|
|
256
|
-
/** Statistics */
|
|
257
218
|
stats: ComponentStats;
|
|
258
|
-
/** Whether component is valid */
|
|
259
219
|
valid: boolean;
|
|
260
|
-
/** Validation issues */
|
|
261
220
|
issues: string[];
|
|
262
|
-
/**
|
|
221
|
+
/** Alias of `issues` */
|
|
263
222
|
errors: string[];
|
|
264
|
-
/** Warnings found */
|
|
265
223
|
warnings: string[];
|
|
266
224
|
}
|
|
267
225
|
|
|
268
|
-
/**
|
|
269
|
-
* Component inspector class
|
|
270
|
-
*/
|
|
226
|
+
/** Analyzes component structure and keeps a history of inspections. */
|
|
271
227
|
export class ComponentInspector {
|
|
272
228
|
constructor(options?: InspectorOptions);
|
|
273
229
|
|
|
274
|
-
|
|
230
|
+
options: InspectorOptions;
|
|
231
|
+
components: Map<string, InspectionResult>;
|
|
232
|
+
history: InspectionResult[];
|
|
233
|
+
inspectionCount: number;
|
|
234
|
+
|
|
235
|
+
/** Analyze a component and record the result */
|
|
275
236
|
inspect(component: unknown, metadata?: Record<string, unknown>): InspectionResult;
|
|
276
237
|
|
|
277
|
-
/**
|
|
278
|
-
|
|
238
|
+
/** Props of the component's root element */
|
|
239
|
+
extractProps(component: unknown): Record<string, unknown>;
|
|
240
|
+
|
|
241
|
+
/** Type, validity, issues and warnings */
|
|
242
|
+
analyzeComponent(component: unknown): ComponentAnalysis;
|
|
279
243
|
|
|
280
|
-
/**
|
|
244
|
+
/** Structural tree, capped at `maxDepth` */
|
|
245
|
+
buildComponentTree(component: unknown, depth?: number, maxDepth?: number): ComponentTreeNode;
|
|
246
|
+
|
|
247
|
+
/** Depth, node counts and complexity */
|
|
248
|
+
calculateStats(component: unknown): ComponentStats;
|
|
249
|
+
|
|
250
|
+
/** A past inspection by id */
|
|
281
251
|
getComponent(id: string): InspectionResult | undefined;
|
|
282
252
|
|
|
283
|
-
/**
|
|
253
|
+
/** Copy of the inspection history */
|
|
254
|
+
getHistory(): InspectionResult[];
|
|
255
|
+
|
|
256
|
+
/** Past inspections matching every criterion */
|
|
257
|
+
search(criteria: Record<string, unknown>): InspectionResult[];
|
|
258
|
+
|
|
259
|
+
/** Structural differences between two components */
|
|
260
|
+
compare(componentA: unknown, componentB: unknown): Record<string, unknown>;
|
|
261
|
+
|
|
262
|
+
/** Summary of everything inspected so far */
|
|
263
|
+
generateReport(): Record<string, unknown>;
|
|
264
|
+
|
|
265
|
+
/** Drop recorded inspections and history */
|
|
284
266
|
clear(): void;
|
|
267
|
+
/** Drop history only */
|
|
268
|
+
clearHistory(): void;
|
|
269
|
+
|
|
270
|
+
/** Counts of inspections, components and issues */
|
|
271
|
+
getStats(): Record<string, unknown>;
|
|
272
|
+
|
|
273
|
+
/** Serialize recorded inspections */
|
|
274
|
+
export(): Record<string, unknown>;
|
|
285
275
|
}
|
|
286
276
|
|
|
287
|
-
/**
|
|
288
|
-
* Create an inspector instance
|
|
289
|
-
*/
|
|
277
|
+
/** Create a {@link ComponentInspector}. */
|
|
290
278
|
export function createInspector(options?: InspectorOptions): ComponentInspector;
|
|
291
279
|
|
|
292
|
-
/**
|
|
293
|
-
|
|
294
|
-
*/
|
|
295
|
-
export function inspect(component: unknown, metadata?: Record<string, unknown>): InspectionResult;
|
|
280
|
+
/** Inspect a component with a throwaway inspector. */
|
|
281
|
+
export function inspect(component: unknown, options?: InspectorOptions): InspectionResult;
|
|
296
282
|
|
|
297
|
-
/**
|
|
298
|
-
|
|
299
|
-
*/
|
|
300
|
-
export function validateComponent(component: unknown): { valid: boolean; issues: string[] };
|
|
283
|
+
/** Throws when the component is structurally invalid. */
|
|
284
|
+
export function validateComponent(component: unknown): boolean;
|
|
301
285
|
|
|
302
286
|
// ============================================================================
|
|
303
|
-
// Profiler
|
|
287
|
+
// Profiler
|
|
304
288
|
// ============================================================================
|
|
305
289
|
|
|
306
|
-
/**
|
|
307
|
-
* Profiler configuration options
|
|
308
|
-
*/
|
|
309
290
|
export interface ProfilerOptions {
|
|
310
|
-
/**
|
|
291
|
+
/** Record anything at all; defaults to `true` */
|
|
292
|
+
enabled?: boolean;
|
|
293
|
+
/** Fraction of sessions and renders to record, 0 to 1; defaults to `1.0` */
|
|
311
294
|
sampleRate?: number;
|
|
312
|
-
/**
|
|
295
|
+
/** Above this many ms a render counts as slow; defaults to `16` */
|
|
296
|
+
slowThreshold?: number;
|
|
297
|
+
/** Sample heap usage where the runtime exposes it */
|
|
298
|
+
trackMemory?: boolean;
|
|
299
|
+
/** Cap on retained measurements; defaults to `1000` */
|
|
313
300
|
maxSamples?: number;
|
|
314
|
-
|
|
315
|
-
autoStart?: boolean;
|
|
301
|
+
[option: string]: unknown;
|
|
316
302
|
}
|
|
317
303
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
/** Measurement name */
|
|
323
|
-
name: string;
|
|
324
|
-
/** Duration in ms */
|
|
325
|
-
duration: number;
|
|
326
|
-
/** Start time */
|
|
327
|
-
startTime: number;
|
|
328
|
-
/** End time */
|
|
329
|
-
endTime: number;
|
|
330
|
-
/** Additional metadata */
|
|
331
|
-
metadata?: Record<string, unknown>;
|
|
304
|
+
export interface MemoryUsage {
|
|
305
|
+
used: number;
|
|
306
|
+
total: number;
|
|
307
|
+
limit: number;
|
|
332
308
|
}
|
|
333
309
|
|
|
334
|
-
/**
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
export interface ProfilerResult {
|
|
338
|
-
/** Component name */
|
|
310
|
+
/** One recorded render. */
|
|
311
|
+
export interface RenderMeasurement {
|
|
312
|
+
id: string;
|
|
339
313
|
componentName: string;
|
|
340
|
-
|
|
341
|
-
duration: number;
|
|
342
|
-
/** Render phase */
|
|
343
|
-
phase: 'mount' | 'update';
|
|
344
|
-
/** Actual duration (excluding suspended time) */
|
|
345
|
-
actualDuration: number;
|
|
346
|
-
/** Base duration (without memoization) */
|
|
347
|
-
baseDuration: number;
|
|
348
|
-
/** Start time */
|
|
314
|
+
props: Record<string, unknown>;
|
|
349
315
|
startTime: number;
|
|
350
|
-
|
|
351
|
-
|
|
316
|
+
endTime?: number;
|
|
317
|
+
duration?: number;
|
|
318
|
+
startMemory: MemoryUsage | null;
|
|
319
|
+
endMemory?: MemoryUsage | null;
|
|
320
|
+
memoryDelta?: number;
|
|
321
|
+
phase: string;
|
|
322
|
+
result?: Record<string, unknown>;
|
|
323
|
+
/** Whether `duration` exceeded `slowThreshold` */
|
|
324
|
+
slow?: boolean;
|
|
352
325
|
}
|
|
353
326
|
|
|
354
|
-
/**
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
327
|
+
/** A point in time inside a session. */
|
|
328
|
+
export interface ProfilerMark {
|
|
329
|
+
name: string;
|
|
330
|
+
timestamp: number;
|
|
331
|
+
data: Record<string, unknown>;
|
|
332
|
+
memory: MemoryUsage | null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface ProfilerStatistics {
|
|
336
|
+
mean: number;
|
|
337
|
+
median: number;
|
|
338
|
+
min: number;
|
|
339
|
+
max: number;
|
|
340
|
+
stdDev: number;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export interface ProfilerMetrics {
|
|
344
|
+
totalOperations: number;
|
|
345
|
+
totalDuration: number;
|
|
346
|
+
operationCounts: Record<string, number>;
|
|
347
|
+
averageDuration: number;
|
|
348
|
+
memoryUsage: number | null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface ProfilerReport {
|
|
352
|
+
summary: {
|
|
353
|
+
totalOperations: number;
|
|
354
|
+
averageDuration: number;
|
|
355
|
+
slowOperations: number;
|
|
356
|
+
};
|
|
357
|
+
statistics: ProfilerStatistics;
|
|
358
|
+
operations: Array<{ name: string; duration: number; timestamp: number }>;
|
|
359
|
+
bottlenecks: Array<{ name: string; duration: number; timestamp: number }>;
|
|
360
|
+
recommendations: unknown[];
|
|
361
|
+
timestamp: number;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface MeasurementQuery {
|
|
365
|
+
componentName?: string;
|
|
366
|
+
/** Only measurements flagged slow */
|
|
367
|
+
slow?: boolean;
|
|
368
|
+
minDuration?: number;
|
|
369
|
+
limit?: number;
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
/**
|
|
373
|
-
*
|
|
373
|
+
* Records render timings, session marks and memory usage.
|
|
374
|
+
*
|
|
375
|
+
* `start()` returns a session id — `null` when profiling is disabled or the
|
|
376
|
+
* call was sampled out — and every id-taking method tolerates `null`.
|
|
374
377
|
*/
|
|
375
378
|
export class PerformanceProfiler {
|
|
376
379
|
constructor(options?: ProfilerOptions);
|
|
377
380
|
|
|
378
|
-
|
|
379
|
-
|
|
381
|
+
options: ProfilerOptions;
|
|
382
|
+
measurements: RenderMeasurement[];
|
|
383
|
+
sessions: Map<string, Record<string, unknown>>;
|
|
384
|
+
currentSession: Record<string, unknown> | null;
|
|
385
|
+
marks: Map<string, RenderMeasurement>;
|
|
386
|
+
|
|
387
|
+
/** Open a session; `null` when disabled or sampled out */
|
|
388
|
+
start(name?: string): string | null;
|
|
389
|
+
|
|
390
|
+
/** Close a session and return its analysis; `null` when unknown */
|
|
391
|
+
stop(sessionId: string | null): Record<string, unknown> | null;
|
|
392
|
+
|
|
393
|
+
/** Begin timing a render; `null` when disabled or sampled out */
|
|
394
|
+
startRender(componentName: string, props?: Record<string, unknown>): string | null;
|
|
395
|
+
|
|
396
|
+
/** Finish timing a render; `null` when the id is unknown */
|
|
397
|
+
endRender(measurementId: string | null, result?: Record<string, unknown>): RenderMeasurement | null;
|
|
398
|
+
|
|
399
|
+
/** Record a named point in the current session */
|
|
400
|
+
mark(name: string, data?: Record<string, unknown>): ProfilerMark;
|
|
401
|
+
|
|
402
|
+
/** Elapsed time between two marks; throws when either is missing */
|
|
403
|
+
measure(startMark: string, endMark: string): {
|
|
404
|
+
duration: number;
|
|
405
|
+
startMark: string;
|
|
406
|
+
endMark: string;
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
/** Heap usage, where the runtime exposes it */
|
|
410
|
+
getMemoryUsage(): MemoryUsage | null;
|
|
411
|
+
|
|
412
|
+
/** A mark in the current session by name */
|
|
413
|
+
findMark(name: string): ProfilerMark | null | undefined;
|
|
414
|
+
|
|
415
|
+
/** Retained measurements, optionally narrowed */
|
|
416
|
+
getMeasurements(filter?: MeasurementQuery): RenderMeasurement[];
|
|
380
417
|
|
|
381
|
-
/**
|
|
382
|
-
|
|
418
|
+
/** Aggregate one session's measurements */
|
|
419
|
+
analyzeSession(session: Record<string, unknown>): Record<string, unknown>;
|
|
383
420
|
|
|
384
|
-
/**
|
|
385
|
-
|
|
421
|
+
/** Group measurements by component name */
|
|
422
|
+
groupByComponent(measurements: RenderMeasurement[]): Record<string, unknown>;
|
|
386
423
|
|
|
387
|
-
/**
|
|
388
|
-
|
|
424
|
+
/** Totals, averages and the slowest recent renders */
|
|
425
|
+
getSummary(): Record<string, unknown>;
|
|
389
426
|
|
|
390
|
-
/**
|
|
391
|
-
|
|
427
|
+
/** Mean, median, min, max and standard deviation */
|
|
428
|
+
getStatistics(): ProfilerStatistics;
|
|
392
429
|
|
|
393
|
-
/**
|
|
430
|
+
/** Measurements slower than the threshold, slowest first */
|
|
431
|
+
getBottlenecks(threshold?: number | null): Array<{
|
|
432
|
+
name: string;
|
|
433
|
+
duration: number;
|
|
434
|
+
timestamp: number;
|
|
435
|
+
}>;
|
|
436
|
+
|
|
437
|
+
/** Operation counts, totals and heap usage */
|
|
438
|
+
getMetrics(): ProfilerMetrics;
|
|
439
|
+
|
|
440
|
+
/** Statistics, metrics, bottlenecks and recommendations together */
|
|
441
|
+
generateReport(): ProfilerReport;
|
|
442
|
+
|
|
443
|
+
/** Serialize sessions, measurements and metrics */
|
|
444
|
+
export(): Record<string, unknown>;
|
|
445
|
+
|
|
446
|
+
/** Render the metrics as a printable block */
|
|
447
|
+
formatMetrics(): string;
|
|
448
|
+
|
|
449
|
+
/** Compare two sessions; `null` when either id is unknown */
|
|
450
|
+
compare(profileId1: string, profileId2: string): Record<string, unknown> | null;
|
|
451
|
+
|
|
452
|
+
/** Suggestions derived from the recorded measurements */
|
|
453
|
+
getRecommendations(): unknown[];
|
|
454
|
+
|
|
455
|
+
/** Drop measurements, sessions and marks */
|
|
394
456
|
clear(): void;
|
|
395
457
|
|
|
396
|
-
/**
|
|
397
|
-
|
|
458
|
+
/** Resume recording */
|
|
459
|
+
enable(): void;
|
|
460
|
+
/** Stop recording; `start()` and `startRender()` then return `null` */
|
|
461
|
+
disable(): void;
|
|
398
462
|
}
|
|
399
463
|
|
|
400
|
-
/**
|
|
401
|
-
* Create a profiler instance
|
|
402
|
-
*/
|
|
464
|
+
/** Create a {@link PerformanceProfiler}. */
|
|
403
465
|
export function createProfiler(options?: ProfilerOptions): PerformanceProfiler;
|
|
404
466
|
|
|
405
467
|
/**
|
|
406
|
-
*
|
|
468
|
+
* Time an async function. Rejects with `{ error, duration }` when `fn` throws.
|
|
407
469
|
*/
|
|
408
|
-
export function measure<T>(
|
|
470
|
+
export function measure<T>(
|
|
471
|
+
name: string,
|
|
472
|
+
fn: () => T | Promise<T>,
|
|
473
|
+
profiler?: PerformanceProfiler | null
|
|
474
|
+
): Promise<{ value: T; duration: number }>;
|
|
409
475
|
|
|
410
|
-
/**
|
|
411
|
-
|
|
412
|
-
*/
|
|
413
|
-
export function profile<T>(fn: () => T): { result: T; duration: number };
|
|
476
|
+
/** Wrap a function so calls to it can be profiled. */
|
|
477
|
+
export function profile<F extends (...args: never[]) => unknown>(fn: F): F;
|
|
414
478
|
|
|
415
479
|
// ============================================================================
|
|
416
|
-
// DevTools
|
|
480
|
+
// DevTools
|
|
417
481
|
// ============================================================================
|
|
418
482
|
|
|
419
483
|
/**
|
|
420
|
-
*
|
|
484
|
+
* Development-only instrumentation: render interception, validation, hot
|
|
485
|
+
* reload and a browser panel.
|
|
486
|
+
*
|
|
487
|
+
* Enabled only when `NODE_ENV=development`, or on localhost / `?dev=true` in
|
|
488
|
+
* a browser. `isEnabled` is a property, not a method.
|
|
421
489
|
*/
|
|
422
|
-
export
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
490
|
+
export class DevTools {
|
|
491
|
+
constructor(coherentInstance?: unknown);
|
|
492
|
+
|
|
493
|
+
coherent: unknown;
|
|
494
|
+
/** Whether instrumentation is active in this environment */
|
|
495
|
+
isEnabled: boolean;
|
|
496
|
+
renderHistory: unknown[];
|
|
497
|
+
componentRegistry: Map<string, unknown>;
|
|
498
|
+
warnings: unknown[];
|
|
499
|
+
errors: unknown[];
|
|
500
|
+
hotReloadEnabled: boolean;
|
|
501
|
+
|
|
502
|
+
/** Whether the environment looks like development */
|
|
503
|
+
shouldEnable(): boolean;
|
|
504
|
+
|
|
505
|
+
/** Install every hook; called by the constructor when enabled */
|
|
506
|
+
initialize(): void;
|
|
507
|
+
|
|
508
|
+
/** Report a component's type, structure and props */
|
|
509
|
+
inspectComponent(component: unknown): Record<string, unknown>;
|
|
510
|
+
|
|
511
|
+
/** Render the structure as an indented tree */
|
|
512
|
+
visualizeStructure(component: unknown, depth?: number, maxDepth?: number): string;
|
|
513
|
+
|
|
514
|
+
/** Suggestions for a specific component */
|
|
515
|
+
getOptimizationRecommendations(component: unknown): unknown[];
|
|
516
|
+
|
|
517
|
+
/** Aggregate timings from the render history */
|
|
518
|
+
getPerformanceInsights(): Record<string, unknown>;
|
|
519
|
+
|
|
520
|
+
/** Throws when the component is structurally invalid */
|
|
521
|
+
validateComponent(component: unknown): boolean;
|
|
522
|
+
|
|
523
|
+
/** Deep structural check, collecting issues instead of throwing */
|
|
524
|
+
deepValidateComponent(component: unknown, path?: string, depth?: number): unknown[];
|
|
525
|
+
|
|
526
|
+
/** Depth, breadth and node counts */
|
|
527
|
+
analyzeComplexity(component: unknown, depth?: number): Record<string, unknown>;
|
|
528
|
+
|
|
529
|
+
/** Drop render history, warnings and errors */
|
|
530
|
+
clearDevData(): void;
|
|
531
|
+
|
|
532
|
+
/** Turn one instrumentation feature on or off */
|
|
533
|
+
toggleFeature(feature: string): void;
|
|
534
|
+
|
|
535
|
+
/** Print a summary of the session to the console */
|
|
536
|
+
printDevSummary(): void;
|
|
433
537
|
}
|
|
434
538
|
|
|
435
|
-
/**
|
|
436
|
-
|
|
437
|
-
*/
|
|
438
|
-
export interface DevToolsInstance {
|
|
439
|
-
/** Inspect a component */
|
|
440
|
-
inspect(component: ComponentInstance): InspectorData;
|
|
539
|
+
/** Create a {@link DevTools} instance bound to a Coherent instance. */
|
|
540
|
+
export function createDevTools(coherentInstance?: unknown): DevTools;
|
|
441
541
|
|
|
442
|
-
|
|
443
|
-
|
|
542
|
+
// ============================================================================
|
|
543
|
+
// Component Visualizer
|
|
544
|
+
// ============================================================================
|
|
444
545
|
|
|
445
|
-
|
|
446
|
-
|
|
546
|
+
export interface VisualizerOptions {
|
|
547
|
+
/** Depth cap; defaults to `50` */
|
|
548
|
+
maxDepth?: number;
|
|
549
|
+
/** Include props; defaults to `true` */
|
|
550
|
+
showProps?: boolean;
|
|
551
|
+
/** Include metadata; defaults to `true` */
|
|
552
|
+
showMetadata?: boolean;
|
|
553
|
+
/** Emit ANSI colors; defaults to `true` */
|
|
554
|
+
colorOutput?: boolean;
|
|
555
|
+
/** One line per node; defaults to `false` */
|
|
556
|
+
compactMode?: boolean;
|
|
557
|
+
[option: string]: unknown;
|
|
558
|
+
}
|
|
447
559
|
|
|
448
|
-
|
|
449
|
-
|
|
560
|
+
export interface VisualizerStats {
|
|
561
|
+
totalComponents: number;
|
|
562
|
+
totalDepth: number;
|
|
563
|
+
staticComponents: number;
|
|
564
|
+
dynamicComponents: number;
|
|
565
|
+
/** Time spent building the visualization, in ms */
|
|
566
|
+
renderTime: number;
|
|
567
|
+
}
|
|
450
568
|
|
|
451
|
-
|
|
452
|
-
|
|
569
|
+
export interface VisualizationResult {
|
|
570
|
+
/** The printable tree */
|
|
571
|
+
visualization: string;
|
|
572
|
+
stats: VisualizerStats;
|
|
573
|
+
tree: ComponentTreeNode;
|
|
574
|
+
}
|
|
453
575
|
|
|
454
|
-
|
|
455
|
-
|
|
576
|
+
/** Renders a component tree as printable text, DOT or JSON. */
|
|
577
|
+
export class ComponentVisualizer {
|
|
578
|
+
constructor(options?: VisualizerOptions);
|
|
579
|
+
|
|
580
|
+
options: VisualizerOptions;
|
|
581
|
+
stats: VisualizerStats;
|
|
582
|
+
|
|
583
|
+
/** Build the tree and render it */
|
|
584
|
+
visualize(component: unknown, name?: string): VisualizationResult;
|
|
585
|
+
|
|
586
|
+
/** Build the tree without rendering */
|
|
587
|
+
buildTree(component: unknown, name: string, depth: number): ComponentTreeNode;
|
|
588
|
+
|
|
589
|
+
/** Render a prepared tree */
|
|
590
|
+
renderTree(tree: ComponentTreeNode): string;
|
|
456
591
|
|
|
457
|
-
/**
|
|
458
|
-
|
|
592
|
+
/** Serialize a tree as JSON */
|
|
593
|
+
exportAsJSON(tree: ComponentTreeNode): string;
|
|
594
|
+
|
|
595
|
+
/** Serialize a tree as Graphviz DOT */
|
|
596
|
+
exportAsDOT(tree: ComponentTreeNode): string;
|
|
459
597
|
}
|
|
460
598
|
|
|
599
|
+
/** Create a {@link ComponentVisualizer}. */
|
|
600
|
+
export function createComponentVisualizer(options?: VisualizerOptions): ComponentVisualizer;
|
|
601
|
+
|
|
602
|
+
/** Visualize with a throwaway visualizer. */
|
|
603
|
+
export function visualizeComponent(
|
|
604
|
+
component: unknown,
|
|
605
|
+
name?: string,
|
|
606
|
+
options?: VisualizerOptions
|
|
607
|
+
): VisualizationResult;
|
|
608
|
+
|
|
609
|
+
/** {@link visualizeComponent}, printed to the console. */
|
|
610
|
+
export function logComponentTree(
|
|
611
|
+
component: unknown,
|
|
612
|
+
name?: string,
|
|
613
|
+
options?: VisualizerOptions
|
|
614
|
+
): VisualizationResult;
|
|
615
|
+
|
|
461
616
|
// ============================================================================
|
|
462
|
-
//
|
|
617
|
+
// Performance Dashboard
|
|
463
618
|
// ============================================================================
|
|
464
619
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
620
|
+
export interface DashboardOptions {
|
|
621
|
+
/** Metric refresh interval in ms; defaults to `5000` */
|
|
622
|
+
updateInterval?: number;
|
|
623
|
+
/** History points retained per category; defaults to `100` */
|
|
624
|
+
maxHistoryPoints?: number;
|
|
625
|
+
/** Raise alerts on threshold breaches; defaults to `true` */
|
|
626
|
+
enableAlerts?: boolean;
|
|
627
|
+
/** Derive recommendations; defaults to `true` */
|
|
628
|
+
enableRecommendations?: boolean;
|
|
629
|
+
/** Emit ANSI colors; defaults to `true` */
|
|
630
|
+
colorOutput?: boolean;
|
|
631
|
+
[option: string]: unknown;
|
|
477
632
|
}
|
|
478
633
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
logger: DevLogger;
|
|
485
|
-
/** Inspector instance */
|
|
486
|
-
inspector: ComponentInspector;
|
|
487
|
-
/** Profiler instance */
|
|
488
|
-
profiler: PerformanceProfiler;
|
|
634
|
+
export interface DashboardMetrics {
|
|
635
|
+
api: Record<string, unknown>;
|
|
636
|
+
components: Record<string, unknown>;
|
|
637
|
+
fullstack: Record<string, unknown>;
|
|
638
|
+
}
|
|
489
639
|
|
|
490
|
-
|
|
640
|
+
/** Collects API, component and full-stack timings and renders them. */
|
|
641
|
+
export class PerformanceDashboard {
|
|
642
|
+
constructor(options?: DashboardOptions);
|
|
491
643
|
|
|
492
|
-
|
|
493
|
-
|
|
644
|
+
options: DashboardOptions;
|
|
645
|
+
metrics: DashboardMetrics;
|
|
646
|
+
alerts: unknown[];
|
|
647
|
+
recommendations: unknown[];
|
|
648
|
+
startTime: number;
|
|
494
649
|
|
|
495
|
-
/**
|
|
496
|
-
|
|
650
|
+
/** Begin refreshing metrics on `updateInterval` */
|
|
651
|
+
startMonitoring(): void;
|
|
652
|
+
/** Stop refreshing */
|
|
653
|
+
stopMonitoring(): void;
|
|
497
654
|
|
|
498
|
-
|
|
499
|
-
|
|
655
|
+
recordAPIRequest(duration: number, routeType: string, cacheHit?: boolean): void;
|
|
656
|
+
recordComponentRender(
|
|
657
|
+
duration: number,
|
|
658
|
+
componentType: string,
|
|
659
|
+
cacheHit?: boolean,
|
|
660
|
+
memoryDelta?: number
|
|
661
|
+
): void;
|
|
662
|
+
recordFullStackRequest(duration: number, error?: unknown, bottlenecks?: unknown[]): void;
|
|
500
663
|
|
|
501
|
-
/**
|
|
502
|
-
|
|
664
|
+
/** Recompute derived metrics, alerts and recommendations */
|
|
665
|
+
updateMetrics(): void;
|
|
503
666
|
|
|
504
|
-
/**
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
667
|
+
/** Cache hit ratio for one category */
|
|
668
|
+
getCacheHitRate(category: string): number;
|
|
669
|
+
|
|
670
|
+
/** Render the dashboard as printable text */
|
|
671
|
+
generateDashboard(): string;
|
|
672
|
+
|
|
673
|
+
/** Overall score derived from the current metrics */
|
|
674
|
+
calculatePerformanceScore(): number;
|
|
675
|
+
|
|
676
|
+
/** Serialize the current metrics */
|
|
677
|
+
exportMetrics(): Record<string, unknown>;
|
|
678
|
+
|
|
679
|
+
/** Clear every metric and alert */
|
|
680
|
+
reset(): void;
|
|
510
681
|
}
|
|
511
682
|
|
|
512
|
-
/**
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
683
|
+
/** Create a {@link PerformanceDashboard}. */
|
|
684
|
+
export function createPerformanceDashboard(options?: DashboardOptions): PerformanceDashboard;
|
|
685
|
+
|
|
686
|
+
/** Print a dashboard to the console and return it. */
|
|
687
|
+
export function showPerformanceDashboard(dashboard: PerformanceDashboard): PerformanceDashboard;
|
|
516
688
|
|
|
517
689
|
// ============================================================================
|
|
518
|
-
//
|
|
690
|
+
// Enhanced Errors
|
|
519
691
|
// ============================================================================
|
|
520
692
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
693
|
+
export interface ErrorHandlerOptions {
|
|
694
|
+
/** How far to walk the component tree for context; defaults to `5` */
|
|
695
|
+
maxContextDepth?: number;
|
|
696
|
+
/** Keep the original stack; defaults to `true` */
|
|
697
|
+
includeStackTrace?: boolean;
|
|
698
|
+
/** Derive fix suggestions; defaults to `true` */
|
|
699
|
+
showSuggestions?: boolean;
|
|
700
|
+
/** Emit ANSI colors; defaults to `true` */
|
|
701
|
+
colorOutput?: boolean;
|
|
702
|
+
[option: string]: unknown;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/** An error with component context and suggested fixes attached. */
|
|
706
|
+
export interface EnhancedError {
|
|
707
|
+
originalError: Error;
|
|
708
|
+
message: string;
|
|
709
|
+
stack?: string;
|
|
710
|
+
timestamp: number;
|
|
711
|
+
component: Record<string, unknown> | null;
|
|
712
|
+
context: Record<string, unknown>;
|
|
713
|
+
suggestions: string[];
|
|
714
|
+
severity: string;
|
|
715
|
+
category: string;
|
|
716
|
+
componentContext?: Record<string, unknown>;
|
|
717
|
+
propValidation?: Record<string, unknown>;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/** Turns a raw error into one carrying component context and suggestions. */
|
|
721
|
+
export class EnhancedErrorHandler {
|
|
722
|
+
constructor(options?: ErrorHandlerOptions);
|
|
723
|
+
|
|
724
|
+
options: ErrorHandlerOptions;
|
|
725
|
+
errorHistory: EnhancedError[];
|
|
726
|
+
|
|
727
|
+
/** Enhance an error and record it; the last 100 are retained */
|
|
728
|
+
handleError(
|
|
729
|
+
error: Error,
|
|
730
|
+
component?: unknown,
|
|
731
|
+
context?: Record<string, unknown>
|
|
732
|
+
): EnhancedError;
|
|
733
|
+
|
|
734
|
+
/** Type, validity and complexity of a component */
|
|
735
|
+
analyzeComponent(component: unknown): Record<string, unknown>;
|
|
736
|
+
|
|
737
|
+
/** Where the component sits in the tree */
|
|
738
|
+
getComponentContext(component: unknown, path?: string[]): Record<string, unknown>;
|
|
739
|
+
|
|
740
|
+
/** Prop problems that could explain the error */
|
|
741
|
+
validateProps(component: unknown): Record<string, unknown>;
|
|
742
|
+
|
|
743
|
+
/** Fixes matching the error's known patterns */
|
|
744
|
+
generateSuggestions(enhancedError: EnhancedError): string[];
|
|
745
|
+
|
|
746
|
+
/** Render an enhanced error for the console */
|
|
747
|
+
formatError(enhancedError: EnhancedError): string;
|
|
748
|
+
|
|
749
|
+
/** Counts by severity and category */
|
|
750
|
+
getErrorStats(): Record<string, unknown>;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** Create an {@link EnhancedErrorHandler}. */
|
|
754
|
+
export function createEnhancedErrorHandler(options?: ErrorHandlerOptions): EnhancedErrorHandler;
|
|
541
755
|
|
|
542
|
-
|
|
756
|
+
/** Enhance an error, print it, and return it. */
|
|
757
|
+
export function handleEnhancedError(
|
|
758
|
+
error: Error,
|
|
759
|
+
component?: unknown,
|
|
760
|
+
context?: Record<string, unknown>
|
|
761
|
+
): EnhancedError;
|