pulse-js-framework 1.7.9 → 1.7.10
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/cli/lint.js +442 -3
- package/compiler/lexer.js +6 -0
- package/compiler/parser.js +144 -1
- package/compiler/transformer/imports.js +15 -0
- package/compiler/transformer/index.js +46 -0
- package/compiler/transformer/view.js +180 -5
- package/package.json +9 -2
- package/runtime/a11y.js +1005 -0
- package/runtime/devtools/a11y-audit.js +442 -0
- package/runtime/devtools/diagnostics.js +403 -0
- package/runtime/devtools/index.js +53 -0
- package/runtime/devtools/time-travel.js +189 -0
- package/runtime/devtools.js +138 -497
- package/runtime/dom-binding.js +7 -4
- package/runtime/dom-element.js +192 -1
- package/runtime/dom.js +8 -2
- package/runtime/index.js +2 -0
- package/runtime/native.js +2 -2
- package/runtime/security.js +461 -0
- package/runtime/utils.js +37 -16
- package/types/a11y.d.ts +336 -0
package/runtime/devtools.js
CHANGED
|
@@ -7,460 +7,81 @@
|
|
|
7
7
|
* - Time-travel debugging with state snapshots
|
|
8
8
|
* - Performance monitoring
|
|
9
9
|
* - Effect tracking
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import { pulse, effect, batch, context } from './pulse.js';
|
|
13
|
-
|
|
14
|
-
// =============================================================================
|
|
15
|
-
// DEV TOOLS STATE
|
|
16
|
-
// =============================================================================
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Dev tools configuration
|
|
20
|
-
*/
|
|
21
|
-
const config = {
|
|
22
|
-
enabled: false,
|
|
23
|
-
maxSnapshots: 50,
|
|
24
|
-
logUpdates: false,
|
|
25
|
-
logEffects: false,
|
|
26
|
-
warnOnSlowEffects: true,
|
|
27
|
-
slowEffectThreshold: 16 // ms (one frame at 60fps)
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Registry of all tracked pulses
|
|
32
|
-
* @type {Map<string, {pulse: Pulse, name: string, createdAt: number}>}
|
|
33
|
-
*/
|
|
34
|
-
const pulseRegistry = new Map();
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Registry of all tracked effects
|
|
38
|
-
* @type {Map<string, {effect: Object, name: string, createdAt: number, runCount: number, totalTime: number}>}
|
|
39
|
-
*/
|
|
40
|
-
const effectRegistry = new Map();
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Time-travel state history
|
|
44
|
-
* @type {Array<{timestamp: number, state: Object, action: string}>}
|
|
45
|
-
*/
|
|
46
|
-
const stateHistory = [];
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Current position in history (for time-travel)
|
|
50
|
-
*/
|
|
51
|
-
let historyIndex = -1;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Flag to prevent recording during time-travel
|
|
55
|
-
*/
|
|
56
|
-
let isTimeTraveling = false;
|
|
57
|
-
|
|
58
|
-
// =============================================================================
|
|
59
|
-
// DIAGNOSTICS API
|
|
60
|
-
// =============================================================================
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @typedef {Object} DiagnosticsStats
|
|
64
|
-
* @property {number} pulseCount - Total number of active pulses
|
|
65
|
-
* @property {number} effectCount - Total number of active effects
|
|
66
|
-
* @property {number} totalEffectRuns - Total effect executions
|
|
67
|
-
* @property {number} avgEffectTime - Average effect execution time (ms)
|
|
68
|
-
* @property {number} pendingEffects - Effects waiting to run
|
|
69
|
-
* @property {number} batchDepth - Current batch nesting depth
|
|
70
|
-
* @property {number} snapshotCount - Number of stored snapshots
|
|
71
|
-
* @property {Object} memoryEstimate - Estimated memory usage
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Get current diagnostics statistics
|
|
76
|
-
* @returns {DiagnosticsStats}
|
|
10
|
+
* - Accessibility auditing
|
|
77
11
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
export function getDiagnostics() {
|
|
84
|
-
let totalRuns = 0;
|
|
85
|
-
let totalTime = 0;
|
|
86
|
-
|
|
87
|
-
for (const entry of effectRegistry.values()) {
|
|
88
|
-
totalRuns += entry.runCount;
|
|
89
|
-
totalTime += entry.totalTime;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
pulseCount: pulseRegistry.size,
|
|
94
|
-
effectCount: effectRegistry.size,
|
|
95
|
-
totalEffectRuns: totalRuns,
|
|
96
|
-
avgEffectTime: totalRuns > 0 ? totalTime / totalRuns : 0,
|
|
97
|
-
pendingEffects: context.pendingEffects.size,
|
|
98
|
-
batchDepth: context.batchDepth,
|
|
99
|
-
snapshotCount: stateHistory.length,
|
|
100
|
-
memoryEstimate: {
|
|
101
|
-
pulses: pulseRegistry.size * 100, // rough estimate in bytes
|
|
102
|
-
effects: effectRegistry.size * 200,
|
|
103
|
-
history: stateHistory.length * 500
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Get detailed effect statistics
|
|
110
|
-
* @returns {Array<{id: string, name: string, runCount: number, avgTime: number, lastRun: number}>}
|
|
12
|
+
* Architecture:
|
|
13
|
+
* This module re-exports from specialized sub-modules:
|
|
14
|
+
* - devtools/diagnostics.js: Graph inspection, tracking, profiling
|
|
15
|
+
* - devtools/time-travel.js: State snapshots and history navigation
|
|
16
|
+
* - devtools/a11y-audit.js: Accessibility validation and reporting
|
|
111
17
|
*/
|
|
112
|
-
export function getEffectStats() {
|
|
113
|
-
return [...effectRegistry.entries()].map(([id, entry]) => ({
|
|
114
|
-
id,
|
|
115
|
-
name: entry.name,
|
|
116
|
-
runCount: entry.runCount,
|
|
117
|
-
avgTime: entry.runCount > 0 ? entry.totalTime / entry.runCount : 0,
|
|
118
|
-
totalTime: entry.totalTime,
|
|
119
|
-
createdAt: entry.createdAt
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
18
|
|
|
123
|
-
|
|
124
|
-
* Get list of all tracked pulses
|
|
125
|
-
* @returns {Array<{id: string, name: string, value: any, subscriberCount: number}>}
|
|
126
|
-
*/
|
|
127
|
-
export function getPulseList() {
|
|
128
|
-
return [...pulseRegistry.entries()].map(([id, entry]) => ({
|
|
129
|
-
id,
|
|
130
|
-
name: entry.name,
|
|
131
|
-
value: entry.pulse.peek(),
|
|
132
|
-
subscriberCount: entry.pulse._subscribers?.size || 0,
|
|
133
|
-
createdAt: entry.createdAt
|
|
134
|
-
}));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// =============================================================================
|
|
138
|
-
// REACTIVE GRAPH INSPECTION
|
|
139
|
-
// =============================================================================
|
|
19
|
+
import { createLogger } from './logger.js';
|
|
140
20
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
value: null,
|
|
187
|
-
dependencies: [],
|
|
188
|
-
dependents: []
|
|
189
|
-
};
|
|
190
|
-
nodes.push(node);
|
|
191
|
-
nodeMap.set(id, node);
|
|
192
|
-
|
|
193
|
-
// Get effect's dependencies
|
|
194
|
-
if (entry.effect?.dependencies) {
|
|
195
|
-
for (const dep of entry.effect.dependencies) {
|
|
196
|
-
const depId = findPulseId(dep);
|
|
197
|
-
if (depId) {
|
|
198
|
-
node.dependencies.push(depId);
|
|
199
|
-
edges.push({ from: depId, to: id });
|
|
200
|
-
|
|
201
|
-
const depNode = nodeMap.get(depId);
|
|
202
|
-
if (depNode) {
|
|
203
|
-
depNode.dependents.push(id);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return { nodes, edges };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Find pulse ID from pulse instance
|
|
215
|
-
*/
|
|
216
|
-
function findPulseId(pulseInstance) {
|
|
217
|
-
for (const [id, entry] of pulseRegistry) {
|
|
218
|
-
if (entry.pulse === pulseInstance) {
|
|
219
|
-
return id;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Export graph in DOT format for Graphviz visualization
|
|
227
|
-
* @returns {string} DOT format graph
|
|
228
|
-
*/
|
|
229
|
-
export function exportGraphAsDot() {
|
|
230
|
-
const { nodes, edges } = getDependencyGraph();
|
|
231
|
-
|
|
232
|
-
let dot = 'digraph ReactiveGraph {\n';
|
|
233
|
-
dot += ' rankdir=LR;\n';
|
|
234
|
-
dot += ' node [shape=box];\n\n';
|
|
235
|
-
|
|
236
|
-
// Add nodes with styling
|
|
237
|
-
for (const node of nodes) {
|
|
238
|
-
const color = node.type === 'pulse' ? 'lightblue' : 'lightgreen';
|
|
239
|
-
const label = `${node.name}\\n${node.type}`;
|
|
240
|
-
dot += ` "${node.id}" [label="${label}" fillcolor="${color}" style="filled"];\n`;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
dot += '\n';
|
|
244
|
-
|
|
245
|
-
// Add edges
|
|
246
|
-
for (const edge of edges) {
|
|
247
|
-
dot += ` "${edge.from}" -> "${edge.to}";\n`;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
dot += '}\n';
|
|
251
|
-
return dot;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// =============================================================================
|
|
255
|
-
// TIME-TRAVEL DEBUGGING
|
|
256
|
-
// =============================================================================
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* @typedef {Object} StateSnapshot
|
|
260
|
-
* @property {number} timestamp - When snapshot was taken
|
|
261
|
-
* @property {Object} state - Serialized state
|
|
262
|
-
* @property {string} action - Description of what caused the snapshot
|
|
263
|
-
* @property {number} index - Position in history
|
|
264
|
-
*/
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Take a snapshot of current state
|
|
268
|
-
* @param {string} [action='manual'] - Description of the action
|
|
269
|
-
* @returns {StateSnapshot}
|
|
270
|
-
*/
|
|
271
|
-
export function takeSnapshot(action = 'manual') {
|
|
272
|
-
if (isTimeTraveling) return null;
|
|
273
|
-
|
|
274
|
-
const state = {};
|
|
275
|
-
for (const [id, entry] of pulseRegistry) {
|
|
276
|
-
try {
|
|
277
|
-
// Deep clone to prevent mutation
|
|
278
|
-
state[id] = JSON.parse(JSON.stringify(entry.pulse.peek()));
|
|
279
|
-
} catch {
|
|
280
|
-
// Non-serializable value
|
|
281
|
-
state[id] = '[Non-serializable]';
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const snapshot = {
|
|
286
|
-
timestamp: Date.now(),
|
|
287
|
-
state,
|
|
288
|
-
action,
|
|
289
|
-
index: stateHistory.length
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
// Trim history if too long
|
|
293
|
-
if (stateHistory.length >= config.maxSnapshots) {
|
|
294
|
-
stateHistory.shift();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
stateHistory.push(snapshot);
|
|
298
|
-
historyIndex = stateHistory.length - 1;
|
|
299
|
-
|
|
300
|
-
return snapshot;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Get state history
|
|
305
|
-
* @returns {StateSnapshot[]}
|
|
306
|
-
*/
|
|
307
|
-
export function getHistory() {
|
|
308
|
-
return [...stateHistory];
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Get current history position
|
|
313
|
-
* @returns {number}
|
|
314
|
-
*/
|
|
315
|
-
export function getHistoryIndex() {
|
|
316
|
-
return historyIndex;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Travel to a specific point in history
|
|
321
|
-
* @param {number} index - History index to travel to
|
|
322
|
-
* @returns {boolean} Success
|
|
323
|
-
*/
|
|
324
|
-
export function travelTo(index) {
|
|
325
|
-
if (index < 0 || index >= stateHistory.length) {
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const snapshot = stateHistory[index];
|
|
330
|
-
isTimeTraveling = true;
|
|
331
|
-
|
|
332
|
-
batch(() => {
|
|
333
|
-
for (const [id, value] of Object.entries(snapshot.state)) {
|
|
334
|
-
const entry = pulseRegistry.get(id);
|
|
335
|
-
if (entry && value !== '[Non-serializable]') {
|
|
336
|
-
entry.pulse.set(value);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
historyIndex = index;
|
|
342
|
-
isTimeTraveling = false;
|
|
343
|
-
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Go back one step in history
|
|
349
|
-
* @returns {boolean} Success
|
|
350
|
-
*/
|
|
351
|
-
export function back() {
|
|
352
|
-
return travelTo(historyIndex - 1);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Go forward one step in history
|
|
357
|
-
* @returns {boolean} Success
|
|
358
|
-
*/
|
|
359
|
-
export function forward() {
|
|
360
|
-
return travelTo(historyIndex + 1);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Clear all history
|
|
365
|
-
*/
|
|
366
|
-
export function clearHistory() {
|
|
367
|
-
stateHistory.length = 0;
|
|
368
|
-
historyIndex = -1;
|
|
369
|
-
}
|
|
21
|
+
// Import from sub-modules
|
|
22
|
+
import {
|
|
23
|
+
config,
|
|
24
|
+
getDiagnostics,
|
|
25
|
+
getEffectStats,
|
|
26
|
+
getPulseList,
|
|
27
|
+
getDependencyGraph,
|
|
28
|
+
exportGraphAsDot,
|
|
29
|
+
trackedPulse as basedTrackedPulse,
|
|
30
|
+
trackedEffect,
|
|
31
|
+
profile,
|
|
32
|
+
mark,
|
|
33
|
+
resetDiagnostics,
|
|
34
|
+
_setSnapshotCountFn
|
|
35
|
+
} from './devtools/diagnostics.js';
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
timeTravelConfig,
|
|
39
|
+
getIsTimeTraveling,
|
|
40
|
+
takeSnapshot,
|
|
41
|
+
getHistory,
|
|
42
|
+
getHistoryIndex,
|
|
43
|
+
getSnapshotCount,
|
|
44
|
+
travelTo,
|
|
45
|
+
back,
|
|
46
|
+
forward,
|
|
47
|
+
clearHistory
|
|
48
|
+
} from './devtools/time-travel.js';
|
|
49
|
+
|
|
50
|
+
// Wire up snapshot count for diagnostics
|
|
51
|
+
_setSnapshotCountFn(getSnapshotCount);
|
|
52
|
+
|
|
53
|
+
import {
|
|
54
|
+
a11yAuditConfig,
|
|
55
|
+
runA11yAudit,
|
|
56
|
+
getA11yIssues,
|
|
57
|
+
getA11yStats,
|
|
58
|
+
enableA11yAudit,
|
|
59
|
+
disableA11yAudit,
|
|
60
|
+
toggleA11yHighlights,
|
|
61
|
+
exportA11yReport,
|
|
62
|
+
resetA11yAudit
|
|
63
|
+
} from './devtools/a11y-audit.js';
|
|
64
|
+
|
|
65
|
+
const log = createLogger('DevTools');
|
|
370
66
|
|
|
371
67
|
// =============================================================================
|
|
372
|
-
// PULSE
|
|
68
|
+
// TRACKED PULSE WITH SNAPSHOT INTEGRATION
|
|
373
69
|
// =============================================================================
|
|
374
70
|
|
|
375
|
-
let pulseIdCounter = 0;
|
|
376
|
-
let trackedEffectIdCounter = 0;
|
|
377
|
-
|
|
378
71
|
/**
|
|
379
|
-
* Create a tracked pulse
|
|
72
|
+
* Create a tracked pulse with automatic snapshot on change
|
|
380
73
|
* @param {any} initialValue - Initial value
|
|
381
74
|
* @param {string} [name] - Display name for debugging
|
|
382
75
|
* @returns {Pulse} Tracked pulse
|
|
383
76
|
*/
|
|
384
77
|
export function trackedPulse(initialValue, name) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
name: name || id,
|
|
391
|
-
createdAt: Date.now()
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// Wrap set to record snapshots
|
|
395
|
-
const originalSet = p.set.bind(p);
|
|
396
|
-
p.set = (value) => {
|
|
397
|
-
const result = originalSet(value);
|
|
398
|
-
if (config.enabled && config.logUpdates) {
|
|
399
|
-
console.log(`[Pulse] ${name || id} updated:`, value);
|
|
400
|
-
}
|
|
401
|
-
if (config.enabled && !isTimeTraveling) {
|
|
402
|
-
takeSnapshot(`${name || id} = ${JSON.stringify(value)}`);
|
|
403
|
-
}
|
|
404
|
-
return result;
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
// Add dispose method
|
|
408
|
-
p.dispose = () => {
|
|
409
|
-
pulseRegistry.delete(id);
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
return p;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Create a tracked effect (for dev tools)
|
|
417
|
-
* @param {function} fn - Effect function
|
|
418
|
-
* @param {string} [name] - Display name for debugging
|
|
419
|
-
* @returns {function} Dispose function
|
|
420
|
-
*/
|
|
421
|
-
export function trackedEffect(fn, name) {
|
|
422
|
-
const id = `effect_${++trackedEffectIdCounter}`;
|
|
423
|
-
const startTime = Date.now();
|
|
424
|
-
|
|
425
|
-
const entry = {
|
|
426
|
-
effect: null,
|
|
427
|
-
name: name || id,
|
|
428
|
-
createdAt: startTime,
|
|
429
|
-
runCount: 0,
|
|
430
|
-
totalTime: 0
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
effectRegistry.set(id, entry);
|
|
434
|
-
|
|
435
|
-
const wrappedFn = () => {
|
|
436
|
-
const runStart = performance.now();
|
|
437
|
-
|
|
438
|
-
if (config.enabled && config.logEffects) {
|
|
439
|
-
console.log(`[Effect] ${name || id} running...`);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const result = fn();
|
|
443
|
-
|
|
444
|
-
const runTime = performance.now() - runStart;
|
|
445
|
-
entry.runCount++;
|
|
446
|
-
entry.totalTime += runTime;
|
|
447
|
-
|
|
448
|
-
if (config.enabled && config.warnOnSlowEffects && runTime > config.slowEffectThreshold) {
|
|
449
|
-
console.warn(`[Effect] ${name || id} took ${runTime.toFixed(2)}ms (slow)`);
|
|
78
|
+
return basedTrackedPulse(initialValue, name, {
|
|
79
|
+
onSnapshot: (action) => {
|
|
80
|
+
if (!getIsTimeTraveling()) {
|
|
81
|
+
takeSnapshot(action);
|
|
82
|
+
}
|
|
450
83
|
}
|
|
451
|
-
|
|
452
|
-
return result;
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const dispose = effect(wrappedFn, { id });
|
|
456
|
-
|
|
457
|
-
// Store reference to effect for graph building
|
|
458
|
-
entry.effect = context.currentEffect;
|
|
459
|
-
|
|
460
|
-
return () => {
|
|
461
|
-
dispose();
|
|
462
|
-
effectRegistry.delete(id);
|
|
463
|
-
};
|
|
84
|
+
});
|
|
464
85
|
}
|
|
465
86
|
|
|
466
87
|
// =============================================================================
|
|
@@ -474,24 +95,47 @@ export function trackedEffect(fn, name) {
|
|
|
474
95
|
export function enableDevTools(options = {}) {
|
|
475
96
|
Object.assign(config, options, { enabled: true });
|
|
476
97
|
|
|
98
|
+
if (options.maxSnapshots) {
|
|
99
|
+
timeTravelConfig.maxSnapshots = options.maxSnapshots;
|
|
100
|
+
}
|
|
101
|
+
|
|
477
102
|
if (typeof window !== 'undefined') {
|
|
478
103
|
// Expose to window for browser dev tools
|
|
479
104
|
window.__PULSE_DEVTOOLS__ = {
|
|
105
|
+
// Diagnostics
|
|
480
106
|
getDiagnostics,
|
|
481
107
|
getEffectStats,
|
|
482
108
|
getPulseList,
|
|
483
109
|
getDependencyGraph,
|
|
484
110
|
exportGraphAsDot,
|
|
111
|
+
profile,
|
|
112
|
+
mark,
|
|
113
|
+
|
|
114
|
+
// Time-travel
|
|
485
115
|
takeSnapshot,
|
|
486
116
|
getHistory,
|
|
487
117
|
travelTo,
|
|
488
118
|
back,
|
|
489
119
|
forward,
|
|
490
120
|
clearHistory,
|
|
491
|
-
|
|
121
|
+
|
|
122
|
+
// A11y Audit
|
|
123
|
+
runA11yAudit,
|
|
124
|
+
getA11yIssues,
|
|
125
|
+
getA11yStats,
|
|
126
|
+
enableA11yAudit,
|
|
127
|
+
disableA11yAudit,
|
|
128
|
+
toggleA11yHighlights,
|
|
129
|
+
exportA11yReport,
|
|
130
|
+
resetA11yAudit,
|
|
131
|
+
|
|
132
|
+
// Config
|
|
133
|
+
config,
|
|
134
|
+
timeTravelConfig,
|
|
135
|
+
a11yAuditConfig
|
|
492
136
|
};
|
|
493
137
|
|
|
494
|
-
|
|
138
|
+
log.info('Enabled. Access via window.__PULSE_DEVTOOLS__');
|
|
495
139
|
}
|
|
496
140
|
}
|
|
497
141
|
|
|
@@ -504,6 +148,8 @@ export function disableDevTools() {
|
|
|
504
148
|
if (typeof window !== 'undefined') {
|
|
505
149
|
delete window.__PULSE_DEVTOOLS__;
|
|
506
150
|
}
|
|
151
|
+
|
|
152
|
+
log.info('Disabled');
|
|
507
153
|
}
|
|
508
154
|
|
|
509
155
|
/**
|
|
@@ -520,67 +166,58 @@ export function isDevToolsEnabled() {
|
|
|
520
166
|
*/
|
|
521
167
|
export function configureDevTools(options) {
|
|
522
168
|
Object.assign(config, options);
|
|
169
|
+
|
|
170
|
+
if (options.maxSnapshots) {
|
|
171
|
+
timeTravelConfig.maxSnapshots = options.maxSnapshots;
|
|
172
|
+
}
|
|
523
173
|
}
|
|
524
174
|
|
|
525
175
|
/**
|
|
526
176
|
* Clear all dev tools data
|
|
527
177
|
*/
|
|
528
178
|
export function resetDevTools() {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
historyIndex = -1;
|
|
533
|
-
pulseIdCounter = 0;
|
|
534
|
-
trackedEffectIdCounter = 0;
|
|
179
|
+
resetDiagnostics();
|
|
180
|
+
clearHistory();
|
|
181
|
+
resetA11yAudit();
|
|
535
182
|
}
|
|
536
183
|
|
|
537
184
|
// =============================================================================
|
|
538
|
-
//
|
|
185
|
+
// RE-EXPORTS
|
|
539
186
|
// =============================================================================
|
|
540
187
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
*/
|
|
552
|
-
export function profile(name, fn) {
|
|
553
|
-
const start = performance.now();
|
|
554
|
-
|
|
555
|
-
try {
|
|
556
|
-
return fn();
|
|
557
|
-
} finally {
|
|
558
|
-
const duration = performance.now() - start;
|
|
559
|
-
console.log(`[Profile] ${name}: ${duration.toFixed(2)}ms`);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
188
|
+
export {
|
|
189
|
+
// Diagnostics
|
|
190
|
+
getDiagnostics,
|
|
191
|
+
getEffectStats,
|
|
192
|
+
getPulseList,
|
|
193
|
+
getDependencyGraph,
|
|
194
|
+
exportGraphAsDot,
|
|
195
|
+
trackedEffect,
|
|
196
|
+
profile,
|
|
197
|
+
mark,
|
|
562
198
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
199
|
+
// Time-travel
|
|
200
|
+
takeSnapshot,
|
|
201
|
+
getHistory,
|
|
202
|
+
getHistoryIndex,
|
|
203
|
+
travelTo,
|
|
204
|
+
back,
|
|
205
|
+
forward,
|
|
206
|
+
clearHistory,
|
|
207
|
+
|
|
208
|
+
// A11y Audit
|
|
209
|
+
runA11yAudit,
|
|
210
|
+
getA11yIssues,
|
|
211
|
+
getA11yStats,
|
|
212
|
+
enableA11yAudit,
|
|
213
|
+
disableA11yAudit,
|
|
214
|
+
toggleA11yHighlights,
|
|
215
|
+
exportA11yReport,
|
|
216
|
+
resetA11yAudit
|
|
217
|
+
};
|
|
581
218
|
|
|
582
219
|
// =============================================================================
|
|
583
|
-
//
|
|
220
|
+
// DEFAULT EXPORT
|
|
584
221
|
// =============================================================================
|
|
585
222
|
|
|
586
223
|
export default {
|
|
@@ -588,10 +225,12 @@ export default {
|
|
|
588
225
|
getDiagnostics,
|
|
589
226
|
getEffectStats,
|
|
590
227
|
getPulseList,
|
|
591
|
-
|
|
592
|
-
// Graph
|
|
593
228
|
getDependencyGraph,
|
|
594
229
|
exportGraphAsDot,
|
|
230
|
+
trackedPulse,
|
|
231
|
+
trackedEffect,
|
|
232
|
+
profile,
|
|
233
|
+
mark,
|
|
595
234
|
|
|
596
235
|
// Time-travel
|
|
597
236
|
takeSnapshot,
|
|
@@ -602,10 +241,6 @@ export default {
|
|
|
602
241
|
forward,
|
|
603
242
|
clearHistory,
|
|
604
243
|
|
|
605
|
-
// Tracking
|
|
606
|
-
trackedPulse,
|
|
607
|
-
trackedEffect,
|
|
608
|
-
|
|
609
244
|
// Configuration
|
|
610
245
|
enableDevTools,
|
|
611
246
|
disableDevTools,
|
|
@@ -613,7 +248,13 @@ export default {
|
|
|
613
248
|
configureDevTools,
|
|
614
249
|
resetDevTools,
|
|
615
250
|
|
|
616
|
-
//
|
|
617
|
-
|
|
618
|
-
|
|
251
|
+
// Accessibility Audit
|
|
252
|
+
runA11yAudit,
|
|
253
|
+
getA11yIssues,
|
|
254
|
+
getA11yStats,
|
|
255
|
+
enableA11yAudit,
|
|
256
|
+
disableA11yAudit,
|
|
257
|
+
toggleA11yHighlights,
|
|
258
|
+
exportA11yReport,
|
|
259
|
+
resetA11yAudit
|
|
619
260
|
};
|