@eztra.services/engine 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,436 @@
1
+ // src/ComposableController.ts
2
+ var ComposableController = class {
3
+ controllers = /* @__PURE__ */ new Map();
4
+ messenger;
5
+ persistenceService;
6
+ features;
7
+ listeners = /* @__PURE__ */ new Set();
8
+ initialized = false;
9
+ constructor(config) {
10
+ this.messenger = config.messenger;
11
+ this.persistenceService = config.persistenceService;
12
+ this.features = {
13
+ enablePersistence: config.features?.enablePersistence ?? true,
14
+ enableStateRestoration: config.features?.enableStateRestoration ?? true,
15
+ enableAutoSave: config.features?.enableAutoSave ?? false
16
+ };
17
+ }
18
+ /**
19
+ * Register a controller
20
+ *
21
+ * @param name - Unique controller identifier
22
+ * @param controller - Controller instance
23
+ * @throws Error if controller is already registered
24
+ */
25
+ registerController(name, controller) {
26
+ if (this.controllers.has(name)) {
27
+ throw new Error(`Controller "${name}" is already registered`);
28
+ }
29
+ if (this.initialized) {
30
+ throw new Error("Cannot register controllers after initialization");
31
+ }
32
+ this.controllers.set(name, controller);
33
+ console.log(`[ComposableController] Registered controller: ${name}`);
34
+ }
35
+ /**
36
+ * Unregister a controller
37
+ *
38
+ * @param name - Controller identifier
39
+ */
40
+ async unregisterController(name) {
41
+ const controller = this.controllers.get(name);
42
+ if (!controller) {
43
+ return;
44
+ }
45
+ await controller.destroy();
46
+ this.controllers.delete(name);
47
+ console.log(`[ComposableController] Unregistered controller: ${name}`);
48
+ }
49
+ /**
50
+ * Get a controller by name
51
+ *
52
+ * @param name - Controller identifier
53
+ * @returns Controller instance or undefined
54
+ */
55
+ getController(name) {
56
+ return this.controllers.get(name);
57
+ }
58
+ /**
59
+ * Get all registered controller names
60
+ */
61
+ getControllerNames() {
62
+ return Array.from(this.controllers.keys());
63
+ }
64
+ /**
65
+ * Get unified state from all controllers
66
+ *
67
+ * @returns Object mapping controller names to their states
68
+ */
69
+ getState() {
70
+ const composedState = {};
71
+ for (const [name, controller] of this.controllers) {
72
+ composedState[name] = controller.state;
73
+ }
74
+ return composedState;
75
+ }
76
+ /**
77
+ * Subscribe to state changes from all controllers
78
+ *
79
+ * @param listener - Callback for state changes
80
+ * @returns Unsubscribe function
81
+ */
82
+ subscribe(listener) {
83
+ this.listeners.add(listener);
84
+ return () => {
85
+ this.listeners.delete(listener);
86
+ };
87
+ }
88
+ /**
89
+ * Initialize all controllers
90
+ *
91
+ * This should be called after all controllers are registered.
92
+ * The initialization process:
93
+ * 1. Restore persisted state (if enabled)
94
+ * 2. Initialize each controller in order
95
+ * 3. Set up state change subscriptions
96
+ */
97
+ async initialize() {
98
+ if (this.initialized) {
99
+ console.warn("[ComposableController] Already initialized");
100
+ return;
101
+ }
102
+ console.log("[ComposableController] Initializing...");
103
+ if (this.features.enableStateRestoration && this.persistenceService) {
104
+ await this.restoreAllStates();
105
+ }
106
+ for (const [name, controller] of this.controllers) {
107
+ try {
108
+ console.log(`[ComposableController] Initializing ${name}...`);
109
+ await controller.initialize();
110
+ this.subscribeToController(name, controller);
111
+ } catch (error) {
112
+ console.error(`[ComposableController] Failed to initialize ${name}:`, error);
113
+ throw error;
114
+ }
115
+ }
116
+ this.initialized = true;
117
+ console.log("[ComposableController] Initialization complete");
118
+ }
119
+ /**
120
+ * Start all controllers
121
+ *
122
+ * This is called after initialization to begin normal operations.
123
+ */
124
+ async start() {
125
+ if (!this.initialized) {
126
+ await this.initialize();
127
+ }
128
+ console.log("[ComposableController] Starting all controllers...");
129
+ for (const [name, controller] of this.controllers) {
130
+ if (typeof controller.start === "function") {
131
+ try {
132
+ await controller.start();
133
+ } catch (error) {
134
+ console.error(`[ComposableController] Failed to start ${name}:`, error);
135
+ }
136
+ }
137
+ }
138
+ console.log("[ComposableController] All controllers started");
139
+ }
140
+ /**
141
+ * Stop all controllers
142
+ *
143
+ * Persists state if auto-save is enabled.
144
+ */
145
+ async stop() {
146
+ console.log("[ComposableController] Stopping all controllers...");
147
+ if (this.features.enableAutoSave && this.features.enablePersistence) {
148
+ await this.persistAllStates();
149
+ }
150
+ for (const [name, controller] of this.controllers) {
151
+ if (typeof controller.stop === "function") {
152
+ try {
153
+ await controller.stop();
154
+ } catch (error) {
155
+ console.error(`[ComposableController] Failed to stop ${name}:`, error);
156
+ }
157
+ }
158
+ }
159
+ console.log("[ComposableController] All controllers stopped");
160
+ }
161
+ /**
162
+ * Destroy all controllers and cleanup
163
+ */
164
+ async destroy() {
165
+ console.log("[ComposableController] Destroying...");
166
+ if (this.features.enablePersistence) {
167
+ await this.persistAllStates();
168
+ }
169
+ for (const [name] of this.controllers) {
170
+ await this.unregisterController(name);
171
+ }
172
+ this.listeners.clear();
173
+ this.initialized = false;
174
+ console.log("[ComposableController] Destroyed");
175
+ }
176
+ /**
177
+ * Persist all controller states
178
+ */
179
+ async persistAllStates() {
180
+ if (!this.persistenceService) {
181
+ return;
182
+ }
183
+ console.log("[ComposableController] Persisting all states...");
184
+ for (const [name, controller] of this.controllers) {
185
+ try {
186
+ if (typeof controller.persist === "function") {
187
+ await controller.persist();
188
+ }
189
+ } catch (error) {
190
+ console.error(`[ComposableController] Failed to persist ${name}:`, error);
191
+ }
192
+ }
193
+ }
194
+ /**
195
+ * Restore all controller states from storage
196
+ *
197
+ * @private
198
+ */
199
+ async restoreAllStates() {
200
+ console.log("[ComposableController] Restoring persisted states...");
201
+ for (const [name, controller] of this.controllers) {
202
+ try {
203
+ if (typeof controller.loadPersistedState === "function") {
204
+ const restored = await controller.loadPersistedState();
205
+ if (restored) {
206
+ console.log(`[ComposableController] Restored state for ${name}`);
207
+ }
208
+ }
209
+ } catch (error) {
210
+ console.error(`[ComposableController] Failed to restore ${name}:`, error);
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Subscribe to a controller's state changes
216
+ *
217
+ * @private
218
+ */
219
+ subscribeToController(name, controller) {
220
+ const eventName = `${name}:stateChange`;
221
+ this.messenger.subscribe(eventName, (event) => {
222
+ this.listeners.forEach((listener) => {
223
+ try {
224
+ listener(event.newState, name);
225
+ } catch (error) {
226
+ console.error(
227
+ `[ComposableController] Error in state change listener:`,
228
+ error
229
+ );
230
+ }
231
+ });
232
+ if (this.features.enableAutoSave && this.features.enablePersistence) {
233
+ if (typeof controller.persist === "function") {
234
+ controller.persist().catch((error) => {
235
+ console.error(`[ComposableController] Auto-save failed for ${name}:`, error);
236
+ });
237
+ }
238
+ }
239
+ });
240
+ }
241
+ };
242
+
243
+ // src/EztraEngine.ts
244
+ import { ControllerMessenger } from "@eztra/controller";
245
+ import { StatePersistenceService } from "@eztra/storage";
246
+ import {
247
+ NetworkController
248
+ } from "@eztra/network-controller";
249
+ import {
250
+ WalletController
251
+ } from "@eztra/wallet-controller";
252
+ var EztraEngine = class {
253
+ messenger;
254
+ composableController;
255
+ persistenceService;
256
+ initialized = false;
257
+ // Public controller references
258
+ networkController;
259
+ walletController;
260
+ constructor(config) {
261
+ this.messenger = new ControllerMessenger();
262
+ this.persistenceService = new StatePersistenceService(config.storage);
263
+ this.networkController = this.createNetworkController(config);
264
+ this.walletController = this.createWalletController(config);
265
+ this.composableController = new ComposableController({
266
+ messenger: this.messenger,
267
+ persistenceService: this.persistenceService,
268
+ features: config.features
269
+ });
270
+ this.composableController.registerController(
271
+ "NetworkController",
272
+ this.networkController
273
+ );
274
+ this.composableController.registerController(
275
+ "WalletController",
276
+ this.walletController
277
+ );
278
+ }
279
+ /**
280
+ * Create NetworkController with restricted messenger
281
+ *
282
+ * @private
283
+ */
284
+ createNetworkController(config) {
285
+ return new NetworkController({
286
+ messenger: this.messenger.getRestricted({
287
+ name: "NetworkController",
288
+ allowedActions: [
289
+ "NetworkController:setChainId",
290
+ "NetworkController:getActiveChainId",
291
+ "NetworkController:getRpcUrl",
292
+ "NetworkController:refreshEndpoints",
293
+ "NetworkController:setWalletIndex"
294
+ ],
295
+ allowedEvents: [
296
+ "NetworkController:stateChange",
297
+ "NetworkController:chainChanged",
298
+ "NetworkController:endpointsUpdated"
299
+ ]
300
+ }),
301
+ persistenceService: this.persistenceService,
302
+ state: config.initialState?.NetworkController,
303
+ defaultChainId: config.defaultChainId
304
+ });
305
+ }
306
+ /**
307
+ * Create WalletController with restricted messenger
308
+ *
309
+ * @private
310
+ */
311
+ createWalletController(config) {
312
+ return new WalletController({
313
+ messenger: this.messenger.getRestricted({
314
+ name: "WalletController",
315
+ allowedActions: [
316
+ "WalletController:addWallet",
317
+ "WalletController:selectWallet",
318
+ "WalletController:getActiveWallet",
319
+ "WalletController:lockWallet",
320
+ "WalletController:unlockWallet"
321
+ ],
322
+ allowedEvents: [
323
+ "WalletController:stateChange",
324
+ "WalletController:walletSelected",
325
+ "WalletController:walletLocked",
326
+ "WalletController:walletUnlocked"
327
+ ],
328
+ externalActions: [
329
+ "NetworkController:getActiveChainId",
330
+ "NetworkController:setWalletIndex"
331
+ ],
332
+ externalEvents: ["NetworkController:chainChanged"]
333
+ }),
334
+ persistenceService: this.persistenceService,
335
+ state: config.initialState?.WalletController
336
+ });
337
+ }
338
+ /**
339
+ * Initialize the engine and all controllers
340
+ */
341
+ async initialize() {
342
+ if (this.initialized) {
343
+ console.warn("[EztraEngine] Already initialized");
344
+ return;
345
+ }
346
+ console.log("[EztraEngine] Initializing...");
347
+ await this.composableController.initialize();
348
+ this.initialized = true;
349
+ console.log("[EztraEngine] Initialization complete");
350
+ }
351
+ /**
352
+ * Start the engine
353
+ */
354
+ async start() {
355
+ if (!this.initialized) {
356
+ await this.initialize();
357
+ }
358
+ await this.composableController.start();
359
+ }
360
+ /**
361
+ * Stop the engine
362
+ */
363
+ async stop() {
364
+ await this.composableController.stop();
365
+ }
366
+ /**
367
+ * Destroy the engine and cleanup
368
+ */
369
+ async destroy() {
370
+ await this.composableController.destroy();
371
+ this.initialized = false;
372
+ }
373
+ /**
374
+ * Get unified state from all controllers
375
+ */
376
+ getState() {
377
+ return this.composableController.getState();
378
+ }
379
+ /**
380
+ * Call an action
381
+ */
382
+ call(action, ...params) {
383
+ return this.messenger.call(action, ...params);
384
+ }
385
+ /**
386
+ * Subscribe to an event
387
+ */
388
+ subscribe(event, listener) {
389
+ return this.messenger.subscribe(event, listener);
390
+ }
391
+ /**
392
+ * Get the global messenger (for advanced usage)
393
+ */
394
+ getMessenger() {
395
+ return this.messenger;
396
+ }
397
+ /**
398
+ * Check if engine is initialized
399
+ */
400
+ isInitialized() {
401
+ return this.initialized;
402
+ }
403
+ };
404
+ var engineInstance = null;
405
+ async function initializeEngine(config) {
406
+ if (engineInstance) {
407
+ throw new Error("Engine already initialized. Use getEngine() to access the instance.");
408
+ }
409
+ engineInstance = new EztraEngine(config);
410
+ await engineInstance.initialize();
411
+ return engineInstance;
412
+ }
413
+ function getEngine() {
414
+ if (!engineInstance) {
415
+ throw new Error("Engine not initialized. Call initializeEngine() first.");
416
+ }
417
+ return engineInstance;
418
+ }
419
+ function isEngineInitialized() {
420
+ return engineInstance !== null && engineInstance.isInitialized();
421
+ }
422
+ async function resetEngine() {
423
+ if (engineInstance) {
424
+ await engineInstance.destroy();
425
+ engineInstance = null;
426
+ }
427
+ }
428
+ export {
429
+ ComposableController,
430
+ EztraEngine,
431
+ getEngine,
432
+ initializeEngine,
433
+ isEngineInitialized,
434
+ resetEngine
435
+ };
436
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ComposableController.ts","../src/EztraEngine.ts"],"sourcesContent":["import type {\n BaseController,\n StateChangeEvent,\n ControllerMessenger,\n} from '@eztra/controller';\n\n/**\n * Composable controller configuration\n */\nexport interface ComposableControllerConfig {\n /**\n * Global messenger instance\n */\n messenger: ControllerMessenger<any, any>;\n\n /**\n * Optional persistence service for all controllers\n */\n persistenceService?: any;\n\n /**\n * Feature flags\n */\n features?: {\n enablePersistence?: boolean;\n enableStateRestoration?: boolean;\n enableAutoSave?: boolean;\n };\n}\n\n/**\n * Controller map type\n */\nexport type ControllerMap = {\n [name: string]: BaseController<any, any, any>;\n};\n\n/**\n * Composed state type - merges all controller states\n */\nexport type ComposedState<Controllers extends ControllerMap> = {\n [K in keyof Controllers]: Controllers[K] extends BaseController<any, infer State, any>\n ? State\n : never;\n};\n\n/**\n * State change listener for composed state\n */\nexport type ComposedStateChangeListener = (state: any, controllerName: string) => void;\n\n/**\n * ComposableController - Orchestrates multiple controllers into a unified system\n *\n * Inspired by MetaMask's ComposableController, this provides:\n * - Unified state management across all controllers\n * - Lifecycle orchestration (initialize, start, stop, destroy)\n * - Centralized state persistence and restoration\n * - Single subscription point for all state changes\n * - Context sharing between controllers\n *\n * @example\n * ```typescript\n * const messenger = new ControllerMessenger<EztraActions, EztraEvents>();\n * const persistenceService = new StatePersistenceService(storage);\n *\n * const composable = new ComposableController({\n * messenger,\n * persistenceService,\n * features: {\n * enablePersistence: true,\n * enableStateRestoration: true,\n * },\n * });\n *\n * // Register controllers\n * composable.registerController('NetworkController', networkController);\n * composable.registerController('WalletController', walletController);\n *\n * // Initialize all controllers\n * await composable.initialize();\n *\n * // Get unified state\n * const state = composable.getState();\n * // { NetworkController: {...}, WalletController: {...} }\n *\n * // Subscribe to all state changes\n * composable.subscribe((state, controllerName) => {\n * console.log(`${controllerName} state changed:`, state);\n * });\n * ```\n */\nexport class ComposableController {\n private controllers = new Map<string, BaseController<any, any, any>>();\n private messenger: ControllerMessenger<any, any>;\n private persistenceService?: any;\n private features: Required<NonNullable<ComposableControllerConfig['features']>>;\n private listeners = new Set<ComposedStateChangeListener>();\n private initialized = false;\n\n constructor(config: ComposableControllerConfig) {\n this.messenger = config.messenger;\n this.persistenceService = config.persistenceService;\n this.features = {\n enablePersistence: config.features?.enablePersistence ?? true,\n enableStateRestoration: config.features?.enableStateRestoration ?? true,\n enableAutoSave: config.features?.enableAutoSave ?? false,\n };\n }\n\n /**\n * Register a controller\n *\n * @param name - Unique controller identifier\n * @param controller - Controller instance\n * @throws Error if controller is already registered\n */\n registerController<T extends BaseController<any, any, any>>(\n name: string,\n controller: T\n ): void {\n if (this.controllers.has(name)) {\n throw new Error(`Controller \"${name}\" is already registered`);\n }\n\n if (this.initialized) {\n throw new Error('Cannot register controllers after initialization');\n }\n\n this.controllers.set(name, controller);\n console.log(`[ComposableController] Registered controller: ${name}`);\n }\n\n /**\n * Unregister a controller\n *\n * @param name - Controller identifier\n */\n async unregisterController(name: string): Promise<void> {\n const controller = this.controllers.get(name);\n if (!controller) {\n return;\n }\n\n // Destroy controller\n await controller.destroy();\n this.controllers.delete(name);\n\n console.log(`[ComposableController] Unregistered controller: ${name}`);\n }\n\n /**\n * Get a controller by name\n *\n * @param name - Controller identifier\n * @returns Controller instance or undefined\n */\n getController<T extends BaseController<any, any, any>>(name: string): T | undefined {\n return this.controllers.get(name) as T | undefined;\n }\n\n /**\n * Get all registered controller names\n */\n getControllerNames(): string[] {\n return Array.from(this.controllers.keys());\n }\n\n /**\n * Get unified state from all controllers\n *\n * @returns Object mapping controller names to their states\n */\n getState(): Record<string, any> {\n const composedState: Record<string, any> = {};\n\n for (const [name, controller] of this.controllers) {\n composedState[name] = controller.state;\n }\n\n return composedState;\n }\n\n /**\n * Subscribe to state changes from all controllers\n *\n * @param listener - Callback for state changes\n * @returns Unsubscribe function\n */\n subscribe(listener: ComposedStateChangeListener): () => void {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Initialize all controllers\n *\n * This should be called after all controllers are registered.\n * The initialization process:\n * 1. Restore persisted state (if enabled)\n * 2. Initialize each controller in order\n * 3. Set up state change subscriptions\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n console.warn('[ComposableController] Already initialized');\n return;\n }\n\n console.log('[ComposableController] Initializing...');\n\n // Restore persisted state first\n if (this.features.enableStateRestoration && this.persistenceService) {\n await this.restoreAllStates();\n }\n\n // Initialize all controllers\n for (const [name, controller] of this.controllers) {\n try {\n console.log(`[ComposableController] Initializing ${name}...`);\n await controller.initialize();\n\n // Subscribe to controller state changes\n this.subscribeToController(name, controller);\n } catch (error) {\n console.error(`[ComposableController] Failed to initialize ${name}:`, error);\n throw error;\n }\n }\n\n this.initialized = true;\n console.log('[ComposableController] Initialization complete');\n }\n\n /**\n * Start all controllers\n *\n * This is called after initialization to begin normal operations.\n */\n async start(): Promise<void> {\n if (!this.initialized) {\n await this.initialize();\n }\n\n console.log('[ComposableController] Starting all controllers...');\n\n for (const [name, controller] of this.controllers) {\n // Check if controller has a start method\n if (typeof (controller as any).start === 'function') {\n try {\n await (controller as any).start();\n } catch (error) {\n console.error(`[ComposableController] Failed to start ${name}:`, error);\n }\n }\n }\n\n console.log('[ComposableController] All controllers started');\n }\n\n /**\n * Stop all controllers\n *\n * Persists state if auto-save is enabled.\n */\n async stop(): Promise<void> {\n console.log('[ComposableController] Stopping all controllers...');\n\n // Persist state if auto-save is enabled\n if (this.features.enableAutoSave && this.features.enablePersistence) {\n await this.persistAllStates();\n }\n\n // Stop all controllers\n for (const [name, controller] of this.controllers) {\n if (typeof (controller as any).stop === 'function') {\n try {\n await (controller as any).stop();\n } catch (error) {\n console.error(`[ComposableController] Failed to stop ${name}:`, error);\n }\n }\n }\n\n console.log('[ComposableController] All controllers stopped');\n }\n\n /**\n * Destroy all controllers and cleanup\n */\n async destroy(): Promise<void> {\n console.log('[ComposableController] Destroying...');\n\n // Persist state before destruction\n if (this.features.enablePersistence) {\n await this.persistAllStates();\n }\n\n // Destroy all controllers\n for (const [name] of this.controllers) {\n await this.unregisterController(name);\n }\n\n // Clear listeners\n this.listeners.clear();\n\n this.initialized = false;\n console.log('[ComposableController] Destroyed');\n }\n\n /**\n * Persist all controller states\n */\n async persistAllStates(): Promise<void> {\n if (!this.persistenceService) {\n return;\n }\n\n console.log('[ComposableController] Persisting all states...');\n\n for (const [name, controller] of this.controllers) {\n try {\n if (typeof (controller as any).persist === 'function') {\n await (controller as any).persist();\n }\n } catch (error) {\n console.error(`[ComposableController] Failed to persist ${name}:`, error);\n }\n }\n }\n\n /**\n * Restore all controller states from storage\n *\n * @private\n */\n private async restoreAllStates(): Promise<void> {\n console.log('[ComposableController] Restoring persisted states...');\n\n for (const [name, controller] of this.controllers) {\n try {\n if (typeof (controller as any).loadPersistedState === 'function') {\n const restored = await (controller as any).loadPersistedState();\n if (restored) {\n console.log(`[ComposableController] Restored state for ${name}`);\n }\n }\n } catch (error) {\n console.error(`[ComposableController] Failed to restore ${name}:`, error);\n }\n }\n }\n\n /**\n * Subscribe to a controller's state changes\n *\n * @private\n */\n private subscribeToController(\n name: string,\n controller: BaseController<any, any, any>\n ): void {\n // Subscribe to controller's stateChange event via messenger\n const eventName = `${name}:stateChange` as any;\n\n this.messenger.subscribe(eventName, (event: StateChangeEvent<any>) => {\n // Notify all listeners\n this.listeners.forEach((listener) => {\n try {\n listener(event.newState, name);\n } catch (error) {\n console.error(\n `[ComposableController] Error in state change listener:`,\n error\n );\n }\n });\n\n // Auto-save if enabled\n if (this.features.enableAutoSave && this.features.enablePersistence) {\n if (typeof (controller as any).persist === 'function') {\n (controller as any).persist().catch((error: Error) => {\n console.error(`[ComposableController] Auto-save failed for ${name}:`, error);\n });\n }\n }\n });\n }\n}\n","import { ControllerMessenger } from '@eztra/controller';\nimport { StatePersistenceService, type IStorage } from '@eztra/storage';\nimport {\n NetworkController,\n type NetworkControllerActions,\n type NetworkControllerEvents,\n} from '@eztra/network-controller';\nimport {\n WalletController,\n type WalletControllerActions,\n type WalletControllerEvents,\n} from '@eztra/wallet-controller';\nimport { ComposableController } from './ComposableController';\n\n/**\n * Combined actions from all controllers\n */\nexport type EztraEngineActions = NetworkControllerActions & WalletControllerActions;\n\n/**\n * Combined events from all controllers\n */\nexport type EztraEngineEvents = NetworkControllerEvents & WalletControllerEvents;\n\n/**\n * Engine configuration\n */\nexport interface EngineConfig {\n /**\n * Storage implementation\n */\n storage: IStorage;\n\n /**\n * Default chain ID\n */\n defaultChainId?: number;\n\n /**\n * Enable features\n */\n features?: {\n enablePersistence?: boolean;\n enableStateRestoration?: boolean;\n enableAutoSave?: boolean;\n };\n\n /**\n * Initial state for controllers\n */\n initialState?: {\n NetworkController?: any;\n WalletController?: any;\n };\n}\n\n/**\n * EztraEngine - Main engine class that orchestrates all controllers\n *\n * This is the primary entry point for initializing the Eztra Engine with\n * MetaMask-style controller architecture.\n *\n * Features:\n * - Automatic controller initialization\n * - Type-safe inter-controller communication\n * - Unified state management\n * - State persistence and restoration\n * - Lifecycle management\n *\n * @example\n * ```typescript\n * import { EztraEngine } from '@eztra/engine';\n * import { createWebStorage } from '@eztra/engine/storage/web';\n *\n * // Initialize engine\n * const engine = new EztraEngine({\n * storage: createWebStorage({ id: 'eztra' }),\n * defaultChainId: 1, // Ethereum\n * features: {\n * enablePersistence: true,\n * enableStateRestoration: true,\n * enableAutoSave: true,\n * },\n * });\n *\n * // Initialize all controllers\n * await engine.initialize();\n *\n * // Access controllers\n * const chainId = engine.call('NetworkController:getActiveChainId');\n * const wallet = engine.call('WalletController:getActiveWallet');\n *\n * // Subscribe to events\n * engine.subscribe('NetworkController:chainChanged', (event) => {\n * console.log('Chain changed:', event.chainId);\n * });\n *\n * // Get unified state\n * const state = engine.getState();\n * // { NetworkController: {...}, WalletController: {...} }\n * ```\n */\nexport class EztraEngine {\n private messenger: ControllerMessenger<EztraEngineActions, EztraEngineEvents>;\n private composableController: ComposableController;\n private persistenceService: StatePersistenceService;\n private initialized = false;\n\n // Public controller references\n public networkController: NetworkController;\n public walletController: WalletController;\n\n constructor(config: EngineConfig) {\n // Create global messenger\n this.messenger = new ControllerMessenger<\n EztraEngineActions,\n EztraEngineEvents\n >();\n\n // Create persistence service\n this.persistenceService = new StatePersistenceService(config.storage);\n\n // Create controllers with restricted messengers\n this.networkController = this.createNetworkController(config);\n this.walletController = this.createWalletController(config);\n\n // Create composable controller\n this.composableController = new ComposableController({\n messenger: this.messenger,\n persistenceService: this.persistenceService,\n features: config.features,\n });\n\n // Register controllers\n this.composableController.registerController(\n 'NetworkController',\n this.networkController\n );\n this.composableController.registerController(\n 'WalletController',\n this.walletController\n );\n }\n\n /**\n * Create NetworkController with restricted messenger\n *\n * @private\n */\n private createNetworkController(config: EngineConfig): NetworkController {\n return new NetworkController({\n messenger: this.messenger.getRestricted({\n name: 'NetworkController',\n allowedActions: [\n 'NetworkController:setChainId',\n 'NetworkController:getActiveChainId',\n 'NetworkController:getRpcUrl',\n 'NetworkController:refreshEndpoints',\n 'NetworkController:setWalletIndex',\n ],\n allowedEvents: [\n 'NetworkController:stateChange',\n 'NetworkController:chainChanged',\n 'NetworkController:endpointsUpdated',\n ],\n }),\n persistenceService: this.persistenceService,\n state: config.initialState?.NetworkController,\n defaultChainId: config.defaultChainId,\n });\n }\n\n /**\n * Create WalletController with restricted messenger\n *\n * @private\n */\n private createWalletController(config: EngineConfig): WalletController {\n return new WalletController({\n messenger: this.messenger.getRestricted({\n name: 'WalletController',\n allowedActions: [\n 'WalletController:addWallet',\n 'WalletController:selectWallet',\n 'WalletController:getActiveWallet',\n 'WalletController:lockWallet',\n 'WalletController:unlockWallet',\n ],\n allowedEvents: [\n 'WalletController:stateChange',\n 'WalletController:walletSelected',\n 'WalletController:walletLocked',\n 'WalletController:walletUnlocked',\n ],\n externalActions: [\n 'NetworkController:getActiveChainId',\n 'NetworkController:setWalletIndex',\n ],\n externalEvents: ['NetworkController:chainChanged'],\n }),\n persistenceService: this.persistenceService,\n state: config.initialState?.WalletController,\n });\n }\n\n /**\n * Initialize the engine and all controllers\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n console.warn('[EztraEngine] Already initialized');\n return;\n }\n\n console.log('[EztraEngine] Initializing...');\n\n // Initialize all controllers via composable controller\n await this.composableController.initialize();\n\n this.initialized = true;\n console.log('[EztraEngine] Initialization complete');\n }\n\n /**\n * Start the engine\n */\n async start(): Promise<void> {\n if (!this.initialized) {\n await this.initialize();\n }\n\n await this.composableController.start();\n }\n\n /**\n * Stop the engine\n */\n async stop(): Promise<void> {\n await this.composableController.stop();\n }\n\n /**\n * Destroy the engine and cleanup\n */\n async destroy(): Promise<void> {\n await this.composableController.destroy();\n this.initialized = false;\n }\n\n /**\n * Get unified state from all controllers\n */\n getState(): Record<string, any> {\n return this.composableController.getState();\n }\n\n /**\n * Call an action\n */\n call<Action extends keyof EztraEngineActions>(\n action: Action,\n ...params: any[]\n ): any {\n return this.messenger.call(action, ...params);\n }\n\n /**\n * Subscribe to an event\n */\n subscribe<Event extends keyof EztraEngineEvents>(\n event: Event,\n listener: any\n ): () => void {\n return this.messenger.subscribe(event, listener);\n }\n\n /**\n * Get the global messenger (for advanced usage)\n */\n getMessenger(): ControllerMessenger<EztraEngineActions, EztraEngineEvents> {\n return this.messenger;\n }\n\n /**\n * Check if engine is initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n}\n\n// ==================== Singleton Pattern ====================\n\nlet engineInstance: EztraEngine | null = null;\n\n/**\n * Initialize the Eztra Engine (singleton)\n *\n * @param config - Engine configuration\n * @returns Engine instance\n * @throws Error if already initialized\n *\n * @example\n * ```typescript\n * import { initializeEngine } from '@eztra/engine';\n *\n * const engine = await initializeEngine({\n * storage: createWebStorage({ id: 'eztra' }),\n * defaultChainId: 1,\n * });\n * ```\n */\nexport async function initializeEngine(config: EngineConfig): Promise<EztraEngine> {\n if (engineInstance) {\n throw new Error('Engine already initialized. Use getEngine() to access the instance.');\n }\n\n engineInstance = new EztraEngine(config);\n await engineInstance.initialize();\n\n return engineInstance;\n}\n\n/**\n * Get the initialized engine instance\n *\n * @returns Engine instance\n * @throws Error if not initialized\n */\nexport function getEngine(): EztraEngine {\n if (!engineInstance) {\n throw new Error('Engine not initialized. Call initializeEngine() first.');\n }\n\n return engineInstance;\n}\n\n/**\n * Check if engine is initialized\n */\nexport function isEngineInitialized(): boolean {\n return engineInstance !== null && engineInstance.isInitialized();\n}\n\n/**\n * Reset engine (for testing)\n */\nexport async function resetEngine(): Promise<void> {\n if (engineInstance) {\n await engineInstance.destroy();\n engineInstance = null;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@eztra.services/engine",
3
+ "version": "0.1.7",
4
+ "description": "Eztra Engine - Composable controller system",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "keywords": [
18
+ "engine",
19
+ "controller",
20
+ "state-management"
21
+ ],
22
+ "author": "Eztra Team",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "@eztra/controller": "1.0.0",
26
+ "@eztra/network-controller": "1.0.0",
27
+ "@eztra/storage": "1.0.0",
28
+ "@eztra/wallet-controller": "1.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.0.0",
32
+ "tsup": "^8.0.0",
33
+ "typescript": "^5.9.3",
34
+ "vitest": "^3.2.4"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "dev": "tsup --watch",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "typecheck": "tsc --noEmit",
42
+ "clean": "rm -rf dist"
43
+ }
44
+ }