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

module-system.mddocs/

0

# Module System

1

2

Modular organization system for complex applications with namespacing, dynamic registration, and hierarchical state management.

3

4

## Capabilities

5

6

### Module Definition

7

8

Define store modules with their own state, mutations, actions, and getters.

9

10

```typescript { .api }

11

/**

12

* Module definition interface

13

*/

14

interface Module<S, R> {

15

/** Enable/disable namespacing for this module */

16

namespaced?: boolean;

17

/** Module state object or function returning state */

18

state?: S | (() => S);

19

/** Module getters for derived state */

20

getters?: GetterTree<S, R>;

21

/** Module actions for async operations */

22

actions?: ActionTree<S, R>;

23

/** Module mutations for state changes */

24

mutations?: MutationTree<S>;

25

/** Sub-modules for nested organization */

26

modules?: ModuleTree<R>;

27

}

28

29

interface GetterTree<S, R> {

30

[key: string]: Getter<S, R>;

31

}

32

33

interface ActionTree<S, R> {

34

[key: string]: ActionHandler<S, R>;

35

}

36

37

interface MutationTree<S> {

38

[key: string]: Mutation<S>;

39

}

40

41

interface ModuleTree<R> {

42

[key: string]: Module<any, R>;

43

}

44

45

type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;

46

type ActionHandler<S, R> = (context: ActionContext<S, R>, payload?: any) => any;

47

type Mutation<S> = (state: S, payload?: any) => any;

48

```

49

50

**Usage Examples:**

51

52

```typescript

53

// User module

54

const userModule = {

55

namespaced: true,

56

57

state: () => ({

58

profile: null,

59

preferences: {},

60

isAuthenticated: false

61

}),

62

63

mutations: {

64

setProfile(state, profile) {

65

state.profile = profile;

66

},

67

setAuthenticated(state, status) {

68

state.isAuthenticated = status;

69

}

70

},

71

72

actions: {

73

async login({ commit, dispatch }, credentials) {

74

const user = await api.login(credentials);

75

commit('setProfile', user);

76

commit('setAuthenticated', true);

77

78

// Call root action

79

dispatch('initializeApp', null, { root: true });

80

}

81

},

82

83

getters: {

84

displayName: state => state.profile?.name || 'Guest',

85

isLoggedIn: state => state.isAuthenticated && state.profile

86

}

87

};

88

89

// Cart module with sub-modules

90

const cartModule = {

91

namespaced: true,

92

93

state: () => ({

94

items: [],

95

discounts: []

96

}),

97

98

modules: {

99

shipping: shippingModule,

100

payment: paymentModule

101

},

102

103

mutations: {

104

addItem(state, item) {

105

const existing = state.items.find(i => i.id === item.id);

106

if (existing) {

107

existing.quantity += item.quantity;

108

} else {

109

state.items.push(item);

110

}

111

}

112

},

113

114

getters: {

115

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

116

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

117

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

118

sum + item.quantity, 0)

119

}

120

};

121

```

122

123

### Dynamic Module Registration

124

125

Register modules dynamically at runtime.

126

127

```typescript { .api }

128

/**

129

* Register a module dynamically

130

* @param path - Module path (string or array)

131

* @param module - Module definition

132

* @param options - Registration options

133

*/

134

registerModule<T>(path: string | string[], module: Module<T, S>, options?: ModuleOptions): void;

135

136

interface ModuleOptions {

137

/** Preserve existing state when registering */

138

preserveState?: boolean;

139

}

140

```

141

142

**Usage Examples:**

143

144

```typescript

145

import { createStore } from 'vuex';

146

147

const store = createStore({

148

state: { count: 0 }

149

});

150

151

// Register single module

152

store.registerModule('user', userModule);

153

154

// Access: store.state.user.profile

155

// Commit: store.commit('user/setProfile', profile)

156

157

// Register nested module

158

store.registerModule(['cart', 'shipping'], shippingModule);

159

160

// Access: store.state.cart.shipping.address

161

// Commit: store.commit('cart/shipping/setAddress', address)

162

163

// Register with preserved state

164

store.registerModule('settings', settingsModule, {

165

preserveState: true // Don't replace existing settings state

166

});

167

168

// Register multiple modules

169

const modules = {

170

user: userModule,

171

cart: cartModule,

172

notifications: notificationModule

173

};

174

175

Object.keys(modules).forEach(name => {

176

store.registerModule(name, modules[name]);

177

});

178

```

179

180

### Dynamic Module Unregistration

181

182

Unregister modules dynamically to clean up resources.

183

184

```typescript { .api }

185

/**

186

* Unregister a module dynamically

187

* @param path - Module path (string or array)

188

*/

189

unregisterModule(path: string | string[]): void;

190

```

191

192

**Usage Examples:**

193

194

```typescript

195

// Unregister single module

196

store.unregisterModule('user');

197

198

// Unregister nested module

199

store.unregisterModule(['cart', 'shipping']);

200

201

// Conditional unregistration

202

if (store.hasModule('temporaryData')) {

203

store.unregisterModule('temporaryData');

204

}

205

206

// Clean up route-specific modules

207

router.beforeEach((to, from, next) => {

208

// Unregister previous route's modules

209

if (from.meta.storeModules) {

210

from.meta.storeModules.forEach(moduleName => {

211

if (store.hasModule(moduleName)) {

212

store.unregisterModule(moduleName);

213

}

214

});

215

}

216

217

// Register new route's modules

218

if (to.meta.storeModules) {

219

to.meta.storeModules.forEach(({ name, module }) => {

220

store.registerModule(name, module);

221

});

222

}

223

224

next();

225

});

226

```

227

228

### Module Existence Check

229

230

Check if a module is registered.

231

232

```typescript { .api }

233

/**

234

* Check if a module exists

235

* @param path - Module path (string or array)

236

* @returns True if module exists

237

*/

238

hasModule(path: string | string[]): boolean;

239

```

240

241

**Usage Examples:**

242

243

```typescript

244

// Check single module

245

if (store.hasModule('user')) {

246

console.log('User module is registered');

247

}

248

249

// Check nested module

250

if (store.hasModule(['cart', 'payment'])) {

251

store.dispatch('cart/payment/processPayment');

252

}

253

254

// Conditional registration

255

if (!store.hasModule('analytics')) {

256

store.registerModule('analytics', analyticsModule);

257

}

258

259

// Guard against missing modules

260

const safeDispatch = (action, payload) => {

261

const [module] = action.split('/');

262

if (store.hasModule(module)) {

263

return store.dispatch(action, payload);

264

} else {

265

console.warn(`Module ${module} not found for action ${action}`);

266

return Promise.resolve();

267

}

268

};

269

```

270

271

### Namespaced Module Access

272

273

Access namespaced module state, getters, mutations, and actions.

274

275

**Usage Examples:**

276

277

```typescript

278

const store = createStore({

279

modules: {

280

user: {

281

namespaced: true,

282

state: { profile: null },

283

mutations: { setProfile(state, profile) { state.profile = profile; } },

284

actions: {

285

async fetchProfile({ commit }, userId) {

286

const profile = await api.getProfile(userId);

287

commit('setProfile', profile);

288

}

289

},

290

getters: {

291

displayName: state => state.profile?.name || 'Guest'

292

}

293

},

294

295

cart: {

296

namespaced: true,

297

state: { items: [] },

298

modules: {

299

shipping: {

300

namespaced: true,

301

state: { address: null },

302

mutations: { setAddress(state, address) { state.address = address; } }

303

}

304

}

305

}

306

}

307

});

308

309

// Access namespaced state

310

const userProfile = store.state.user.profile;

311

const cartItems = store.state.cart.items;

312

const shippingAddress = store.state.cart.shipping.address;

313

314

// Access namespaced getters

315

const displayName = store.getters['user/displayName'];

316

317

// Commit namespaced mutations

318

store.commit('user/setProfile', profile);

319

store.commit('cart/shipping/setAddress', address);

320

321

// Dispatch namespaced actions

322

await store.dispatch('user/fetchProfile', userId);

323

324

// Using helpers with namespaces

325

import { mapState, mapActions } from 'vuex';

326

327

export default {

328

computed: {

329

...mapState('user', ['profile']),

330

...mapState('cart', {

331

cartItems: 'items'

332

})

333

},

334

methods: {

335

...mapActions('user', ['fetchProfile']),

336

...mapActions('cart', ['addItem'])

337

}

338

};

339

```

340

341

### Root Access from Modules

342

343

Access root state and dispatch root actions from within modules.

344

345

**Usage Examples:**

346

347

```typescript

348

const userModule = {

349

namespaced: true,

350

351

state: () => ({ profile: null }),

352

353

actions: {

354

async login({ commit, dispatch, rootState, rootGetters }, credentials) {

355

// Access root state

356

const appVersion = rootState.appVersion;

357

358

// Access root getters

359

const isOnline = rootGetters.isOnline;

360

361

if (!isOnline) {

362

throw new Error('Cannot login while offline');

363

}

364

365

const user = await api.login(credentials, { appVersion });

366

commit('setProfile', user);

367

368

// Dispatch root action

369

await dispatch('analytics/trackEvent', {

370

event: 'user_login',

371

userId: user.id

372

}, { root: true });

373

374

// Commit root mutation

375

commit('setLastLoginTime', Date.now(), { root: true });

376

}

377

},

378

379

getters: {

380

// Access root state in getters

381

canAccessPremium: (state, getters, rootState, rootGetters) => {

382

return state.profile?.tier === 'premium' || rootGetters.hasGlobalAccess;

383

}

384

}

385

};

386

```

387

388

### Module Hot Reloading

389

390

Support for hot module replacement during development.

391

392

**Usage Examples:**

393

394

```typescript

395

import { createStore } from 'vuex';

396

import userModule from './modules/user';

397

import cartModule from './modules/cart';

398

399

const store = createStore({

400

modules: {

401

user: userModule,

402

cart: cartModule

403

}

404

});

405

406

// Hot module replacement

407

if (module.hot) {

408

// Accept updates for modules

409

module.hot.accept(['./modules/user'], () => {

410

const newUserModule = require('./modules/user').default;

411

store.hotUpdate({

412

modules: {

413

user: newUserModule

414

}

415

});

416

});

417

418

// Accept updates for multiple modules

419

module.hot.accept([

420

'./modules/user',

421

'./modules/cart'

422

], () => {

423

const newUserModule = require('./modules/user').default;

424

const newCartModule = require('./modules/cart').default;

425

426

store.hotUpdate({

427

modules: {

428

user: newUserModule,

429

cart: newCartModule

430

}

431

});

432

});

433

}

434

435

export default store;

436

```

437

438

### Module Composition Patterns

439

440

Advanced patterns for organizing and composing modules.

441

442

**Usage Examples:**

443

444

```typescript

445

// Base module factory

446

const createCrudModule = (resource) => ({

447

namespaced: true,

448

449

state: () => ({

450

items: [],

451

loading: false,

452

error: null

453

}),

454

455

mutations: {

456

setItems(state, items) { state.items = items; },

457

setLoading(state, loading) { state.loading = loading; },

458

setError(state, error) { state.error = error; }

459

},

460

461

actions: {

462

async fetchAll({ commit }) {

463

commit('setLoading', true);

464

try {

465

const items = await api.getAll(resource);

466

commit('setItems', items);

467

} catch (error) {

468

commit('setError', error);

469

} finally {

470

commit('setLoading', false);

471

}

472

}

473

},

474

475

getters: {

476

byId: state => id => state.items.find(item => item.id === id),

477

count: state => state.items.length

478

}

479

});

480

481

// Create specific modules

482

const usersModule = {

483

...createCrudModule('users'),

484

actions: {

485

...createCrudModule('users').actions,

486

async login({ dispatch }, credentials) {

487

// User-specific action

488

const user = await api.login(credentials);

489

dispatch('fetchAll'); // Inherited action

490

return user;

491

}

492

}

493

};

494

495

const postsModule = createCrudModule('posts');

496

497

// Plugin-based module composition

498

const withCaching = (module, cacheKey) => ({

499

...module,

500

actions: {

501

...module.actions,

502

async fetchAll({ commit, state }) {

503

const cached = localStorage.getItem(cacheKey);

504

if (cached) {

505

commit('setItems', JSON.parse(cached));

506

} else {

507

await module.actions.fetchAll({ commit, state });

508

localStorage.setItem(cacheKey, JSON.stringify(state.items));

509

}

510

}

511

}

512

});

513

514

const cachedUsersModule = withCaching(usersModule, 'cached_users');

515

```