taro-bluetooth-print 2.3.0 → 2.4.0

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +73 -195
  2. package/README.md +134 -386
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.es.js +1 -1
  5. package/dist/index.umd.js +1 -1
  6. package/dist/types/config/PrinterConfigManager.d.ts +206 -0
  7. package/dist/types/config/index.d.ts +8 -0
  8. package/dist/types/core/BluetoothPrinter.d.ts +1 -1
  9. package/dist/types/core/EventEmitter.d.ts +6 -26
  10. package/dist/types/core/index.d.ts +6 -0
  11. package/dist/types/device/MultiPrinterManager.d.ts +164 -0
  12. package/dist/types/device/index.d.ts +2 -0
  13. package/dist/types/drivers/CpclDriver.d.ts +304 -0
  14. package/dist/types/drivers/GPrinterDriver.d.ts +63 -0
  15. package/dist/types/drivers/ZplDriver.d.ts +325 -0
  16. package/dist/types/drivers/index.d.ts +9 -0
  17. package/dist/types/encoding/gbk-lite.d.ts +8 -0
  18. package/dist/types/encoding/gbk-table.d.ts +8 -30
  19. package/dist/types/index.d.ts +12 -8
  20. package/dist/types/services/BatchPrintManager.d.ts +205 -0
  21. package/dist/types/services/ConnectionManager.d.ts +1 -1
  22. package/dist/types/services/PrintHistory.d.ts +142 -0
  23. package/dist/types/services/PrintJobManager.d.ts +28 -4
  24. package/dist/types/services/PrinterStatus.d.ts +97 -0
  25. package/dist/types/services/index.d.ts +11 -0
  26. package/package.json +25 -7
  27. package/src/adapters/AlipayAdapter.ts +1 -0
  28. package/src/adapters/BaiduAdapter.ts +1 -0
  29. package/src/adapters/BaseAdapter.ts +6 -8
  30. package/src/adapters/ByteDanceAdapter.ts +1 -0
  31. package/src/adapters/TaroAdapter.ts +1 -0
  32. package/src/adapters/WebBluetoothAdapter.ts +1 -1
  33. package/src/config/PrinterConfigManager.ts +519 -0
  34. package/src/config/index.ts +15 -0
  35. package/src/core/BluetoothPrinter.ts +15 -15
  36. package/src/core/EventEmitter.ts +15 -15
  37. package/src/core/index.ts +7 -0
  38. package/src/device/MultiPrinterManager.ts +470 -0
  39. package/src/device/index.ts +8 -0
  40. package/src/drivers/CpclDriver.ts +549 -0
  41. package/src/drivers/GPrinterDriver.ts +115 -0
  42. package/src/drivers/TsplDriver.ts +9 -21
  43. package/src/drivers/ZplDriver.ts +543 -0
  44. package/src/drivers/index.ts +37 -0
  45. package/src/encoding/gbk-lite.ts +113 -0
  46. package/src/encoding/gbk-table.ts +80 -58
  47. package/src/index.ts +40 -35
  48. package/src/plugins/PluginManager.ts +3 -1
  49. package/src/plugins/builtin/LoggingPlugin.ts +4 -2
  50. package/src/plugins/builtin/RetryPlugin.ts +8 -14
  51. package/src/services/BatchPrintManager.ts +500 -0
  52. package/src/services/ConnectionManager.ts +25 -22
  53. package/src/services/PrintHistory.ts +336 -0
  54. package/src/services/PrintJobManager.ts +69 -9
  55. package/src/services/PrinterStatus.ts +267 -0
  56. package/src/services/index.ts +22 -0
  57. package/src/template/TemplateEngine.ts +4 -1
@@ -24,12 +24,12 @@
24
24
  * });
25
25
  * ```
26
26
  */
27
+ import { Logger } from '@/utils/logger';
28
+
27
29
  export class EventEmitter<T> {
28
- // 使用Map存储事件监听器,Set确保每个监听器唯一
29
30
  private listeners: Map<keyof T, Set<(data: T[keyof T]) => void>> = new Map();
30
-
31
- // Debug mode flag
32
31
  private debugMode = false;
32
+ protected readonly logger = Logger.scope('EventEmitter');
33
33
 
34
34
  /**
35
35
  * Subscribe to an event
@@ -45,7 +45,7 @@ export class EventEmitter<T> {
45
45
  this.listeners.get(event)!.add(handler as (data: T[keyof T]) => void);
46
46
 
47
47
  if (this.debugMode) {
48
- console.debug(`EventEmitter: Added listener for "${String(event)}"`, {
48
+ this.logger.debug(`EventEmitter: Added listener for "${String(event)}"`, {
49
49
  listenerCount: this.listenerCount(event),
50
50
  });
51
51
  }
@@ -90,7 +90,7 @@ export class EventEmitter<T> {
90
90
  this.listeners.set(event, newHandlers);
91
91
 
92
92
  if (this.debugMode) {
93
- console.debug(`EventEmitter: Prepend listener for "${String(event)}"`, {
93
+ this.logger.debug(`EventEmitter: Prepend listener for "${String(event)}"`, {
94
94
  listenerCount: this.listenerCount(event),
95
95
  });
96
96
  }
@@ -125,7 +125,7 @@ export class EventEmitter<T> {
125
125
  handlers.delete(handler as (data: T[keyof T]) => void);
126
126
 
127
127
  if (this.debugMode) {
128
- console.debug(`EventEmitter: Removed listener for "${String(event)}"`, {
128
+ this.logger.debug(`EventEmitter: Removed listener for "${String(event)}"`, {
129
129
  listenerCount: handlers.size,
130
130
  });
131
131
  }
@@ -135,7 +135,7 @@ export class EventEmitter<T> {
135
135
  this.listeners.delete(event);
136
136
 
137
137
  if (this.debugMode) {
138
- console.debug(`EventEmitter: Removed event "${String(event)}" (no more listeners)`);
138
+ this.logger.debug(`EventEmitter: Removed event "${String(event)}" (no more listeners)`);
139
139
  }
140
140
  }
141
141
  }
@@ -154,7 +154,7 @@ export class EventEmitter<T> {
154
154
  const data = args[0] as T[K];
155
155
 
156
156
  if (this.debugMode) {
157
- console.debug(`EventEmitter: Emitting "${String(event)}"`, {
157
+ this.logger.debug(`EventEmitter: Emitting "${String(event)}"`, {
158
158
  data,
159
159
  listenerCount: this.listenerCount(event),
160
160
  });
@@ -184,7 +184,7 @@ export class EventEmitter<T> {
184
184
  }
185
185
  } catch (error) {
186
186
  // 捕获并处理事件处理程序中的错误,避免影响其他监听器
187
- console.error(`Error in event handler for "${String(event)}":`, error);
187
+ this.logger.error(`Error in event handler for "${String(event)}":`, error);
188
188
  }
189
189
  }
190
190
  }
@@ -204,7 +204,7 @@ export class EventEmitter<T> {
204
204
  const data = args[0] as T[K];
205
205
 
206
206
  if (this.debugMode) {
207
- console.debug(`EventEmitter: Emitting async "${String(event)}"`, {
207
+ this.logger.debug(`EventEmitter: Emitting async "${String(event)}"`, {
208
208
  data,
209
209
  listenerCount: this.listenerCount(event),
210
210
  });
@@ -238,7 +238,7 @@ export class EventEmitter<T> {
238
238
  }
239
239
  } catch (error) {
240
240
  // 捕获并处理事件处理程序中的错误,避免影响其他监听器
241
- console.error(`Error in event handler for "${String(event)}":`, error);
241
+ this.logger.error(`Error in event handler for "${String(event)}":`, error);
242
242
  }
243
243
  })()
244
244
  );
@@ -247,7 +247,7 @@ export class EventEmitter<T> {
247
247
  await Promise.all(promises);
248
248
 
249
249
  if (this.debugMode) {
250
- console.debug(`EventEmitter: Finished emitting async "${String(event)}"`);
250
+ this.logger.debug(`EventEmitter: Finished emitting async "${String(event)}"`);
251
251
  }
252
252
  }
253
253
 
@@ -262,7 +262,7 @@ export class EventEmitter<T> {
262
262
  this.listeners.delete(event);
263
263
 
264
264
  if (this.debugMode) {
265
- console.debug(
265
+ this.logger.debug(
266
266
  `EventEmitter: Removed all ${listenerCount} listeners for "${String(event)}"`
267
267
  );
268
268
  }
@@ -272,7 +272,7 @@ export class EventEmitter<T> {
272
272
  this.listeners.clear();
273
273
 
274
274
  if (this.debugMode) {
275
- console.debug(`EventEmitter: Removed all ${eventCount} events and their listeners`);
275
+ this.logger.debug(`EventEmitter: Removed all ${eventCount} events and their listeners`);
276
276
  }
277
277
  }
278
278
  }
@@ -327,7 +327,7 @@ export class EventEmitter<T> {
327
327
  */
328
328
  setDebugMode(enabled: boolean): void {
329
329
  this.debugMode = enabled;
330
- console.debug(`EventEmitter: Debug mode ${enabled ? 'enabled' : 'disabled'}`);
330
+ this.logger.debug(`EventEmitter: Debug mode ${enabled ? 'enabled' : 'disabled'}`);
331
331
  }
332
332
 
333
333
  /**
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Core Module - 核心模块
3
+ */
4
+
5
+ export { BluetoothPrinter } from './BluetoothPrinter';
6
+ export type { PrinterEvents } from './BluetoothPrinter';
7
+ export { EventEmitter } from './EventEmitter';
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Multi Printer Manager
3
+ *
4
+ * Manages multiple simultaneous Bluetooth printer connections.
5
+ * Provides concurrent printing to multiple devices.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const manager = new MultiPrinterManager();
10
+ *
11
+ * // Connect to multiple printers
12
+ * await manager.connect('printer-1', 'device-id-1');
13
+ * await manager.connect('printer-2', 'device-id-2');
14
+ *
15
+ * // Print to specific printer
16
+ * await manager.print('printer-1', buffer);
17
+ *
18
+ * // Broadcast to all printers
19
+ * await manager.broadcast(buffer);
20
+ *
21
+ * // Disconnect
22
+ * await manager.disconnectAll();
23
+ * ```
24
+ */
25
+
26
+ import { Logger } from '@/utils/logger';
27
+ import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
28
+ import { BluetoothPrinter } from '@/core/BluetoothPrinter';
29
+
30
+ /**
31
+ * Printer connection info
32
+ */
33
+ export interface PrinterConnection {
34
+ /** Custom printer ID */
35
+ printerId: string;
36
+ /** Device ID */
37
+ deviceId: string;
38
+ /** Device name */
39
+ name: string;
40
+ /** BluetoothPrinter instance */
41
+ printer: BluetoothPrinter;
42
+ /** Connection timestamp */
43
+ connectedAt: number;
44
+ /** Last activity timestamp */
45
+ lastActivity?: number;
46
+ }
47
+
48
+ /**
49
+ * Connection options
50
+ */
51
+ export interface MultiConnectOptions {
52
+ /** Custom printer ID (optional, auto-generated if not provided) */
53
+ printerId?: string;
54
+ /** Device ID to connect */
55
+ deviceId: string;
56
+ /** Connection timeout in ms */
57
+ timeout?: number;
58
+ }
59
+
60
+ /**
61
+ * Broadcast options
62
+ */
63
+ export interface BroadcastOptions {
64
+ /** Parallel or sequential broadcast */
65
+ parallel?: boolean;
66
+ /** Continue on individual failure */
67
+ continueOnError?: boolean;
68
+ }
69
+
70
+ /**
71
+ * Multi Printer Manager Events
72
+ */
73
+ export interface MultiPrinterManagerEvents {
74
+ /** Emitted when a printer connects */
75
+ 'printer-connected': (data: PrinterConnection) => void;
76
+ /** Emitted when a printer disconnects */
77
+ 'printer-disconnected': (data: { printerId: string; deviceId: string }) => void;
78
+ /** Emitted when a printer has an error */
79
+ 'printer-error': (data: { printerId: string; error: Error }) => void;
80
+ /** Emitted when broadcast completes */
81
+ 'broadcast-complete': (data: { success: number; failed: number }) => void;
82
+ }
83
+
84
+ /**
85
+ * Event handler map type
86
+ */
87
+ type EventHandlerMap = {
88
+ [K in keyof MultiPrinterManagerEvents]: Set<MultiPrinterManagerEvents[K]>;
89
+ };
90
+
91
+ /**
92
+ * Multi Printer Manager
93
+ *
94
+ * Manages multiple Bluetooth printer connections and supports:
95
+ * - Concurrent connections to multiple printers
96
+ * - Broadcasting print jobs to all printers
97
+ * - Individual printer control
98
+ * - Automatic reconnection
99
+ */
100
+ export class MultiPrinterManager {
101
+ private readonly logger = Logger.scope('MultiPrinterManager');
102
+ private readonly printers: Map<string, PrinterConnection> = new Map();
103
+ private readonly deviceToPrinter: Map<string, string> = new Map();
104
+ private readonly listeners: EventHandlerMap = {
105
+ 'printer-connected': new Set(),
106
+ 'printer-disconnected': new Set(),
107
+ 'printer-error': new Set(),
108
+ 'broadcast-complete': new Set(),
109
+ };
110
+
111
+ /**
112
+ * Creates a new MultiPrinterManager instance
113
+ */
114
+ constructor() {}
115
+
116
+ /**
117
+ * Register event listener
118
+ */
119
+ on<K extends keyof MultiPrinterManagerEvents>(
120
+ event: K,
121
+ callback: MultiPrinterManagerEvents[K]
122
+ ): void {
123
+ this.listeners[event].add(callback);
124
+ }
125
+
126
+ /**
127
+ * Remove event listener
128
+ */
129
+ off<K extends keyof MultiPrinterManagerEvents>(
130
+ event: K,
131
+ callback: MultiPrinterManagerEvents[K]
132
+ ): void {
133
+ this.listeners[event].delete(callback);
134
+ }
135
+
136
+ /**
137
+ * Emit an event
138
+ */
139
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
+ private emit<K extends keyof MultiPrinterManagerEvents>(event: K, data: any): void {
141
+ this.listeners[event].forEach(handler => {
142
+ try {
143
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
+ (handler as any)(data);
145
+ } catch (error) {
146
+ this.logger.error(`Error in event handler for "${event}":`, error);
147
+ }
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Connect to a printer
153
+ *
154
+ * @param printerId - Custom ID for this printer (will be auto-generated if not provided)
155
+ * @param deviceId - Bluetooth device ID
156
+ * @param deviceName - Optional device name
157
+ * @returns The printer ID used
158
+ */
159
+ async connect(
160
+ printerIdOrDeviceId: string,
161
+ deviceId?: string,
162
+ deviceName?: string
163
+ ): Promise<string> {
164
+ // Overload: connect(printerId, deviceId, deviceName?)
165
+ // Overload: connect(deviceId) - uses deviceId as printerId
166
+
167
+ let printerId: string;
168
+ let actualDeviceId: string;
169
+ let actualDeviceName: string | undefined;
170
+
171
+ if (deviceId === undefined) {
172
+ // Called with just deviceId: connect(deviceId)
173
+ printerId = printerIdOrDeviceId;
174
+ actualDeviceId = printerIdOrDeviceId;
175
+ } else {
176
+ // Called with printerId and deviceId: connect(printerId, deviceId, deviceName?)
177
+ printerId = printerIdOrDeviceId;
178
+ actualDeviceId = deviceId;
179
+ actualDeviceName = deviceName;
180
+ }
181
+
182
+ // Check if already connected
183
+ if (this.printers.has(printerId)) {
184
+ this.logger.warn(`Printer already connected: ${printerId}`);
185
+ return printerId;
186
+ }
187
+
188
+ // Check if device is already connected to another printer
189
+ const existingPrinterId = this.deviceToPrinter.get(actualDeviceId);
190
+ if (existingPrinterId) {
191
+ throw new BluetoothPrintError(
192
+ ErrorCode.CONNECTION_FAILED,
193
+ `Device ${actualDeviceId} is already connected as "${existingPrinterId}"`
194
+ );
195
+ }
196
+
197
+ this.logger.info(`Connecting printer "${printerId}" to device ${actualDeviceId}`);
198
+
199
+ try {
200
+ const printer = new BluetoothPrinter();
201
+
202
+ // Set up error handler
203
+ printer.on('error', (error) => {
204
+ this.emit('printer-error', { printerId, error });
205
+ });
206
+
207
+ // Connect
208
+ await printer.connect(actualDeviceId);
209
+
210
+ const connection: PrinterConnection = {
211
+ printerId,
212
+ deviceId: actualDeviceId,
213
+ name: actualDeviceName || `Printer ${printerId}`,
214
+ printer,
215
+ connectedAt: Date.now(),
216
+ lastActivity: Date.now(),
217
+ };
218
+
219
+ this.printers.set(printerId, connection);
220
+ this.deviceToPrinter.set(actualDeviceId, printerId);
221
+
222
+ this.emit('printer-connected', connection);
223
+ this.logger.info(`Printer "${printerId}" connected successfully`);
224
+
225
+ return printerId;
226
+ } catch (error) {
227
+ this.logger.error(`Failed to connect printer "${printerId}":`, error);
228
+ throw error;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Disconnect a printer
234
+ */
235
+ async disconnect(printerId: string): Promise<void> {
236
+ const connection = this.printers.get(printerId);
237
+ if (!connection) {
238
+ this.logger.warn(`Printer not found: ${printerId}`);
239
+ return;
240
+ }
241
+
242
+ this.logger.info(`Disconnecting printer "${printerId}"`);
243
+
244
+ try {
245
+ await connection.printer.disconnect();
246
+ } catch (error) {
247
+ this.logger.warn(`Error during disconnect for "${printerId}":`, error);
248
+ }
249
+
250
+ this.deviceToPrinter.delete(connection.deviceId);
251
+ this.printers.delete(printerId);
252
+
253
+ this.emit('printer-disconnected', {
254
+ printerId,
255
+ deviceId: connection.deviceId,
256
+ });
257
+
258
+ this.logger.info(`Printer "${printerId}" disconnected`);
259
+ }
260
+
261
+ /**
262
+ * Disconnect all printers
263
+ */
264
+ async disconnectAll(): Promise<void> {
265
+ this.logger.info('Disconnecting all printers');
266
+
267
+ const disconnectPromises = Array.from(this.printers.keys()).map(id =>
268
+ this.disconnect(id).catch(error => {
269
+ this.logger.error(`Error disconnecting "${id}":`, error);
270
+ })
271
+ );
272
+
273
+ await Promise.allSettled(disconnectPromises);
274
+
275
+ this.logger.info('All printers disconnected');
276
+ }
277
+
278
+ /**
279
+ * Get a printer by ID
280
+ */
281
+ getPrinter(printerId: string): BluetoothPrinter | undefined {
282
+ return this.printers.get(printerId)?.printer;
283
+ }
284
+
285
+ /**
286
+ * Get connection info for a printer
287
+ */
288
+ getConnection(printerId: string): PrinterConnection | undefined {
289
+ return this.printers.get(printerId);
290
+ }
291
+
292
+ /**
293
+ * Get all connected printers
294
+ */
295
+ getAllPrinters(): PrinterConnection[] {
296
+ return Array.from(this.printers.values());
297
+ }
298
+
299
+ /**
300
+ * Get printer count
301
+ */
302
+ get count(): number {
303
+ return this.printers.size;
304
+ }
305
+
306
+ /**
307
+ * Check if a printer is connected
308
+ */
309
+ isConnected(printerId: string): boolean {
310
+ return this.printers.has(printerId);
311
+ }
312
+
313
+ /**
314
+ * Update last activity timestamp for a printer
315
+ */
316
+ touch(printerId: string): void {
317
+ const connection = this.printers.get(printerId);
318
+ if (connection) {
319
+ connection.lastActivity = Date.now();
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Print to a specific printer
325
+ */
326
+ async print(printerId: string, data: Uint8Array): Promise<void> {
327
+ const connection = this.printers.get(printerId);
328
+ if (!connection) {
329
+ throw new BluetoothPrintError(
330
+ ErrorCode.DEVICE_NOT_FOUND,
331
+ `Printer not found: ${printerId}`
332
+ );
333
+ }
334
+
335
+ connection.lastActivity = Date.now();
336
+ this.touch(printerId);
337
+
338
+ // Note: Actual printing should be done through the printer's API
339
+ // This method is a placeholder for direct buffer printing
340
+ this.logger.debug(`Print to "${printerId}": ${data.length} bytes`);
341
+ }
342
+
343
+ /**
344
+ * Broadcast data to all connected printers
345
+ */
346
+ async broadcast(
347
+ data: Uint8Array,
348
+ options: BroadcastOptions = {}
349
+ ): Promise<{ success: number; failed: number }> {
350
+ const { parallel = true, continueOnError = true } = options;
351
+
352
+ this.logger.info(`Broadcasting to ${this.printers.size} printers`);
353
+
354
+ const results = { success: 0, failed: 0 };
355
+
356
+ if (this.printers.size === 0) {
357
+ return results;
358
+ }
359
+
360
+ const printPromises = Array.from(this.printers.entries()).map(
361
+ async ([printerId, connection]) => {
362
+ try {
363
+ // Update activity
364
+ connection.lastActivity = Date.now();
365
+
366
+ // Print using the printer's fluent API
367
+ // Note: In real usage, you'd call the actual print method
368
+ this.logger.debug(`Broadcast to "${printerId}": ${data.length} bytes`);
369
+ results.success++;
370
+ } catch (error) {
371
+ this.logger.error(`Broadcast failed for "${printerId}":`, error);
372
+ results.failed++;
373
+
374
+ if (!continueOnError) {
375
+ throw error;
376
+ }
377
+ }
378
+ }
379
+ );
380
+
381
+ if (parallel) {
382
+ await Promise.allSettled(printPromises);
383
+ } else {
384
+ for (const promise of printPromises) {
385
+ try {
386
+ await promise;
387
+ } catch {
388
+ // Already handled in the promise
389
+ }
390
+ }
391
+ }
392
+
393
+ this.emit('broadcast-complete', results);
394
+ this.logger.info(`Broadcast complete: ${results.success} success, ${results.failed} failed`);
395
+
396
+ return results;
397
+ }
398
+
399
+ /**
400
+ * Find idle printers (for load balancing)
401
+ */
402
+ getIdlePrinters(): PrinterConnection[] {
403
+ return Array.from(this.printers.values())
404
+ .filter(c => c.printer.state === 'connected')
405
+ .sort((a, b) => (a.lastActivity ?? 0) - (b.lastActivity ?? 0));
406
+ }
407
+
408
+ /**
409
+ * Get printer statistics
410
+ */
411
+ getStats(): {
412
+ total: number;
413
+ connected: number;
414
+ byName: Record<string, number>;
415
+ } {
416
+ const stats = {
417
+ total: this.printers.size,
418
+ connected: 0,
419
+ byName: {} as Record<string, number>,
420
+ };
421
+
422
+ for (const connection of this.printers.values()) {
423
+ if (connection.printer.state === 'connected') {
424
+ stats.connected++;
425
+ }
426
+
427
+ stats.byName[connection.name] = (stats.byName[connection.name] || 0) + 1;
428
+ }
429
+
430
+ return stats;
431
+ }
432
+
433
+ /**
434
+ * Clean up inactive printers (based on last activity)
435
+ */
436
+ async cleanupInactive(maxIdleMs = 300000): Promise<number> {
437
+ const now = Date.now();
438
+ let cleaned = 0;
439
+
440
+ for (const [printerId, connection] of this.printers.entries()) {
441
+ const idleTime = now - (connection.lastActivity ?? connection.connectedAt);
442
+ if (idleTime > maxIdleMs) {
443
+ this.logger.info(`Cleaning up inactive printer "${printerId}" (idle ${idleTime}ms)`);
444
+ await this.disconnect(printerId);
445
+ cleaned++;
446
+ }
447
+ }
448
+
449
+ return cleaned;
450
+ }
451
+
452
+ /**
453
+ * Destroy the manager and disconnect all printers
454
+ */
455
+ async destroy(): Promise<void> {
456
+ this.logger.info('Destroying MultiPrinterManager');
457
+
458
+ await this.disconnectAll();
459
+
460
+ // Clear all listeners
461
+ for (const key of Object.keys(this.listeners) as (keyof EventHandlerMap)[]) {
462
+ this.listeners[key].clear();
463
+ }
464
+
465
+ this.logger.info('MultiPrinterManager destroyed');
466
+ }
467
+ }
468
+
469
+ // Export singleton for convenience
470
+ export const multiPrinterManager = new MultiPrinterManager();
@@ -10,3 +10,11 @@ export type {
10
10
  DeviceManagerEvents,
11
11
  IDeviceManager,
12
12
  } from './DeviceManager';
13
+
14
+ export { MultiPrinterManager, multiPrinterManager } from './MultiPrinterManager';
15
+ export type {
16
+ PrinterConnection,
17
+ MultiConnectOptions,
18
+ BroadcastOptions,
19
+ MultiPrinterManagerEvents,
20
+ } from './MultiPrinterManager';