@capillaryjs/capillary-devtools 1.0.0
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/CHANGELOG.md +18 -0
- package/LICENSE +55 -0
- package/NOTICE +2 -0
- package/README.md +142 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/model/TracePlayback.d.ts +28 -0
- package/dist/model/TracePlayback.d.ts.map +1 -0
- package/dist/model/TracePlayback.js +63 -0
- package/dist/model/TracePlayback.js.map +1 -0
- package/dist/model/TraceRecorder.d.ts +58 -0
- package/dist/model/TraceRecorder.d.ts.map +1 -0
- package/dist/model/TraceRecorder.js +269 -0
- package/dist/model/TraceRecorder.js.map +1 -0
- package/dist/model/TraceSelection.d.ts +18 -0
- package/dist/model/TraceSelection.d.ts.map +1 -0
- package/dist/model/TraceSelection.js +17 -0
- package/dist/model/TraceSelection.js.map +1 -0
- package/dist/model/index.d.ts +11 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +6 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/projection.d.ts +37 -0
- package/dist/model/projection.d.ts.map +1 -0
- package/dist/model/projection.js +207 -0
- package/dist/model/projection.js.map +1 -0
- package/dist/model/scope.d.ts +4 -0
- package/dist/model/scope.d.ts.map +1 -0
- package/dist/model/scope.js +4 -0
- package/dist/model/scope.js.map +1 -0
- package/dist/model/types.d.ts +76 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +2 -0
- package/dist/model/types.js.map +1 -0
- package/dist/views/ActivityOverviewView.d.ts +6 -0
- package/dist/views/ActivityOverviewView.d.ts.map +1 -0
- package/dist/views/ActivityOverviewView.js +12 -0
- package/dist/views/ActivityOverviewView.js.map +1 -0
- package/dist/views/CausalTraceView.d.ts +7 -0
- package/dist/views/CausalTraceView.d.ts.map +1 -0
- package/dist/views/CausalTraceView.js +34 -0
- package/dist/views/CausalTraceView.js.map +1 -0
- package/dist/views/ChronologicalTraceView.d.ts +6 -0
- package/dist/views/ChronologicalTraceView.d.ts.map +1 -0
- package/dist/views/ChronologicalTraceView.js +10 -0
- package/dist/views/ChronologicalTraceView.js.map +1 -0
- package/dist/views/FlowGraphView.d.ts +7 -0
- package/dist/views/FlowGraphView.d.ts.map +1 -0
- package/dist/views/FlowGraphView.js +47 -0
- package/dist/views/FlowGraphView.js.map +1 -0
- package/dist/views/TraceDetailsView.d.ts +6 -0
- package/dist/views/TraceDetailsView.d.ts.map +1 -0
- package/dist/views/TraceDetailsView.js +28 -0
- package/dist/views/TraceDetailsView.js.map +1 -0
- package/dist/views/TraceInspector.d.ts +9 -0
- package/dist/views/TraceInspector.d.ts.map +1 -0
- package/dist/views/TraceInspector.js +40 -0
- package/dist/views/TraceInspector.js.map +1 -0
- package/dist/views/TracePlaybackControls.d.ts +7 -0
- package/dist/views/TracePlaybackControls.d.ts.map +1 -0
- package/dist/views/TracePlaybackControls.js +18 -0
- package/dist/views/TracePlaybackControls.js.map +1 -0
- package/dist/views/TraceTimelineView.d.ts +6 -0
- package/dist/views/TraceTimelineView.d.ts.map +1 -0
- package/dist/views/TraceTimelineView.js +11 -0
- package/dist/views/TraceTimelineView.js.map +1 -0
- package/dist/views/shared.d.ts +23 -0
- package/dist/views/shared.d.ts.map +1 -0
- package/dist/views/shared.js +52 -0
- package/dist/views/shared.js.map +1 -0
- package/package.json +73 -0
- package/styles/structural.css +331 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Diagnostics, Emitter } from '@capillaryjs/capillary';
|
|
2
|
+
import { devtoolsDiagnosticScope } from './scope.js';
|
|
3
|
+
/** A bounded, caller-owned sink. Payload references never enter default recordings. */
|
|
4
|
+
export class TraceRecorder {
|
|
5
|
+
revision = new Emitter(0, { diagnosticScope: devtoolsDiagnosticScope });
|
|
6
|
+
options;
|
|
7
|
+
events = [];
|
|
8
|
+
nodes = new Map();
|
|
9
|
+
edges = [];
|
|
10
|
+
sequence = 0;
|
|
11
|
+
unsubscribe = null;
|
|
12
|
+
startedAt = null;
|
|
13
|
+
stoppedAt = null;
|
|
14
|
+
lateStart = true;
|
|
15
|
+
gaps = 0;
|
|
16
|
+
evictedEvents = 0;
|
|
17
|
+
droppedTopology = 0;
|
|
18
|
+
bytes = 0;
|
|
19
|
+
notifying = false;
|
|
20
|
+
disposed = false;
|
|
21
|
+
cached = null;
|
|
22
|
+
recordBytes = new WeakMap();
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.options = { maxEvents: 2000, maxBytes: 2_000_000, maxNodes: 2000, maxEdges: 8000,
|
|
25
|
+
maxPreviewLength: 180, capture: 'scalar', clock: Date.now,
|
|
26
|
+
topology: true, verbose: false, ui: true, ...options };
|
|
27
|
+
for (const key of ['maxEvents', 'maxBytes', 'maxNodes', 'maxEdges', 'maxPreviewLength']) {
|
|
28
|
+
if (!Number.isSafeInteger(this.options[key]) || this.options[key] < 1) {
|
|
29
|
+
throw new TypeError(`${key} must be a positive safe integer`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!['none', 'scalar', 'preview', 'raw', 'formatter'].includes(this.options.capture)) {
|
|
33
|
+
throw new TypeError('Unknown trace capture mode');
|
|
34
|
+
}
|
|
35
|
+
if (this.options.capture === 'formatter' && typeof options.formatter !== 'function') {
|
|
36
|
+
throw new TypeError('Formatter capture requires a formatter');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
start(options = {}) {
|
|
40
|
+
if (this.disposed)
|
|
41
|
+
throw new Error('Cannot start a disposed recorder');
|
|
42
|
+
if (this.unsubscribe)
|
|
43
|
+
return this;
|
|
44
|
+
if (this.startedAt !== null)
|
|
45
|
+
this.gaps += 1;
|
|
46
|
+
else {
|
|
47
|
+
this.startedAt = this.options.clock();
|
|
48
|
+
this.lateStart = !options.fromStart;
|
|
49
|
+
}
|
|
50
|
+
this.stoppedAt = null;
|
|
51
|
+
this.unsubscribe = Diagnostics.subscribe((fact) => this.accept(fact), this.options);
|
|
52
|
+
for (const root of options.roots ?? [])
|
|
53
|
+
Diagnostics.inspect(root);
|
|
54
|
+
this.changed();
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
stop() {
|
|
58
|
+
if (!this.unsubscribe)
|
|
59
|
+
return this;
|
|
60
|
+
this.unsubscribe();
|
|
61
|
+
this.unsubscribe = null;
|
|
62
|
+
this.stoppedAt = this.options.clock();
|
|
63
|
+
this.changed();
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
reset() {
|
|
67
|
+
const active = this.unsubscribe !== null;
|
|
68
|
+
this.stop();
|
|
69
|
+
this.events = [];
|
|
70
|
+
this.nodes.clear();
|
|
71
|
+
this.edges = [];
|
|
72
|
+
this.sequence = 0;
|
|
73
|
+
this.startedAt = null;
|
|
74
|
+
this.stoppedAt = null;
|
|
75
|
+
this.lateStart = true;
|
|
76
|
+
this.gaps = 0;
|
|
77
|
+
this.evictedEvents = 0;
|
|
78
|
+
this.droppedTopology = 0;
|
|
79
|
+
this.bytes = 0;
|
|
80
|
+
if (active)
|
|
81
|
+
this.start();
|
|
82
|
+
this.changed();
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
snapshot() {
|
|
86
|
+
this.cached ??= Object.freeze({ schemaVersion: 1, events: Object.freeze([...this.events]),
|
|
87
|
+
nodes: Object.freeze([...this.nodes.values()]), edges: Object.freeze([...this.edges]),
|
|
88
|
+
capture: Object.freeze({ active: this.unsubscribe !== null, startedAt: this.startedAt,
|
|
89
|
+
stoppedAt: this.stoppedAt, lateStart: this.lateStart, gaps: this.gaps,
|
|
90
|
+
evictedEvents: this.evictedEvents, droppedTopology: this.droppedTopology, bytes: this.bytes,
|
|
91
|
+
maxBytes: this.options.maxBytes, maxEvents: this.options.maxEvents,
|
|
92
|
+
rawReferences: this.options.capture === 'raw', topology: this.options.topology,
|
|
93
|
+
ui: this.options.ui, verbose: this.options.verbose }) });
|
|
94
|
+
return this.cached;
|
|
95
|
+
}
|
|
96
|
+
/** Safe JSON export strips raw references before serialization (including toJSON hooks). */
|
|
97
|
+
export() { return JSON.stringify(exportable(this.snapshot()), null, 2); }
|
|
98
|
+
dispose() { this.stop(); this.disposed = true; this.revision.dispose(); }
|
|
99
|
+
accept(fact) {
|
|
100
|
+
if (fact.type === 'node')
|
|
101
|
+
this.addNode(fact.node);
|
|
102
|
+
else if (fact.type === 'disposed') {
|
|
103
|
+
const node = this.nodes.get(fact.nodeId);
|
|
104
|
+
if (node)
|
|
105
|
+
this.nodes.set(node.id, Object.freeze({ ...node, disposedSequence: this.sequence }));
|
|
106
|
+
this.edges = this.edges.map((edge) => edge.toSequence === null
|
|
107
|
+
&& (edge.from === fact.nodeId || edge.to === fact.nodeId)
|
|
108
|
+
? Object.freeze({ ...edge, toSequence: this.sequence }) : edge);
|
|
109
|
+
}
|
|
110
|
+
else if (fact.type === 'edge') {
|
|
111
|
+
const index = this.edges.findIndex((edge) => edge.from === fact.from && edge.to === fact.to && edge.toSequence === null);
|
|
112
|
+
if (fact.connected && index < 0)
|
|
113
|
+
this.edges.push(Object.freeze({ from: fact.from, to: fact.to,
|
|
114
|
+
fromSequence: this.sequence, toSequence: null }));
|
|
115
|
+
else if (!fact.connected && index >= 0)
|
|
116
|
+
this.edges[index] = Object.freeze({ ...this.edges[index], toSequence: this.sequence });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const event = fact.event;
|
|
120
|
+
if (event.diagnosticScope?.capture === false)
|
|
121
|
+
return;
|
|
122
|
+
const details = event.diagnostic;
|
|
123
|
+
const owner = event.owner;
|
|
124
|
+
const identity = details?.node ?? Diagnostics.node(owner !== null && (typeof owner === 'object' || typeof owner === 'function') ? owner : event);
|
|
125
|
+
if (!identity)
|
|
126
|
+
return;
|
|
127
|
+
const node = details?.node ?? { ...identity, label: event.purpose };
|
|
128
|
+
this.addNode(node);
|
|
129
|
+
const preview = (value, field) => this.preview(value, { nodeId: node.id, field });
|
|
130
|
+
this.events.push(Object.freeze({ id: event.id, sequence: this.sequence++, timestamp: this.options.clock(),
|
|
131
|
+
parentId: event.parent?.id ?? null, nodeId: node.id, kind: details?.kind ?? 'operation',
|
|
132
|
+
outcome: details?.outcome ?? null, cause: (typeof event.cause === 'string' ? event.cause : scalarText(event.cause)).slice(0, 512),
|
|
133
|
+
value: preview(event.value, 'value'), before: details && Object.hasOwn(details, 'before')
|
|
134
|
+
? preview(details.before, 'before') : null,
|
|
135
|
+
error: details?.error == null ? null : preview(details.error, 'error'),
|
|
136
|
+
fetchState: details?.fetchState ?? null, attemptId: details?.attemptId ?? null,
|
|
137
|
+
delayMs: details?.delayMs ?? null,
|
|
138
|
+
inputs: Object.freeze((details?.inputs ?? []).map((input) => Object.freeze({ nodeId: input.nodeId,
|
|
139
|
+
value: preview(input.value, 'input') }))) }));
|
|
140
|
+
}
|
|
141
|
+
this.enforceBounds();
|
|
142
|
+
this.changed();
|
|
143
|
+
}
|
|
144
|
+
addNode(node) {
|
|
145
|
+
if (this.nodes.has(node.id))
|
|
146
|
+
return;
|
|
147
|
+
this.nodes.set(node.id, Object.freeze({ ...node, label: node.label.slice(0, 240),
|
|
148
|
+
firstSequence: this.sequence, disposedSequence: null }));
|
|
149
|
+
}
|
|
150
|
+
preview(value, context) {
|
|
151
|
+
const mode = this.options.capture;
|
|
152
|
+
const type = value === null ? 'null' : typeof value;
|
|
153
|
+
let text;
|
|
154
|
+
let captureError;
|
|
155
|
+
try {
|
|
156
|
+
text = mode === 'none' ? 'not captured'
|
|
157
|
+
: mode === 'formatter' ? this.options.formatter(value, context)
|
|
158
|
+
: mode === 'preview' ? boundedPreview(value, this.options.maxPreviewLength)
|
|
159
|
+
: scalarText(value);
|
|
160
|
+
if (typeof text !== 'string')
|
|
161
|
+
throw new TypeError('Formatter must return a string');
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
text = '[capture failed]';
|
|
165
|
+
captureError = 'Preview capture failed';
|
|
166
|
+
}
|
|
167
|
+
const truncated = text.length > this.options.maxPreviewLength;
|
|
168
|
+
return Object.freeze({ text: text.slice(0, this.options.maxPreviewLength), type, truncated,
|
|
169
|
+
historical: mode !== 'raw', ...(captureError ? { captureError } : {}),
|
|
170
|
+
...(mode === 'raw' ? { raw: value } : {}) });
|
|
171
|
+
}
|
|
172
|
+
enforceBounds() {
|
|
173
|
+
while (this.events.length > this.options.maxEvents) {
|
|
174
|
+
this.events.shift();
|
|
175
|
+
this.evictedEvents += 1;
|
|
176
|
+
}
|
|
177
|
+
while (this.nodes.size > this.options.maxNodes)
|
|
178
|
+
this.evictNode();
|
|
179
|
+
while (this.edges.length > this.options.maxEdges) {
|
|
180
|
+
this.edges.shift();
|
|
181
|
+
this.droppedTopology += 1;
|
|
182
|
+
}
|
|
183
|
+
this.bytes = this.byteSize();
|
|
184
|
+
while (this.bytes > this.options.maxBytes) {
|
|
185
|
+
if (this.events.length) {
|
|
186
|
+
this.events.shift();
|
|
187
|
+
this.evictedEvents += 1;
|
|
188
|
+
}
|
|
189
|
+
else if (this.edges.length) {
|
|
190
|
+
this.edges.shift();
|
|
191
|
+
this.droppedTopology += 1;
|
|
192
|
+
}
|
|
193
|
+
else if (this.nodes.size)
|
|
194
|
+
this.evictNode();
|
|
195
|
+
else
|
|
196
|
+
break;
|
|
197
|
+
this.bytes = this.byteSize();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
evictNode() {
|
|
201
|
+
const id = this.nodes.keys().next().value;
|
|
202
|
+
this.nodes.delete(id);
|
|
203
|
+
const removed = this.edges.filter((edge) => edge.from === id || edge.to === id).length;
|
|
204
|
+
this.edges = this.edges.filter((edge) => edge.from !== id && edge.to !== id);
|
|
205
|
+
this.droppedTopology += 1 + removed;
|
|
206
|
+
}
|
|
207
|
+
byteSize() {
|
|
208
|
+
// UTF-16 upper bound for JSON metadata; excludes deliberately retained raw object heaps.
|
|
209
|
+
const size = (record, serialize) => {
|
|
210
|
+
let bytes = this.recordBytes.get(record);
|
|
211
|
+
if (bytes === undefined) {
|
|
212
|
+
bytes = JSON.stringify(serialize()).length * 2;
|
|
213
|
+
this.recordBytes.set(record, bytes);
|
|
214
|
+
}
|
|
215
|
+
return bytes;
|
|
216
|
+
};
|
|
217
|
+
return this.events.reduce((bytes, record) => bytes + size(record, () => exportEvent(record)), 0)
|
|
218
|
+
+ [...this.nodes.values(), ...this.edges].reduce((bytes, record) => bytes + size(record, () => record), 0);
|
|
219
|
+
}
|
|
220
|
+
changed() {
|
|
221
|
+
this.cached = null;
|
|
222
|
+
if (this.notifying || this.disposed)
|
|
223
|
+
return;
|
|
224
|
+
this.notifying = true;
|
|
225
|
+
queueMicrotask(() => {
|
|
226
|
+
this.notifying = false;
|
|
227
|
+
if (!this.disposed)
|
|
228
|
+
this.revision.set(this.revision.get() + 1);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function scalarText(value) {
|
|
233
|
+
if (value === null)
|
|
234
|
+
return 'null';
|
|
235
|
+
if (typeof value === 'string')
|
|
236
|
+
return JSON.stringify(value);
|
|
237
|
+
if (typeof value === 'object')
|
|
238
|
+
return '[object; contents not captured]';
|
|
239
|
+
if (typeof value === 'function')
|
|
240
|
+
return '[function]';
|
|
241
|
+
return String(value);
|
|
242
|
+
}
|
|
243
|
+
function boundedPreview(value, budget) {
|
|
244
|
+
if (value === null || typeof value !== 'object')
|
|
245
|
+
return scalarText(value);
|
|
246
|
+
const parts = [];
|
|
247
|
+
// This explicitly selected mode reads only descriptors; never invoke a getter.
|
|
248
|
+
for (const key of Reflect.ownKeys(value).slice(0, 8)) {
|
|
249
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
250
|
+
parts.push(`${String(key)}: ${descriptor && 'value' in descriptor ? scalarText(descriptor.value).slice(0, budget) : '[accessor]'}`);
|
|
251
|
+
if (parts.join(', ').length >= budget)
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
return `{${parts.join(', ')}}`;
|
|
255
|
+
}
|
|
256
|
+
function exportPreview(value) {
|
|
257
|
+
if (!value)
|
|
258
|
+
return null;
|
|
259
|
+
const { raw: _raw, ...safe } = value;
|
|
260
|
+
return safe;
|
|
261
|
+
}
|
|
262
|
+
function exportEvent(event) {
|
|
263
|
+
return { ...event, value: exportPreview(event.value), before: exportPreview(event.before),
|
|
264
|
+
error: exportPreview(event.error), inputs: event.inputs.map((input) => ({ ...input, value: exportPreview(input.value) })) };
|
|
265
|
+
}
|
|
266
|
+
function exportable(recording) {
|
|
267
|
+
return { ...recording, events: recording.events.map(exportEvent) };
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=TraceRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TraceRecorder.js","sourceRoot":"","sources":["../../src/model/TraceRecorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Emitter } from '@capillaryjs/capillary';
|
|
2
|
+
export interface TraceSelectionState {
|
|
3
|
+
readonly rootId: string | null;
|
|
4
|
+
readonly eventId: string | null;
|
|
5
|
+
readonly nodeId: string | null;
|
|
6
|
+
readonly sourceId: string | null;
|
|
7
|
+
readonly targetId: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare class TraceSelection {
|
|
10
|
+
readonly state: Emitter<TraceSelectionState, unknown>;
|
|
11
|
+
selectRoot(rootId: string | null): void;
|
|
12
|
+
selectEvent(eventId: string, nodeId: string): void;
|
|
13
|
+
selectNode(nodeId: string): void;
|
|
14
|
+
filter(sourceId: string | null, targetId: string | null): void;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
private patch;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=TraceSelection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TraceSelection.d.ts","sourceRoot":"","sources":["../../src/model/TraceSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAA;AAG9C,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACnC;AAED,qBAAa,cAAc;IACvB,QAAQ,CAAC,KAAK,wCACkE;IAChF,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAGvC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAClD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAChC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAG9D,OAAO,IAAI,IAAI;IACf,OAAO,CAAC,KAAK;CAChB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Emitter } from '@capillaryjs/capillary';
|
|
2
|
+
import { devtoolsDiagnosticScope } from './scope.js';
|
|
3
|
+
export class TraceSelection {
|
|
4
|
+
state = new Emitter({ rootId: null, eventId: null, nodeId: null,
|
|
5
|
+
sourceId: null, targetId: null }, { diagnosticScope: devtoolsDiagnosticScope });
|
|
6
|
+
selectRoot(rootId) {
|
|
7
|
+
this.state.set({ rootId, eventId: rootId, nodeId: null, sourceId: null, targetId: null });
|
|
8
|
+
}
|
|
9
|
+
selectEvent(eventId, nodeId) { this.patch({ eventId, nodeId }); }
|
|
10
|
+
selectNode(nodeId) { this.patch({ nodeId, eventId: null }); }
|
|
11
|
+
filter(sourceId, targetId) {
|
|
12
|
+
this.patch({ sourceId, targetId, nodeId: targetId ?? sourceId, eventId: null });
|
|
13
|
+
}
|
|
14
|
+
dispose() { this.state.dispose(); }
|
|
15
|
+
patch(next) { this.state.set({ ...this.state.get(), ...next }); }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=TraceSelection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TraceSelection.js","sourceRoot":"","sources":["../../src/model/TraceSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAA;AAC9C,OAAO,EAAC,uBAAuB,EAAC,MAAM,YAAY,CAAA;AAUlD,MAAM,OAAO,cAAc;IACd,KAAK,GAAG,IAAI,OAAO,CAAsB,EAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI;QACxF,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAC,EAAE,EAAC,eAAe,EAAE,uBAAuB,EAAC,CAAC,CAAA;IAChF,UAAU,CAAC,MAAqB;QAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAA;IAC3F,CAAC;IACD,WAAW,CAAC,OAAe,EAAE,MAAc,IAAU,IAAI,CAAC,KAAK,CAAC,EAAC,OAAO,EAAE,MAAM,EAAC,CAAC,CAAA,CAAC,CAAC;IACpF,UAAU,CAAC,MAAc,IAAU,IAAI,CAAC,KAAK,CAAC,EAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAA,CAAC,CAAC;IACxE,MAAM,CAAC,QAAuB,EAAE,QAAuB;QACnD,IAAI,CAAC,KAAK,CAAC,EAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC,CAAA;IACjF,CAAC;IACD,OAAO,KAAW,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA,CAAC,CAAC;IAChC,KAAK,CAAC,IAAkC,IAAU,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAC,CAAC,CAAA,CAAC,CAAC;CAC7G"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { TraceRecorder } from './TraceRecorder.js';
|
|
2
|
+
export type { TraceRecorderOptions, TraceCaptureMode, TraceCaptureContext } from './TraceRecorder.js';
|
|
3
|
+
export { TraceSelection } from './TraceSelection.js';
|
|
4
|
+
export type { TraceSelectionState } from './TraceSelection.js';
|
|
5
|
+
export { TracePlayback } from './TracePlayback.js';
|
|
6
|
+
export type { TracePlaybackScheduler, TracePlaybackState } from './TracePlayback.js';
|
|
7
|
+
export { devtoolsDiagnosticScope } from './scope.js';
|
|
8
|
+
export { filterTrace, traceRoots, causalOutline, traceAttempts, projectFlow, nodeValueEvent, captureLimitations } from './projection.js';
|
|
9
|
+
export type { CausalRow, FlowNode, FlowEdge, TraceFlow } from './projection.js';
|
|
10
|
+
export type { TraceEvent, TraceNode, TraceEdge, TraceRecording, TraceFilter, TraceAttempt, ValuePreview } from './types.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAChD,YAAY,EAAC,oBAAoB,EAAE,gBAAgB,EAAE,mBAAmB,EAAC,MAAM,oBAAoB,CAAA;AACnG,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAA;AAClD,YAAY,EAAC,mBAAmB,EAAC,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAChD,YAAY,EAAC,sBAAsB,EAAE,kBAAkB,EAAC,MAAM,oBAAoB,CAAA;AAClF,OAAO,EAAC,uBAAuB,EAAC,MAAM,YAAY,CAAA;AAClD,OAAO,EAAC,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAC,MAAM,iBAAiB,CAAA;AACtI,YAAY,EAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAC,MAAM,iBAAiB,CAAA;AAC7E,YAAY,EAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAC,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { TraceRecorder } from './TraceRecorder.js';
|
|
2
|
+
export { TraceSelection } from './TraceSelection.js';
|
|
3
|
+
export { TracePlayback } from './TracePlayback.js';
|
|
4
|
+
export { devtoolsDiagnosticScope } from './scope.js';
|
|
5
|
+
export { filterTrace, traceRoots, causalOutline, traceAttempts, projectFlow, nodeValueEvent, captureLimitations } from './projection.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAC,uBAAuB,EAAC,MAAM,YAAY,CAAA;AAClD,OAAO,EAAC,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAC,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TraceAttempt, TraceEvent, TraceFilter, TraceNode, TraceRecording } from './types.js';
|
|
2
|
+
export declare function traceRoots(recording: TraceRecording): readonly TraceEvent[];
|
|
3
|
+
/** Intersect occurrence ancestry/descendancy; every converging occurrence is retained. */
|
|
4
|
+
export declare function filterTrace(recording: TraceRecording, filter?: TraceFilter): readonly TraceEvent[];
|
|
5
|
+
export interface CausalRow {
|
|
6
|
+
readonly event: TraceEvent;
|
|
7
|
+
readonly depth: number;
|
|
8
|
+
readonly missingParent: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Last observed state, not a later lifecycle marker's absent payload. */
|
|
11
|
+
export declare function nodeValueEvent(events: readonly TraceEvent[]): TraceEvent | undefined;
|
|
12
|
+
export declare function causalOutline(events: readonly TraceEvent[]): readonly CausalRow[];
|
|
13
|
+
export declare function traceAttempts(events: readonly TraceEvent[]): readonly TraceAttempt[];
|
|
14
|
+
export interface FlowNode {
|
|
15
|
+
readonly node: TraceNode;
|
|
16
|
+
readonly events: readonly TraceEvent[];
|
|
17
|
+
readonly x: number;
|
|
18
|
+
readonly y: number;
|
|
19
|
+
readonly leaf: string | null;
|
|
20
|
+
}
|
|
21
|
+
export interface FlowEdge {
|
|
22
|
+
readonly from: string;
|
|
23
|
+
readonly to: string;
|
|
24
|
+
readonly observed: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface TraceFlow {
|
|
27
|
+
readonly nodes: readonly FlowNode[];
|
|
28
|
+
readonly edges: readonly FlowEdge[];
|
|
29
|
+
readonly width: number;
|
|
30
|
+
readonly height: number;
|
|
31
|
+
}
|
|
32
|
+
/** Stable geometry depends on the complete selected trace, never the replay cursor. */
|
|
33
|
+
export declare function projectFlow(recording: TraceRecording, events: readonly TraceEvent[], options?: {
|
|
34
|
+
includeConnected?: boolean;
|
|
35
|
+
}): TraceFlow;
|
|
36
|
+
export declare function captureLimitations(recording: TraceRecording): readonly string[];
|
|
37
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../src/model/projection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAC,MAAM,YAAY,CAAA;AAEhG,wBAAgB,UAAU,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,UAAU,EAAE,CAG3E;AAED,0FAA0F;AAC1F,wBAAgB,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,MAAM,GAAE,WAAgB,GAAG,SAAS,UAAU,EAAE,CAuBtG;AAED,MAAM,WAAW,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAAC;AAEhH,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,GAAG,SAAS,CAGpF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,SAAS,EAAE,CAkBjF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,YAAY,EAAE,CAcpF;AAED,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AACD,MAAM,WAAW,QAAQ;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAC;AAClG,MAAM,WAAW,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAC;AAEtJ,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,GAAE;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAM,GAAG,SAAS,CAiE3I;AAiCD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,MAAM,EAAE,CAgB/E"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
export function traceRoots(recording) {
|
|
2
|
+
const ids = new Set(recording.events.map((event) => event.id));
|
|
3
|
+
return recording.events.filter((event) => event.parentId === null || !ids.has(event.parentId));
|
|
4
|
+
}
|
|
5
|
+
/** Intersect occurrence ancestry/descendancy; every converging occurrence is retained. */
|
|
6
|
+
export function filterTrace(recording, filter = {}) {
|
|
7
|
+
const all = recording.events;
|
|
8
|
+
const byId = new Map(all.map((event) => [event.id, event]));
|
|
9
|
+
const descendants = (seeds) => {
|
|
10
|
+
const included = new Set(seeds);
|
|
11
|
+
for (const event of all)
|
|
12
|
+
if (event.parentId && included.has(event.parentId))
|
|
13
|
+
included.add(event.id);
|
|
14
|
+
return included;
|
|
15
|
+
};
|
|
16
|
+
const root = filter.rootId ? descendants(new Set([filter.rootId])) : new Set(all.map((event) => event.id));
|
|
17
|
+
const within = all.filter((event) => root.has(event.id));
|
|
18
|
+
const downstream = filter.sourceId
|
|
19
|
+
? descendants(new Set(within.filter((event) => event.nodeId === filter.sourceId).map((event) => event.id))) : root;
|
|
20
|
+
const upstream = filter.targetId ? new Set() : root;
|
|
21
|
+
if (filter.targetId) {
|
|
22
|
+
for (const target of within.filter((event) => event.nodeId === filter.targetId)) {
|
|
23
|
+
let event = target;
|
|
24
|
+
while (event && !upstream.has(event.id)) {
|
|
25
|
+
upstream.add(event.id);
|
|
26
|
+
event = event.parentId ? byId.get(event.parentId) : undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return within.filter((event) => downstream.has(event.id) && upstream.has(event.id));
|
|
31
|
+
}
|
|
32
|
+
/** Last observed state, not a later lifecycle marker's absent payload. */
|
|
33
|
+
export function nodeValueEvent(events) {
|
|
34
|
+
return events.findLast((event) => ['value', 'state', 'derived', 'recomputed-unchanged', 'subscribed'].includes(event.kind))
|
|
35
|
+
?? events.findLast((event) => event.value.type !== 'undefined') ?? events.at(-1);
|
|
36
|
+
}
|
|
37
|
+
export function causalOutline(events) {
|
|
38
|
+
const children = new Map();
|
|
39
|
+
const ids = new Set(events.map((event) => event.id));
|
|
40
|
+
for (const event of events) {
|
|
41
|
+
const key = event.parentId && ids.has(event.parentId) ? event.parentId : '';
|
|
42
|
+
const list = children.get(key) ?? [];
|
|
43
|
+
list.push(event);
|
|
44
|
+
children.set(key, list);
|
|
45
|
+
}
|
|
46
|
+
const result = [];
|
|
47
|
+
const stack = [...(children.get('') ?? [])].reverse().map((event) => ({ event, depth: 0 }));
|
|
48
|
+
while (stack.length) {
|
|
49
|
+
const current = stack.pop();
|
|
50
|
+
result.push({ ...current, missingParent: current.event.parentId !== null && !ids.has(current.event.parentId) });
|
|
51
|
+
for (const child of [...(children.get(current.event.id) ?? [])].reverse()) {
|
|
52
|
+
stack.push({ event: child, depth: current.depth + 1 });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
export function traceAttempts(events) {
|
|
58
|
+
const groups = new Map();
|
|
59
|
+
for (const event of events) {
|
|
60
|
+
if (!event.attemptId)
|
|
61
|
+
continue;
|
|
62
|
+
const group = groups.get(event.attemptId) ?? [];
|
|
63
|
+
group.push(event);
|
|
64
|
+
groups.set(event.attemptId, group);
|
|
65
|
+
}
|
|
66
|
+
return [...groups].map(([id, group]) => {
|
|
67
|
+
const start = group.find((event) => event.outcome === 'started') ?? null;
|
|
68
|
+
const end = group.findLast((event) => event.outcome !== 'started') ?? null;
|
|
69
|
+
return { id, nodeId: group[0].nodeId, start, end,
|
|
70
|
+
status: end?.outcome ?? 'unfinished in capture',
|
|
71
|
+
durationMs: start && end ? Math.max(0, end.timestamp - start.timestamp) : null };
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/** Stable geometry depends on the complete selected trace, never the replay cursor. */
|
|
75
|
+
export function projectFlow(recording, events, options = {}) {
|
|
76
|
+
const nodes = new Map(recording.nodes.map((node) => [node.id, node]));
|
|
77
|
+
const included = new Set(events.map((event) => event.nodeId));
|
|
78
|
+
const byEventId = new Map(recording.events.map((event) => [event.id, event]));
|
|
79
|
+
const edges = new Map();
|
|
80
|
+
for (const event of events) {
|
|
81
|
+
const parent = event.parentId ? byEventId.get(event.parentId) : null;
|
|
82
|
+
if (parent && included.has(parent.nodeId) && parent.nodeId !== event.nodeId) {
|
|
83
|
+
edges.set(`${parent.nodeId}:${event.nodeId}`, { from: parent.nodeId, to: event.nodeId, observed: true });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const first = events[0]?.sequence ?? 0;
|
|
87
|
+
const last = events.at(-1)?.sequence ?? first;
|
|
88
|
+
const possible = recording.edges.filter((edge) => edge.fromSequence <= last
|
|
89
|
+
&& (edge.toSequence === null || edge.toSequence > first));
|
|
90
|
+
// Downstream discovery includes subscribers and emitters that produced no event.
|
|
91
|
+
const queue = options.includeConnected === false ? [] : [...included];
|
|
92
|
+
while (queue.length) {
|
|
93
|
+
const id = queue.shift();
|
|
94
|
+
for (const edge of possible.filter((candidate) => candidate.from === id)) {
|
|
95
|
+
if (!included.has(edge.to)) {
|
|
96
|
+
included.add(edge.to);
|
|
97
|
+
queue.push(edge.to);
|
|
98
|
+
}
|
|
99
|
+
const key = `${edge.from}:${edge.to}`;
|
|
100
|
+
if (!edges.has(key))
|
|
101
|
+
edges.set(key, { ...edge, observed: false });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Full-input provenance is context, not evidence of propagation.
|
|
105
|
+
for (const event of events)
|
|
106
|
+
for (const input of event.inputs) {
|
|
107
|
+
included.add(input.nodeId);
|
|
108
|
+
const key = `${input.nodeId}:${event.nodeId}`;
|
|
109
|
+
if (!edges.has(key) && input.nodeId !== event.nodeId) {
|
|
110
|
+
edges.set(key, { from: input.nodeId, to: event.nodeId, observed: false });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const ids = [...included];
|
|
114
|
+
const links = [...edges.values()];
|
|
115
|
+
// Strongly connected components keep feedback finite, then rank the condensed DAG.
|
|
116
|
+
const components = stronglyConnected(ids, links);
|
|
117
|
+
const groupOf = new Map(components.flatMap((group, index) => group.map((id) => [id, index])));
|
|
118
|
+
const rank = components.map(() => 0);
|
|
119
|
+
for (let pass = 0; pass < components.length; pass++) {
|
|
120
|
+
let changed = false;
|
|
121
|
+
for (const edge of links) {
|
|
122
|
+
const from = groupOf.get(edge.from), to = groupOf.get(edge.to);
|
|
123
|
+
if (from !== to && rank[to] < rank[from] + 1) {
|
|
124
|
+
rank[to] = rank[from] + 1;
|
|
125
|
+
changed = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!changed)
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
const rows = new Map();
|
|
132
|
+
const positioned = ids.map((id) => {
|
|
133
|
+
const column = rank[groupOf.get(id)] ?? 0;
|
|
134
|
+
const row = rows.get(column) ?? 0;
|
|
135
|
+
rows.set(column, row + 1);
|
|
136
|
+
const occurrences = events.filter((event) => event.nodeId === id);
|
|
137
|
+
const node = nodes.get(id) ?? { id, label: 'Node metadata evicted', kind: 'external', scopeId: 'unknown', firstSequence: first, disposedSequence: null };
|
|
138
|
+
const outgoing = links.some((edge) => edge.from === id);
|
|
139
|
+
const leaf = outgoing ? null
|
|
140
|
+
: node.kind === 'component' || node.kind === 'binding' ? 'UI endpoint'
|
|
141
|
+
: node.kind === 'subscriber' ? 'subscription leaf'
|
|
142
|
+
: node.kind === 'emitter' || node.kind === 'derived' ? 'emitter without known subscribers'
|
|
143
|
+
: 'downstream leaf';
|
|
144
|
+
return { node, events: occurrences, x: 20 + column * 260, y: 20 + row * 132, leaf };
|
|
145
|
+
});
|
|
146
|
+
return { nodes: positioned, edges: links,
|
|
147
|
+
width: Math.max(300, ...positioned.map((node) => node.x + 240)),
|
|
148
|
+
height: Math.max(150, ...positioned.map((node) => node.y + 120)) };
|
|
149
|
+
}
|
|
150
|
+
function stronglyConnected(ids, edges) {
|
|
151
|
+
const visited = new Set(), order = [];
|
|
152
|
+
// Iterative DFS avoids call-stack limits for large retained traces.
|
|
153
|
+
const forward = new Map(ids.map((id) => [id, edges.filter((edge) => edge.from === id).map((edge) => edge.to)]));
|
|
154
|
+
const reverse = new Map(ids.map((id) => [id, edges.filter((edge) => edge.to === id).map((edge) => edge.from)]));
|
|
155
|
+
for (const seed of ids) {
|
|
156
|
+
const stack = [[seed, false]];
|
|
157
|
+
while (stack.length) {
|
|
158
|
+
const [id, finish] = stack.pop();
|
|
159
|
+
if (finish) {
|
|
160
|
+
order.push(id);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (visited.has(id))
|
|
164
|
+
continue;
|
|
165
|
+
visited.add(id);
|
|
166
|
+
stack.push([id, true]);
|
|
167
|
+
for (const next of forward.get(id) ?? [])
|
|
168
|
+
if (!visited.has(next))
|
|
169
|
+
stack.push([next, false]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
visited.clear();
|
|
173
|
+
const groups = [];
|
|
174
|
+
for (const seed of order.reverse()) {
|
|
175
|
+
if (visited.has(seed))
|
|
176
|
+
continue;
|
|
177
|
+
const group = [], stack = [seed];
|
|
178
|
+
while (stack.length) {
|
|
179
|
+
const id = stack.pop();
|
|
180
|
+
if (visited.has(id))
|
|
181
|
+
continue;
|
|
182
|
+
visited.add(id);
|
|
183
|
+
group.push(id);
|
|
184
|
+
stack.push(...(reverse.get(id) ?? []));
|
|
185
|
+
}
|
|
186
|
+
groups.push(group);
|
|
187
|
+
}
|
|
188
|
+
return groups;
|
|
189
|
+
}
|
|
190
|
+
export function captureLimitations(recording) {
|
|
191
|
+
const { capture } = recording;
|
|
192
|
+
return [
|
|
193
|
+
...(!capture.topology ? ['Topology capture was disabled; unobserved downstream connections and leaves are unknown.'] : []),
|
|
194
|
+
...(!capture.ui ? ['UI occurrence capture was disabled; component and interaction execution is unknown.'] : []),
|
|
195
|
+
...(!capture.verbose ? ['Unchanged recomputations and full input snapshots were not captured.'] : []),
|
|
196
|
+
...(capture.lateStart ? ['Capture began after the graph may have been created; earlier activity is unknown.'] : []),
|
|
197
|
+
...(capture.gaps ? [`Recording was paused ${capture.gaps} time(s); activity during those gaps is unknown.`] : []),
|
|
198
|
+
...(capture.evictedEvents ? [`${capture.evictedEvents} events evicted by the recording budget.`] : []),
|
|
199
|
+
...(capture.droppedTopology ? [`${capture.droppedTopology} topology records evicted; connected leaves may be missing.`] : []),
|
|
200
|
+
...(recording.events.some((event) => event.parentId && !recording.events.some((parent) => parent.id === event.parentId))
|
|
201
|
+
? ['Some causal parents are outside this capture.'] : []),
|
|
202
|
+
...(traceAttempts(recording.events).some((attempt) => !attempt.end)
|
|
203
|
+
? ['Some attempts are unfinished in this capture; they are not necessarily still running.'] : []),
|
|
204
|
+
...(capture.rawReferences ? ['Raw references are mutable, are omitted from JSON export, and their object heaps are outside the byte budget.'] : []),
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=projection.js.map
|