or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-management.mdindex.mdplugin-orchestration.mdruntime-inspection.mdtimer-coordination.md

runtime-inspection.mddocs/

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

```