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

plugin-orchestration.mddocs/

0

# Plugin Orchestration

1

2

Central orchestration system providing unified access to containers, clocks, and debugging capabilities. The Ctx class serves as the main interface for plugin development, state coordination, and timer management within the Milkdown ecosystem.

3

4

## Capabilities

5

6

### Ctx Class

7

8

Main orchestration class that coordinates access to containers, clocks, and provides debugging capabilities. Essential for plugin development and managing the complete context lifecycle.

9

10

```typescript { .api }

11

/**

12

* Ctx provides unified access to containers, clocks, and debugging tools

13

*/

14

class Ctx {

15

/** Metadata associated with this context (readonly) */

16

readonly meta: Meta | undefined;

17

/** Inspector instance for debugging and telemetry (readonly) */

18

readonly inspector: Inspector | undefined;

19

20

/** Create a ctx object with container, clock, and optional metadata */

21

constructor(container: Container, clock: Clock, meta?: Meta);

22

23

/** Create a new ctx with additional metadata, returns same instance if empty meta */

24

readonly produce: (meta?: Meta) => Ctx;

25

26

/** Add a slice into the ctx with optional initial value */

27

readonly inject: <T>(sliceType: SliceType<T>, value?: T) => Ctx;

28

/** Remove a slice from the ctx by type or name */

29

readonly remove: <T, N extends string = string>(sliceType: SliceType<T, N> | N) => Ctx;

30

/** Check if the ctx has a slice by type or name */

31

readonly isInjected: <T, N extends string = string>(sliceType: SliceType<T, N> | N) => boolean;

32

33

/** Get a slice instance from the ctx */

34

readonly use: <T, N extends string = string>(sliceType: SliceType<T, N> | N) => Slice<T, N>;

35

/** Get a slice value from the ctx */

36

readonly get: <T, N extends string>(sliceType: SliceType<T, N> | N) => T;

37

/** Set a slice value in the ctx */

38

readonly set: <T, N extends string>(sliceType: SliceType<T, N> | N, value: T) => void;

39

/** Update a slice value using a callback function */

40

readonly update: <T, N extends string>(sliceType: SliceType<T, N> | N, updater: (prev: T) => T) => void;

41

42

/** Add a timer into the ctx */

43

readonly record: (timerType: TimerType) => Ctx;

44

/** Remove a timer from the ctx */

45

readonly clearTimer: (timerType: TimerType) => Ctx;

46

/** Check if the ctx has a timer */

47

readonly isRecorded: (timerType: TimerType) => boolean;

48

/** Get a timer instance from the ctx */

49

readonly timer: (timer: TimerType) => Timer;

50

/** Resolve a timer */

51

readonly done: (timer: TimerType) => void;

52

/** Start a timer and return its promise */

53

readonly wait: (timer: TimerType) => Promise<void>;

54

/** Wait for an array of timers stored in a slice */

55

readonly waitTimers: (slice: SliceType<TimerType[]>) => Promise<void>;

56

}

57

```

58

59

**Usage Examples:**

60

61

```typescript

62

import { Container, Clock, Ctx, createSlice, createTimer } from "@milkdown/ctx";

63

64

// Setup context

65

const container = new Container();

66

const clock = new Clock();

67

const ctx = new Ctx(container, clock);

68

69

// Slice management

70

const counterSlice = createSlice(0, "counter");

71

ctx.inject(counterSlice, 10);

72

73

console.log(ctx.get(counterSlice)); // 10

74

ctx.update(counterSlice, (prev) => prev + 5);

75

console.log(ctx.get(counterSlice)); // 15

76

77

// Timer management

78

const loadTimer = createTimer("load-data", 5000);

79

ctx.record(loadTimer);

80

81

// Simulate async operation

82

setTimeout(() => ctx.done(loadTimer), 2000);

83

84

// Wait for timer

85

ctx.wait(loadTimer).then(() => {

86

console.log("Data loaded!");

87

});

88

```

89

90

### Advanced Context Operations

91

92

**Metadata and Inspector Integration:**

93

94

```typescript

95

import { Ctx, Container, Clock, type Meta } from "@milkdown/ctx";

96

97

const meta: Meta = {

98

displayName: "Data Plugin",

99

description: "Handles data processing",

100

package: "@example/data-plugin",

101

group: "Core"

102

};

103

104

const ctx = new Ctx(new Container(), new Clock(), meta);

105

106

// Create contexts with different metadata

107

const apiCtx = ctx.produce({

108

displayName: "API Handler",

109

package: "@example/api",

110

additional: { endpoint: "/api/v1" }

111

});

112

113

// Access inspector for debugging

114

if (ctx.inspector) {

115

const telemetry = ctx.inspector.read();

116

console.log("Plugin metadata:", telemetry.metadata);

117

console.log("Injected slices:", telemetry.injectedSlices);

118

}

119

```

120

121

**Complex Timer Coordination:**

122

123

```typescript

124

import { Ctx, Container, Clock, createSlice, createTimer, TimerType } from "@milkdown/ctx";

125

126

// Setup context and timers

127

const container = new Container();

128

const clock = new Clock();

129

const ctx = new Ctx(container, clock);

130

const initTimer = createTimer("init", 3000);

131

const loadTimer = createTimer("load", 5000);

132

const renderTimer = createTimer("render", 2000);

133

134

// Create slice to hold timer array

135

const pipelineTimers = createSlice<TimerType[]>([], "pipeline");

136

ctx.inject(pipelineTimers, [initTimer, loadTimer, renderTimer]);

137

138

// Record all timers

139

[initTimer, loadTimer, renderTimer].forEach(timer => ctx.record(timer));

140

141

// Simulate pipeline completion

142

setTimeout(() => ctx.done(initTimer), 1000);

143

setTimeout(() => ctx.done(loadTimer), 2000);

144

setTimeout(() => ctx.done(renderTimer), 3000);

145

146

// Wait for entire pipeline

147

ctx.waitTimers(pipelineTimers).then(() => {

148

console.log("Pipeline completed!");

149

});

150

```

151

152

### MilkdownPlugin Type

153

154

Type definition for Milkdown plugins with optional metadata and lifecycle support.

155

156

```typescript { .api }

157

/**

158

* Plugin function type with optional metadata and lifecycle management

159

*/

160

type MilkdownPlugin = { meta?: Meta } & ((ctx: Ctx) => CtxRunner);

161

162

type CtxRunner = () => RunnerReturnType;

163

type RunnerReturnType = void | Promise<void> | Cleanup | Promise<Cleanup>;

164

type Cleanup = () => void | Promise<void>;

165

```

166

167

**Usage Examples:**

168

169

```typescript

170

import { type MilkdownPlugin, type Meta } from "@milkdown/ctx";

171

172

// Simple plugin

173

const simplePlugin: MilkdownPlugin = (ctx) => {

174

// Setup phase

175

const dataSlice = createSlice([], "plugin-data");

176

ctx.inject(dataSlice);

177

178

return () => {

179

// Run phase

180

console.log("Plugin running");

181

};

182

};

183

184

// Full lifecycle plugin with metadata

185

const fullPlugin: MilkdownPlugin = Object.assign(

186

(ctx) => {

187

// Setup phase

188

const configSlice = createSlice({ enabled: true }, "config");

189

ctx.inject(configSlice);

190

191

return async () => {

192

// Run phase

193

console.log("Async plugin running");

194

195

return async () => {

196

// Cleanup phase

197

console.log("Plugin cleaning up");

198

ctx.remove(configSlice);

199

};

200

};

201

},

202

{

203

meta: {

204

displayName: "Full Lifecycle Plugin",

205

description: "Demonstrates complete plugin lifecycle",

206

package: "@example/full-plugin",

207

group: "Examples"

208

} as Meta

209

}

210

);

211

212

// Plugin with error handling

213

const robustPlugin: MilkdownPlugin = (ctx) => {

214

const errorSlice = createSlice<Error | null>(null, "errors");

215

ctx.inject(errorSlice);

216

217

return () => {

218

try {

219

// Plugin logic

220

console.log("Plugin executing");

221

} catch (error) {

222

ctx.set(errorSlice, error as Error);

223

console.error("Plugin error:", error);

224

}

225

226

return () => {

227

// Cleanup

228

if (ctx.isInjected(errorSlice)) {

229

ctx.remove(errorSlice);

230

}

231

};

232

};

233

};

234

```

235

236

## Integration Patterns

237

238

### Plugin Composition

239

240

```typescript

241

import { Ctx, Container, Clock, type MilkdownPlugin } from "@milkdown/ctx";

242

243

// Create shared context

244

const sharedCtx = new Ctx(new Container(), new Clock());

245

246

// Plugin that provides services

247

const servicePlugin: MilkdownPlugin = (ctx) => {

248

const apiService = createSlice({ baseUrl: "/api" }, "api-service");

249

ctx.inject(apiService);

250

251

return () => {

252

console.log("Service plugin initialized");

253

};

254

};

255

256

// Plugin that consumes services

257

const consumerPlugin: MilkdownPlugin = (ctx) => {

258

return () => {

259

if (ctx.isInjected("api-service")) {

260

const service = ctx.get("api-service");

261

console.log("Using API service:", service.baseUrl);

262

}

263

};

264

};

265

266

// Initialize plugins in order

267

const serviceRunner = servicePlugin(sharedCtx);

268

const consumerRunner = consumerPlugin(sharedCtx);

269

270

serviceRunner();

271

consumerRunner();

272

```

273

274

### State Synchronization

275

276

```typescript

277

// Plugin that manages shared state

278

const stateManagerPlugin: MilkdownPlugin = (ctx) => {

279

const appState = createSlice({

280

theme: "light",

281

language: "en"

282

}, "app-state");

283

284

ctx.inject(appState);

285

286

// Watch for state changes

287

const stateSlice = ctx.use(appState);

288

stateSlice.on((newState) => {

289

console.log("App state changed:", newState);

290

// Persist to localStorage, sync with server, etc.

291

});

292

293

return () => {

294

console.log("State manager ready");

295

};

296

};

297

```