or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-helpers.mdcomposition-api.mddevelopment-tools.mdindex.mdmodule-system.mdstore-management.md

development-tools.mddocs/

0

# Development Tools

1

2

Development and debugging features including logging plugin, DevTools integration, and state subscription mechanisms.

3

4

## Capabilities

5

6

### Logger Plugin

7

8

Development plugin for logging store mutations and actions to the console.

9

10

```typescript { .api }

11

/**

12

* Create a logger plugin for development debugging

13

* @param options - Logger configuration options

14

* @returns Plugin function for store

15

*/

16

function createLogger<S>(options?: LoggerOption<S>): Plugin<S>;

17

18

interface LoggerOption<S> {

19

/** Whether to collapse log groups (default: true) */

20

collapsed?: boolean;

21

/** Filter function to determine which mutations to log */

22

filter?: <P extends Payload>(mutation: P, stateBefore: S, stateAfter: S) => boolean;

23

/** Transform state before logging */

24

transformer?: (state: S) => any;

25

/** Transform mutations before logging */

26

mutationTransformer?: <P extends Payload>(mutation: P) => any;

27

/** Filter function to determine which actions to log */

28

actionFilter?: <P extends Payload>(action: P, state: S) => boolean;

29

/** Transform actions before logging */

30

actionTransformer?: <P extends Payload>(action: P) => any;

31

/** Whether to log mutations (default: true) */

32

logMutations?: boolean;

33

/** Whether to log actions (default: true) */

34

logActions?: boolean;

35

/** Custom logger object (default: console) */

36

logger?: Logger;

37

}

38

39

interface Logger extends Partial<Pick<Console, 'groupCollapsed' | 'group' | 'groupEnd'>> {

40

log(message: string, color: string, payload: any): void;

41

log(message: string): void;

42

}

43

44

interface Payload {

45

type: string;

46

}

47

48

type Plugin<S> = (store: Store<S>) => any;

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

import { createStore, createLogger } from 'vuex';

55

56

// Basic logger setup

57

const store = createStore({

58

state: { count: 0 },

59

mutations: {

60

increment(state) { state.count++; }

61

},

62

plugins: process.env.NODE_ENV !== 'production'

63

? [createLogger()]

64

: []

65

});

66

67

// Advanced logger configuration

68

const loggerPlugin = createLogger({

69

collapsed: false, // Don't collapse log groups

70

71

// Only log mutations that change important state

72

filter: (mutation, stateBefore, stateAfter) => {

73

return mutation.type !== 'UPDATE_SCROLL_POSITION';

74

},

75

76

// Transform state to hide sensitive data

77

transformer: (state) => ({

78

...state,

79

user: state.user ? { ...state.user, password: '[HIDDEN]' } : null

80

}),

81

82

// Format mutation display

83

mutationTransformer: (mutation) => ({

84

type: mutation.type,

85

payload: mutation.type.includes('PASSWORD')

86

? '[HIDDEN]'

87

: mutation.payload

88

}),

89

90

// Only log specific actions

91

actionFilter: (action, state) => {

92

return !action.type.startsWith('analytics/');

93

},

94

95

// Custom action formatting

96

actionTransformer: (action) => ({

97

...action,

98

timestamp: Date.now()

99

}),

100

101

// Disable action logging in production

102

logActions: process.env.NODE_ENV !== 'production',

103

104

// Custom logger

105

logger: {

106

log: console.log,

107

groupCollapsed: console.groupCollapsed,

108

groupEnd: console.groupEnd

109

}

110

});

111

112

const store = createStore({

113

// ... store config

114

plugins: [loggerPlugin]

115

});

116

```

117

118

### State Subscription

119

120

Subscribe to mutations and actions for debugging and monitoring.

121

122

```typescript { .api }

123

/**

124

* Subscribe to store mutations

125

* @param fn - Callback function called on each mutation

126

* @param options - Subscription options

127

* @returns Unsubscribe function

128

*/

129

subscribe<P extends MutationPayload>(

130

fn: (mutation: P, state: S) => any,

131

options?: SubscribeOptions

132

): () => void;

133

134

/**

135

* Subscribe to store actions

136

* @param fn - Callback function or options object

137

* @param options - Subscription options

138

* @returns Unsubscribe function

139

*/

140

subscribeAction<P extends ActionPayload>(

141

fn: SubscribeActionOptions<P, S>,

142

options?: SubscribeOptions

143

): () => void;

144

145

interface MutationPayload extends Payload {

146

payload: any;

147

}

148

149

interface ActionPayload extends Payload {

150

payload: any;

151

}

152

153

interface SubscribeOptions {

154

/** Add subscriber to beginning of list */

155

prepend?: boolean;

156

}

157

158

type ActionSubscriber<P, S> = (action: P, state: S) => any;

159

type ActionErrorSubscriber<P, S> = (action: P, state: S, error: Error) => any;

160

161

interface ActionSubscribersObject<P, S> {

162

before?: ActionSubscriber<P, S>;

163

after?: ActionSubscriber<P, S>;

164

error?: ActionErrorSubscriber<P, S>;

165

}

166

167

type SubscribeActionOptions<P, S> = ActionSubscriber<P, S> | ActionSubscribersObject<P, S>;

168

```

169

170

**Usage Examples:**

171

172

```typescript

173

import { createStore } from 'vuex';

174

175

const store = createStore({

176

state: { count: 0, logs: [] },

177

mutations: {

178

increment(state) { state.count++; },

179

addLog(state, message) { state.logs.push(message); }

180

},

181

actions: {

182

async fetchData({ commit }) {

183

const data = await api.getData();

184

commit('setData', data);

185

}

186

}

187

});

188

189

// Subscribe to mutations

190

const unsubscribeMutations = store.subscribe((mutation, state) => {

191

console.log('Mutation:', mutation.type);

192

console.log('Payload:', mutation.payload);

193

console.log('New state:', state);

194

195

// Log mutations to server

196

analytics.track('store_mutation', {

197

type: mutation.type,

198

payload: mutation.payload

199

});

200

});

201

202

// Subscribe to actions with lifecycle hooks

203

const unsubscribeActions = store.subscribeAction({

204

before: (action, state) => {

205

console.log(`Before action ${action.type}`, action.payload);

206

// Start performance timing

207

performance.mark(`action-${action.type}-start`);

208

},

209

210

after: (action, state) => {

211

console.log(`After action ${action.type}`, state);

212

// End performance timing

213

performance.mark(`action-${action.type}-end`);

214

performance.measure(

215

`action-${action.type}`,

216

`action-${action.type}-start`,

217

`action-${action.type}-end`

218

);

219

},

220

221

error: (action, state, error) => {

222

console.error(`Action ${action.type} failed:`, error);

223

// Log errors to monitoring service

224

errorReporting.captureException(error, {

225

action: action.type,

226

payload: action.payload,

227

state

228

});

229

}

230

});

231

232

// Subscribe with prepend option

233

const debugSubscriber = store.subscribe((mutation, state) => {

234

// This will be called first due to prepend: true

235

console.log('Debug:', mutation);

236

}, { prepend: true });

237

238

// Cleanup subscriptions

239

const cleanup = () => {

240

unsubscribeMutations();

241

unsubscribeActions();

242

debugSubscriber();

243

};

244

245

// Auto-cleanup on app unmount

246

window.addEventListener('beforeunload', cleanup);

247

```

248

249

### State Watching

250

251

Watch specific parts of the store state for changes.

252

253

```typescript { .api }

254

/**

255

* Watch a computed value derived from store state

256

* @param getter - Function that returns the value to watch

257

* @param cb - Callback function called when value changes

258

* @param options - Watch options (Vue WatchOptions)

259

* @returns Unwatch function

260

*/

261

watch<T>(

262

getter: (state: S, getters: any) => T,

263

cb: (value: T, oldValue: T) => void,

264

options?: WatchOptions

265

): () => void;

266

267

// Note: WatchOptions is imported from Vue and includes:

268

// { deep?: boolean; immediate?: boolean; flush?: 'pre' | 'post' | 'sync' }

269

```

270

271

**Usage Examples:**

272

273

```typescript

274

import { createStore } from 'vuex';

275

276

const store = createStore({

277

state: {

278

user: null,

279

cart: { items: [] },

280

ui: { theme: 'light' }

281

},

282

getters: {

283

cartTotal: state => state.cart.items.reduce((sum, item) =>

284

sum + item.price * item.quantity, 0),

285

itemCount: state => state.cart.items.length

286

}

287

});

288

289

// Watch specific state property

290

const unwatchUser = store.watch(

291

(state) => state.user,

292

(newUser, oldUser) => {

293

console.log('User changed:', { newUser, oldUser });

294

295

if (newUser && !oldUser) {

296

// User logged in

297

analytics.track('user_login', { userId: newUser.id });

298

} else if (!newUser && oldUser) {

299

// User logged out

300

analytics.track('user_logout', { userId: oldUser.id });

301

}

302

},

303

{ deep: true } // Watch object properties

304

);

305

306

// Watch computed getter

307

const unwatchCartTotal = store.watch(

308

(state, getters) => getters.cartTotal,

309

(newTotal, oldTotal) => {

310

console.log(`Cart total changed: ${oldTotal} -> ${newTotal}`);

311

312

// Update localStorage

313

localStorage.setItem('cartTotal', newTotal.toString());

314

315

// Show notification for significant changes

316

if (newTotal - oldTotal > 100) {

317

showNotification('Large price change detected!');

318

}

319

}

320

);

321

322

// Watch multiple values

323

const unwatchMultiple = store.watch(

324

(state, getters) => ({

325

user: state.user,

326

itemCount: getters.itemCount,

327

theme: state.ui.theme

328

}),

329

(newValues, oldValues) => {

330

// React to changes in any watched value

331

console.log('Multiple values changed:', { newValues, oldValues });

332

},

333

{ deep: true }

334

);

335

336

// Watch with immediate option

337

const unwatchImmediate = store.watch(

338

(state) => state.ui.theme,

339

(theme) => {

340

document.body.className = `theme-${theme}`;

341

},

342

{ immediate: true } // Call immediately with current value

343

);

344

345

// Conditional watching

346

let themeWatcher = null;

347

348

const startThemeWatching = () => {

349

if (!themeWatcher) {

350

themeWatcher = store.watch(

351

(state) => state.ui.theme,

352

(theme) => applyTheme(theme)

353

);

354

}

355

};

356

357

const stopThemeWatching = () => {

358

if (themeWatcher) {

359

themeWatcher();

360

themeWatcher = null;

361

}

362

};

363

364

// Cleanup all watchers

365

const cleanup = () => {

366

unwatchUser();

367

unwatchCartTotal();

368

unwatchMultiple();

369

unwatchImmediate();

370

stopThemeWatching();

371

};

372

```

373

374

### DevTools Integration

375

376

Integration with Vue DevTools browser extension for debugging.

377

378

**Usage Examples:**

379

380

```typescript

381

import { createApp } from 'vue';

382

import { createStore } from 'vuex';

383

384

// DevTools is automatically enabled in development

385

const store = createStore({

386

state: { count: 0 },

387

mutations: {

388

increment(state) { state.count++; }

389

},

390

// DevTools enabled by default in development

391

devtools: process.env.NODE_ENV !== 'production'

392

});

393

394

// Explicit DevTools control

395

const store = createStore({

396

state: { count: 0 },

397

mutations: {

398

increment(state) { state.count++; }

399

},

400

devtools: true // Force enable even in production

401

});

402

403

// Custom DevTools configuration

404

const app = createApp(App);

405

const store = createStore({

406

// ... store config

407

devtools: true

408

});

409

410

app.use(store);

411

412

// DevTools will show:

413

// - State tree with current values

414

// - Mutation history with time-travel debugging

415

// - Action dispatch logging

416

// - Module hierarchy

417

// - Getter values and dependencies

418

419

// Time-travel debugging features:

420

// 1. Click on any mutation in the timeline to see state at that point

421

// 2. Use "Commit All" to apply all mutations up to a point

422

// 3. Use "Revert" to undo mutations

423

// 4. Export/import state snapshots

424

// 5. Filter mutations and actions by type or module

425

```

426

427

### Custom Development Plugins

428

429

Create custom plugins for development workflow enhancement.

430

431

**Usage Examples:**

432

433

```typescript

434

// Performance monitoring plugin

435

const performancePlugin = (store) => {

436

const actionTimes = new Map();

437

438

store.subscribeAction({

439

before: (action) => {

440

actionTimes.set(action.type, performance.now());

441

},

442

after: (action) => {

443

const startTime = actionTimes.get(action.type);

444

const duration = performance.now() - startTime;

445

446

if (duration > 100) {

447

console.warn(`Slow action detected: ${action.type} took ${duration}ms`);

448

}

449

450

actionTimes.delete(action.type);

451

}

452

});

453

};

454

455

// State persistence plugin

456

const persistPlugin = (store) => {

457

// Load state from localStorage on store creation

458

const saved = localStorage.getItem('vuex-state');

459

if (saved) {

460

store.replaceState(JSON.parse(saved));

461

}

462

463

// Save state on every mutation

464

store.subscribe((mutation, state) => {

465

localStorage.setItem('vuex-state', JSON.stringify(state));

466

});

467

};

468

469

// Error tracking plugin

470

const errorTrackingPlugin = (store) => {

471

store.subscribeAction({

472

error: (action, state, error) => {

473

// Send to error tracking service

474

errorTracker.captureException(error, {

475

extra: {

476

action: action.type,

477

payload: action.payload,

478

state: JSON.stringify(state)

479

}

480

});

481

}

482

});

483

};

484

485

// Usage

486

const store = createStore({

487

// ... store config

488

plugins: process.env.NODE_ENV !== 'production'

489

? [createLogger(), performancePlugin, errorTrackingPlugin]

490

: [persistPlugin]

491

});

492

```