0
# Runtime Inspection
1
2
Debugging and telemetry system for monitoring ctx operations, slice usage, and timer execution. Provides comprehensive runtime analysis capabilities for development and production environments through the Inspector class and structured telemetry data.
3
4
## Capabilities
5
6
### Inspector Class
7
8
Runtime debugging and telemetry collector that monitors all ctx operations including slice injection/consumption and timer execution with duration tracking.
9
10
```typescript { .api }
11
/**
12
* Inspector provides runtime debugging and telemetry collection for ctx operations
13
*/
14
class Inspector {
15
/** Create an inspector with container, clock, and metadata references */
16
constructor(container: Container, clock: Clock, meta: Meta);
17
18
/** Read current telemetry data as a structured object */
19
read(): Telemetry;
20
}
21
```
22
23
**Usage Examples:**
24
25
```typescript
26
import {
27
Container,
28
Clock,
29
Ctx,
30
Inspector,
31
createSlice,
32
createTimer,
33
type Meta
34
} from "@milkdown/ctx";
35
36
// Setup context with metadata for inspection
37
const meta: Meta = {
38
displayName: "Data Processing Plugin",
39
description: "Handles data transformation and caching",
40
package: "@example/data-plugin",
41
group: "Core",
42
additional: { version: "1.2.0", author: "team@example.com" }
43
};
44
45
const container = new Container();
46
const clock = new Clock();
47
const ctx = new Ctx(container, clock, meta);
48
49
// Perform some operations
50
const dataSlice = createSlice([], "processed-data");
51
const cacheSlice = createSlice({}, "cache");
52
const loadTimer = createTimer("load-data", 3000);
53
54
ctx.inject(dataSlice, [1, 2, 3]);
55
ctx.inject(cacheSlice, { key1: "value1" });
56
ctx.record(loadTimer);
57
58
// Access inspector
59
if (ctx.inspector) {
60
const telemetry = ctx.inspector.read();
61
62
console.log("Plugin metadata:", telemetry.metadata);
63
console.log("Injected slices:", telemetry.injectedSlices);
64
console.log("Recorded timers:", telemetry.recordedTimers);
65
}
66
```
67
68
### Meta Interface
69
70
Plugin metadata structure for debugging and identification purposes.
71
72
```typescript { .api }
73
/**
74
* Meta interface defines plugin metadata for debugging and identification
75
*/
76
interface Meta {
77
/** Human-readable plugin name */
78
displayName: string;
79
/** Optional plugin description */
80
description?: string;
81
/** Package that the plugin belongs to */
82
package: string;
83
/** Optional plugin group (internal plugins use "System") */
84
group?: string;
85
/** Optional additional metadata as key-value pairs */
86
additional?: Record<string, any>;
87
}
88
```
89
90
**Usage Examples:**
91
92
```typescript
93
import { type Meta } from "@milkdown/ctx";
94
95
// Basic metadata
96
const basicMeta: Meta = {
97
displayName: "Simple Plugin",
98
package: "@example/simple"
99
};
100
101
// Complete metadata
102
const completeMeta: Meta = {
103
displayName: "Advanced Data Plugin",
104
description: "Provides advanced data processing capabilities",
105
package: "@example/advanced-data",
106
group: "Data Processing",
107
additional: {
108
version: "2.1.0",
109
author: "Jane Smith",
110
license: "MIT",
111
dependencies: ["lodash", "moment"],
112
experimental: true
113
}
114
};
115
116
// System plugin metadata
117
const systemMeta: Meta = {
118
displayName: "Core Timer System",
119
description: "Manages internal timing operations",
120
package: "@milkdown/core",
121
group: "System",
122
additional: {
123
internal: true,
124
priority: "high"
125
}
126
};
127
```
128
129
### Telemetry Interface
130
131
Structured runtime telemetry data containing comprehensive information about plugin execution, slice usage, and timer performance.
132
133
```typescript { .api }
134
/**
135
* Telemetry interface provides structured runtime data for analysis
136
*/
137
interface Telemetry {
138
/** Plugin metadata for identification */
139
metadata: Meta;
140
/** Array of injected slices with their current values */
141
injectedSlices: { name: string; value: unknown }[];
142
/** Array of consumed slices with their current values */
143
consumedSlices: { name: string; value: unknown }[];
144
/** Array of recorded timers with duration and status information */
145
recordedTimers: { name: string; duration: number; status: TimerStatus }[];
146
/** Array of waited timers with duration and status information */
147
waitTimers: { name: string; duration: number; status: TimerStatus }[];
148
}
149
```
150
151
**Usage Examples:**
152
153
```typescript
154
import { Ctx, Container, Clock, createSlice, createTimer, type Meta } from "@milkdown/ctx";
155
156
async function demonstrateTelemetry() {
157
const meta: Meta = {
158
displayName: "Telemetry Demo",
159
package: "@example/telemetry-demo"
160
};
161
162
const ctx = new Ctx(new Container(), new Clock(), meta);
163
164
// Setup slices and timers
165
const configSlice = createSlice({ theme: "dark" }, "config");
166
const dataSlice = createSlice<number[]>([], "data");
167
const initTimer = createTimer("initialization", 2000);
168
const loadTimer = createTimer("data-load", 5000);
169
170
// Inject slices
171
ctx.inject(configSlice, { theme: "light", lang: "en" });
172
ctx.inject(dataSlice, [1, 2, 3, 4, 5]);
173
174
// Record timers
175
ctx.record(initTimer);
176
ctx.record(loadTimer);
177
178
// Use slices (marks them as consumed)
179
const config = ctx.get(configSlice);
180
const data = ctx.get(dataSlice);
181
182
// Start timers
183
const initPromise = ctx.wait(initTimer);
184
const loadPromise = ctx.wait(loadTimer);
185
186
// Simulate completion
187
setTimeout(() => ctx.done(initTimer), 1000);
188
setTimeout(() => ctx.done(loadTimer), 2500);
189
190
// Wait for completion
191
await Promise.all([initPromise, loadPromise]);
192
193
// Read telemetry
194
if (ctx.inspector) {
195
const telemetry = ctx.inspector.read();
196
197
console.log("=== Telemetry Report ===");
198
console.log("Plugin:", telemetry.metadata.displayName);
199
console.log("Package:", telemetry.metadata.package);
200
201
console.log("\nInjected Slices:");
202
telemetry.injectedSlices.forEach(slice => {
203
console.log(`- ${slice.name}:`, slice.value);
204
});
205
206
console.log("\nConsumed Slices:");
207
telemetry.consumedSlices.forEach(slice => {
208
console.log(`- ${slice.name}:`, slice.value);
209
});
210
211
console.log("\nRecorded Timers:");
212
telemetry.recordedTimers.forEach(timer => {
213
console.log(`- ${timer.name}: ${timer.duration}ms (${timer.status})`);
214
});
215
216
console.log("\nWait Timers:");
217
telemetry.waitTimers.forEach(timer => {
218
console.log(`- ${timer.name}: ${timer.duration}ms (${timer.status})`);
219
});
220
}
221
}
222
```
223
224
## Advanced Inspection Patterns
225
226
### Performance Monitoring
227
228
```typescript
229
import { Ctx, Container, Clock, createTimer, type Meta } from "@milkdown/ctx";
230
231
class PerformanceMonitor {
232
private ctx: Ctx;
233
234
constructor(pluginName: string) {
235
const meta: Meta = {
236
displayName: `${pluginName} Performance Monitor`,
237
package: "@monitoring/performance",
238
group: "System",
239
additional: {
240
monitoringEnabled: true,
241
startTime: Date.now()
242
}
243
};
244
245
this.ctx = new Ctx(new Container(), new Clock(), meta);
246
}
247
248
async measureOperation(name: string, operation: () => Promise<void>) {
249
const timer = createTimer(`perf-${name}`, 30000);
250
this.ctx.record(timer);
251
252
const start = performance.now();
253
const promise = this.ctx.wait(timer);
254
255
try {
256
await operation();
257
this.ctx.done(timer);
258
await promise;
259
260
const duration = performance.now() - start;
261
console.log(`Operation ${name} completed in ${duration.toFixed(2)}ms`);
262
263
} catch (error) {
264
console.error(`Operation ${name} failed:`, error);
265
throw error;
266
}
267
}
268
269
getReport() {
270
if (!this.ctx.inspector) return null;
271
272
const telemetry = this.ctx.inspector.read();
273
return {
274
plugin: telemetry.metadata.displayName,
275
totalOperations: telemetry.waitTimers.length,
276
averageDuration: telemetry.waitTimers.reduce((sum, timer) =>
277
sum + timer.duration, 0) / telemetry.waitTimers.length,
278
failedOperations: telemetry.waitTimers.filter(timer =>
279
timer.status === 'rejected').length
280
};
281
}
282
}
283
```
284
285
### Development Debugging
286
287
```typescript
288
import { Ctx, Container, Clock, createSlice, type Meta } from "@milkdown/ctx";
289
290
class DebugContext {
291
private ctx: Ctx;
292
private debugEnabled: boolean;
293
294
constructor(debugEnabled = false) {
295
this.debugEnabled = debugEnabled;
296
297
const meta: Meta = {
298
displayName: "Debug Context",
299
package: "@debug/context",
300
group: "Development",
301
additional: {
302
debugEnabled,
303
environment: process.env.NODE_ENV || "development"
304
}
305
};
306
307
this.ctx = new Ctx(new Container(), new Clock(), meta);
308
}
309
310
injectSlice<T>(sliceType: SliceType<T>, value?: T) {
311
this.ctx.inject(sliceType, value);
312
313
if (this.debugEnabled) {
314
console.log(`π§ Injected slice: ${sliceType.name}`, value);
315
this.logTelemetry();
316
}
317
318
return this;
319
}
320
321
useSlice<T>(sliceType: SliceType<T>) {
322
const result = this.ctx.use(sliceType);
323
324
if (this.debugEnabled) {
325
console.log(`π Using slice: ${sliceType.name}`, result.get());
326
}
327
328
return result;
329
}
330
331
private logTelemetry() {
332
if (!this.ctx.inspector || !this.debugEnabled) return;
333
334
const telemetry = this.ctx.inspector.read();
335
console.log("π Current telemetry:", {
336
injectedSlices: telemetry.injectedSlices.length,
337
consumedSlices: telemetry.consumedSlices.length,
338
activeTimers: telemetry.recordedTimers.filter(t => t.status === 'pending').length
339
});
340
}
341
342
getContext() {
343
return this.ctx;
344
}
345
}
346
347
// Usage
348
const debugCtx = new DebugContext(true);
349
const userSlice = createSlice({ name: "Anonymous" }, "user");
350
351
debugCtx
352
.injectSlice(userSlice, { name: "Alice" })
353
.useSlice(userSlice);
354
```
355
356
### Production Telemetry Collection
357
358
```typescript
359
import { Inspector, type Telemetry, type Meta } from "@milkdown/ctx";
360
361
class TelemetryCollector {
362
private telemetryData: Telemetry[] = [];
363
364
collectFromInspector(inspector: Inspector) {
365
const telemetry = inspector.read();
366
this.telemetryData.push({
367
...telemetry,
368
metadata: {
369
...telemetry.metadata,
370
additional: {
371
...telemetry.metadata.additional,
372
collectedAt: new Date().toISOString()
373
}
374
}
375
});
376
}
377
378
generateReport() {
379
const report = {
380
totalPlugins: this.telemetryData.length,
381
totalSlices: this.telemetryData.reduce((sum, t) =>
382
sum + t.injectedSlices.length, 0),
383
totalTimers: this.telemetryData.reduce((sum, t) =>
384
sum + t.recordedTimers.length, 0),
385
averageTimerDuration: this.calculateAverageTimerDuration(),
386
pluginsByGroup: this.groupPluginsByGroup(),
387
performanceMetrics: this.calculatePerformanceMetrics()
388
};
389
390
return report;
391
}
392
393
private calculateAverageTimerDuration(): number {
394
const allTimers = this.telemetryData.flatMap(t => t.waitTimers);
395
if (allTimers.length === 0) return 0;
396
397
return allTimers.reduce((sum, timer) => sum + timer.duration, 0) / allTimers.length;
398
}
399
400
private groupPluginsByGroup() {
401
const groups: Record<string, number> = {};
402
403
this.telemetryData.forEach(telemetry => {
404
const group = telemetry.metadata.group || "Ungrouped";
405
groups[group] = (groups[group] || 0) + 1;
406
});
407
408
return groups;
409
}
410
411
private calculatePerformanceMetrics() {
412
const allTimers = this.telemetryData.flatMap(t => t.waitTimers);
413
414
return {
415
totalTimers: allTimers.length,
416
successfulTimers: allTimers.filter(t => t.status === 'resolved').length,
417
failedTimers: allTimers.filter(t => t.status === 'rejected').length,
418
timeoutTimers: allTimers.filter(t => t.status === 'rejected').length,
419
averageDuration: this.calculateAverageTimerDuration(),
420
maxDuration: Math.max(...allTimers.map(t => t.duration), 0),
421
minDuration: Math.min(...allTimers.map(t => t.duration), 0)
422
};
423
}
424
425
exportToJson(): string {
426
return JSON.stringify({
427
generatedAt: new Date().toISOString(),
428
summary: this.generateReport(),
429
rawData: this.telemetryData
430
}, null, 2);
431
}
432
}
433
```
434
435
## Integration with Ctx
436
437
The Inspector is automatically created when a Ctx is instantiated with metadata:
438
439
```typescript
440
import { Ctx, Container, Clock, type Meta } from "@milkdown/ctx";
441
442
const meta: Meta = {
443
displayName: "Auto Inspector Plugin",
444
package: "@example/auto-inspector"
445
};
446
447
const ctx = new Ctx(new Container(), new Clock(), meta);
448
449
// Inspector is automatically available
450
console.log("Inspector available:", !!ctx.inspector); // true
451
452
// Without metadata, no inspector is created
453
const ctxWithoutMeta = new Ctx(new Container(), new Clock());
454
console.log("Inspector available:", !!ctxWithoutMeta.inspector); // false
455
```