taro-bluetooth-print 2.9.0 → 2.9.2
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/CHANGELOG.md +16 -1
- package/README.md +53 -4
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/types/core/di/Container.d.ts +84 -0
- package/dist/types/core/di/Tokens.d.ts +29 -0
- package/dist/types/core/di/index.d.ts +3 -0
- package/dist/types/core/event/EventBus.d.ts +66 -0
- package/dist/types/core/event/index.d.ts +2 -0
- package/dist/types/core/index.d.ts +5 -4
- package/dist/types/core/plugin/PluginManager.d.ts +64 -0
- package/dist/types/core/plugin/index.d.ts +2 -0
- package/dist/types/device/MultiPrinterManager.d.ts +2 -0
- package/dist/types/factory/di-factory.d.ts +52 -0
- package/dist/types/index.d.ts +5 -1
- package/dist/types/providers/ServiceProvider.d.ts +56 -0
- package/dist/types/providers/index.d.ts +2 -0
- package/dist/types/template/TemplateEngine.d.ts +24 -68
- package/dist/types/template/engines/TemplateRenderer.d.ts +71 -0
- package/dist/types/template/parsers/TemplateParser.d.ts +23 -0
- package/dist/types/utils/index.d.ts +8 -0
- package/dist/types/utils/logger.d.ts +4 -3
- package/dist/types/utils/outputLimiter.d.ts +87 -0
- package/dist/types/utils/validation.d.ts +11 -309
- package/dist/types/utils/validators/array.d.ts +19 -0
- package/dist/types/utils/validators/buffer.d.ts +18 -0
- package/dist/types/utils/validators/chain.d.ts +31 -0
- package/dist/types/utils/validators/common.d.ts +22 -0
- package/dist/types/utils/validators/number.d.ts +20 -0
- package/dist/types/utils/validators/object.d.ts +24 -0
- package/dist/types/utils/validators/printer.d.ts +40 -0
- package/dist/types/utils/validators/types.d.ts +125 -0
- package/dist/types/utils/validators/uuid.d.ts +23 -0
- package/package.json +1 -1
- package/src/core/BluetoothPrinter.ts +2 -1
- package/src/core/di/Container.ts +332 -0
- package/src/core/di/Tokens.ts +45 -0
- package/src/core/di/index.ts +3 -0
- package/src/core/event/EventBus.ts +251 -0
- package/src/core/event/index.ts +2 -0
- package/src/core/index.ts +10 -4
- package/src/core/plugin/PluginManager.ts +161 -0
- package/src/core/plugin/index.ts +2 -0
- package/src/device/MultiPrinterManager.ts +15 -6
- package/src/factory/di-factory.ts +61 -0
- package/src/index.ts +50 -1
- package/src/providers/ServiceProvider.ts +213 -0
- package/src/providers/index.ts +2 -0
- package/src/template/TemplateEngine.ts +27 -792
- package/src/template/engines/TemplateRenderer.ts +762 -0
- package/src/template/parsers/TemplateParser.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/logger.ts +17 -4
- package/src/utils/outputLimiter.ts +227 -0
- package/src/utils/validation.ts +21 -1138
- package/src/utils/validators/array.ts +95 -0
- package/src/utils/validators/buffer.ts +81 -0
- package/src/utils/validators/chain.ts +181 -0
- package/src/utils/validators/common.ts +216 -0
- package/src/utils/validators/number.ts +101 -0
- package/src/utils/validators/object.ts +63 -0
- package/src/utils/validators/printer.ts +294 -0
- package/src/utils/validators/types.ts +105 -0
- package/src/utils/validators/uuid.ts +49 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 事件总线 - 提供强大的事件驱动能力
|
|
3
|
+
* 支持事件订阅、发布、一次性监听、异步处理等
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
export type EventHandler<T = any> = (payload: T) => void | Promise<void>;
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export type EventFilter<T = any> = (payload: T) => boolean;
|
|
10
|
+
|
|
11
|
+
export interface EventSubscription {
|
|
12
|
+
unsubscribe(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface EventOptions {
|
|
16
|
+
/** 优先级,数字越小优先级越高 */
|
|
17
|
+
priority?: number;
|
|
18
|
+
/** 过滤器 */
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
filter?: EventFilter<any>;
|
|
21
|
+
/** 是否只监听一次 */
|
|
22
|
+
once?: boolean;
|
|
23
|
+
/** 超时时间(毫秒) */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
interface EventListener<T = any> {
|
|
29
|
+
handler: EventHandler<T>;
|
|
30
|
+
options: Required<Pick<EventOptions, 'priority'>> & Omit<EventOptions, 'priority'>;
|
|
31
|
+
id: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class EventBus {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
private listeners = new Map<string | symbol, EventListener<any>[]>();
|
|
37
|
+
private listenerIdCounter = 0;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 订阅事件
|
|
41
|
+
*/
|
|
42
|
+
on<T>(
|
|
43
|
+
event: string | symbol,
|
|
44
|
+
handler: EventHandler<T>,
|
|
45
|
+
options: EventOptions = {}
|
|
46
|
+
): EventSubscription {
|
|
47
|
+
const id = this.generateListenerId();
|
|
48
|
+
const listener: EventListener<T> = {
|
|
49
|
+
handler,
|
|
50
|
+
options: {
|
|
51
|
+
priority: options.priority ?? 100,
|
|
52
|
+
filter: options.filter,
|
|
53
|
+
once: options.once ?? false,
|
|
54
|
+
timeout: options.timeout,
|
|
55
|
+
},
|
|
56
|
+
id,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const eventKey = String(event);
|
|
60
|
+
if (!this.listeners.has(eventKey)) {
|
|
61
|
+
this.listeners.set(eventKey, []);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const listeners = this.listeners.get(eventKey)!;
|
|
65
|
+
listeners.push(listener);
|
|
66
|
+
|
|
67
|
+
// 按优先级排序
|
|
68
|
+
listeners.sort((a, b) => a.options.priority - b.options.priority);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
unsubscribe: () => {
|
|
72
|
+
this.off(event, handler);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 只监听一次
|
|
79
|
+
*/
|
|
80
|
+
once<T>(
|
|
81
|
+
event: string | symbol,
|
|
82
|
+
handler: EventHandler<T>,
|
|
83
|
+
options: Omit<EventOptions, 'once'> = {}
|
|
84
|
+
): EventSubscription {
|
|
85
|
+
return this.on(event, handler, { ...options, once: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 取消订阅
|
|
90
|
+
*/
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
off<T>(event: string | symbol, handler: EventHandler<T>): void {
|
|
93
|
+
const eventKey = String(event);
|
|
94
|
+
const listeners = this.listeners.get(eventKey);
|
|
95
|
+
if (!listeners) return;
|
|
96
|
+
|
|
97
|
+
const index = listeners.findIndex(l => l.handler === handler);
|
|
98
|
+
if (index !== -1) {
|
|
99
|
+
listeners.splice(index, 1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (listeners.length === 0) {
|
|
103
|
+
this.listeners.delete(eventKey);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 发布事件
|
|
109
|
+
*/
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
+
async emit<T>(event: string | symbol, payload: T): Promise<void> {
|
|
112
|
+
const eventKey = String(event);
|
|
113
|
+
const listeners = this.listeners.get(eventKey);
|
|
114
|
+
if (!listeners || listeners.length === 0) return;
|
|
115
|
+
|
|
116
|
+
const toRemove: number[] = [];
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < listeners.length; i++) {
|
|
119
|
+
const listener = listeners[i];
|
|
120
|
+
if (!listener) continue;
|
|
121
|
+
|
|
122
|
+
// 应用过滤器
|
|
123
|
+
if (listener.options.filter && !listener.options.filter(payload)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
if (listener.options.timeout) {
|
|
129
|
+
await this.executeWithTimeout(listener.handler, payload, listener.options.timeout);
|
|
130
|
+
} else {
|
|
131
|
+
await listener.handler(payload);
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(`Error in event handler for "${eventKey}":`, error);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 标记一次性监听器
|
|
138
|
+
if (listener.options.once) {
|
|
139
|
+
toRemove.push(i);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 移除一次性监听器
|
|
144
|
+
for (let i = toRemove.length - 1; i >= 0; i--) {
|
|
145
|
+
const index = toRemove[i];
|
|
146
|
+
if (index !== undefined) {
|
|
147
|
+
listeners.splice(index, 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (listeners.length === 0) {
|
|
152
|
+
this.listeners.delete(eventKey);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 同步发布事件(不等待处理完成)
|
|
158
|
+
*/
|
|
159
|
+
emitSync<T>(event: string | symbol, payload: T): void {
|
|
160
|
+
this.emit(event, payload).catch(error => {
|
|
161
|
+
console.error(`Error in async event handler for "${String(event)}":`, error);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 等待特定事件
|
|
167
|
+
*/
|
|
168
|
+
waitFor<T>(
|
|
169
|
+
event: string | symbol,
|
|
170
|
+
timeout?: number,
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
172
|
+
_filter?: EventFilter<T>
|
|
173
|
+
): Promise<T> {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
const timer = timeout
|
|
176
|
+
? setTimeout(() => {
|
|
177
|
+
subscription.unsubscribe();
|
|
178
|
+
reject(new Error(`Timeout waiting for event "${String(event)}"`));
|
|
179
|
+
}, timeout)
|
|
180
|
+
: null;
|
|
181
|
+
|
|
182
|
+
const subscription = this.once<T>(event, payload => {
|
|
183
|
+
if (timer) clearTimeout(timer);
|
|
184
|
+
resolve(payload);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 检查是否有监听器
|
|
191
|
+
*/
|
|
192
|
+
hasListeners(event: string | symbol): boolean {
|
|
193
|
+
const listeners = this.listeners.get(String(event));
|
|
194
|
+
return !!listeners && listeners.length > 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 获取监听器数量
|
|
199
|
+
*/
|
|
200
|
+
listenerCount(event: string | symbol): number {
|
|
201
|
+
const listeners = this.listeners.get(String(event));
|
|
202
|
+
return listeners?.length ?? 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 移除所有监听器
|
|
207
|
+
*/
|
|
208
|
+
removeAllListeners(event?: string | symbol): void {
|
|
209
|
+
if (event) {
|
|
210
|
+
this.listeners.delete(String(event));
|
|
211
|
+
} else {
|
|
212
|
+
this.listeners.clear();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 获取所有事件名称
|
|
218
|
+
*/
|
|
219
|
+
eventNames(): (string | symbol)[] {
|
|
220
|
+
return Array.from(this.listeners.keys());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private generateListenerId(): string {
|
|
224
|
+
return `listener_${++this.listenerIdCounter}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private executeWithTimeout<T>(
|
|
228
|
+
handler: EventHandler<T>,
|
|
229
|
+
payload: T,
|
|
230
|
+
timeout: number
|
|
231
|
+
): Promise<void> {
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
const timer = setTimeout(() => {
|
|
234
|
+
reject(new Error(`Event handler timed out after ${timeout}ms`));
|
|
235
|
+
}, timeout);
|
|
236
|
+
|
|
237
|
+
Promise.resolve(handler(payload))
|
|
238
|
+
.then(() => {
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
resolve();
|
|
241
|
+
})
|
|
242
|
+
.catch(error => {
|
|
243
|
+
clearTimeout(timer);
|
|
244
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 全局事件总线实例
|
|
251
|
+
export const globalEventBus = new EventBus();
|
package/src/core/index.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 核心架构模块
|
|
3
|
+
* 提供依赖注入、事件总线、插件管理等核心能力
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
6
|
+
// 依赖注入
|
|
7
|
+
export * from './di';
|
|
8
|
+
|
|
9
|
+
// 事件总线
|
|
10
|
+
export * from './event';
|
|
11
|
+
|
|
12
|
+
// 插件管理
|
|
13
|
+
export * from './plugin';
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { EventBus } from '../event/EventBus';
|
|
2
|
+
import type { Container } from '../di/Container';
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
export interface PluginContext {
|
|
6
|
+
/** 事件总线 */
|
|
7
|
+
eventBus: EventBus;
|
|
8
|
+
/** DI容器 */
|
|
9
|
+
container: Container;
|
|
10
|
+
/** 配置 */
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
config: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Plugin {
|
|
16
|
+
/** 插件名称 */
|
|
17
|
+
name: string;
|
|
18
|
+
/** 插件版本 */
|
|
19
|
+
version: string;
|
|
20
|
+
/** 插件依赖 */
|
|
21
|
+
dependencies?: string[];
|
|
22
|
+
/** 安装插件 */
|
|
23
|
+
install(context: PluginContext): void | Promise<void>;
|
|
24
|
+
/** 卸载插件 */
|
|
25
|
+
uninstall?(context: PluginContext): void | Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PluginRegistration {
|
|
29
|
+
plugin: Plugin;
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
config: Record<string, unknown>;
|
|
32
|
+
installed: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class PluginManager {
|
|
36
|
+
private plugins = new Map<string, PluginRegistration>();
|
|
37
|
+
private context: PluginContext;
|
|
38
|
+
|
|
39
|
+
constructor(context: PluginContext) {
|
|
40
|
+
this.context = context;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 注册插件
|
|
45
|
+
*/
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
register(plugin: Plugin, config: Record<string, unknown> = {}): void {
|
|
48
|
+
if (this.plugins.has(plugin.name)) {
|
|
49
|
+
throw new Error(`Plugin "${plugin.name}" is already registered`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.plugins.set(plugin.name, {
|
|
53
|
+
plugin,
|
|
54
|
+
config,
|
|
55
|
+
installed: false,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 安装插件
|
|
61
|
+
*/
|
|
62
|
+
async install(pluginName: string): Promise<void> {
|
|
63
|
+
const registration = this.plugins.get(pluginName);
|
|
64
|
+
if (!registration) {
|
|
65
|
+
throw new Error(`Plugin "${pluginName}" not found`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (registration.installed) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 检查依赖
|
|
73
|
+
if (registration.plugin.dependencies) {
|
|
74
|
+
for (const dep of registration.plugin.dependencies) {
|
|
75
|
+
if (!this.plugins.has(dep)) {
|
|
76
|
+
throw new Error(`Plugin "${pluginName}" depends on "${dep}" which is not registered`);
|
|
77
|
+
}
|
|
78
|
+
await this.install(dep);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 安装插件
|
|
83
|
+
const context: PluginContext = {
|
|
84
|
+
...this.context,
|
|
85
|
+
config: { ...this.context.config, ...registration.config },
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
await registration.plugin.install(context);
|
|
89
|
+
registration.installed = true;
|
|
90
|
+
|
|
91
|
+
// 发布插件安装事件
|
|
92
|
+
await this.context.eventBus.emit('plugin:installed', {
|
|
93
|
+
name: pluginName,
|
|
94
|
+
version: registration.plugin.version,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 卸载插件
|
|
100
|
+
*/
|
|
101
|
+
async uninstall(pluginName: string): Promise<void> {
|
|
102
|
+
const registration = this.plugins.get(pluginName);
|
|
103
|
+
if (!registration || !registration.installed) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 检查是否有其他插件依赖此插件
|
|
108
|
+
for (const [name, reg] of this.plugins) {
|
|
109
|
+
if (reg.installed && reg.plugin.dependencies?.includes(pluginName)) {
|
|
110
|
+
throw new Error(`Cannot uninstall "${pluginName}" because "${name}" depends on it`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (registration.plugin.uninstall) {
|
|
115
|
+
await registration.plugin.uninstall(this.context);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
registration.installed = false;
|
|
119
|
+
|
|
120
|
+
await this.context.eventBus.emit('plugin:uninstalled', { name: pluginName });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 批量安装插件
|
|
125
|
+
*/
|
|
126
|
+
async installAll(): Promise<void> {
|
|
127
|
+
for (const [name] of this.plugins) {
|
|
128
|
+
await this.install(name);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 获取插件
|
|
134
|
+
*/
|
|
135
|
+
getPlugin(name: string): Plugin | undefined {
|
|
136
|
+
return this.plugins.get(name)?.plugin;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 检查插件是否已安装
|
|
141
|
+
*/
|
|
142
|
+
isInstalled(name: string): boolean {
|
|
143
|
+
return this.plugins.get(name)?.installed ?? false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 获取所有已安装的插件
|
|
148
|
+
*/
|
|
149
|
+
getInstalledPlugins(): Plugin[] {
|
|
150
|
+
return Array.from(this.plugins.values())
|
|
151
|
+
.filter(r => r.installed)
|
|
152
|
+
.map(r => r.plugin);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 获取所有已注册的插件
|
|
157
|
+
*/
|
|
158
|
+
getRegisteredPlugins(): Plugin[] {
|
|
159
|
+
return Array.from(this.plugins.values()).map(r => r.plugin);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -44,6 +44,8 @@ export interface PrinterConnection {
|
|
|
44
44
|
connectedAt: number;
|
|
45
45
|
/** Last activity timestamp */
|
|
46
46
|
lastActivity?: number;
|
|
47
|
+
/** Error handler reference (for cleanup) */
|
|
48
|
+
errorHandler?: (error: Error) => void;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/**
|
|
@@ -200,14 +202,15 @@ export class MultiPrinterManager {
|
|
|
200
202
|
try {
|
|
201
203
|
const printer = new BluetoothPrinter();
|
|
202
204
|
|
|
203
|
-
//
|
|
204
|
-
printer.on('error', error => {
|
|
205
|
-
this.emit('printer-error', { printerId, error });
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Connect
|
|
205
|
+
// Connect first so we know it works before storing
|
|
209
206
|
await printer.connect(actualDeviceId);
|
|
210
207
|
|
|
208
|
+
// Set up error handler and store reference for cleanup
|
|
209
|
+
const errorHandler = (error: Error) => {
|
|
210
|
+
this.emit('printer-error', { printerId, error });
|
|
211
|
+
};
|
|
212
|
+
printer.on('error', errorHandler);
|
|
213
|
+
|
|
211
214
|
const connection: PrinterConnection = {
|
|
212
215
|
printerId,
|
|
213
216
|
deviceId: actualDeviceId,
|
|
@@ -215,6 +218,7 @@ export class MultiPrinterManager {
|
|
|
215
218
|
printer,
|
|
216
219
|
connectedAt: Date.now(),
|
|
217
220
|
lastActivity: Date.now(),
|
|
221
|
+
errorHandler,
|
|
218
222
|
};
|
|
219
223
|
|
|
220
224
|
this.printers.set(printerId, connection);
|
|
@@ -242,6 +246,11 @@ export class MultiPrinterManager {
|
|
|
242
246
|
|
|
243
247
|
this.logger.info(`Disconnecting printer "${printerId}"`);
|
|
244
248
|
|
|
249
|
+
// Remove error handler to prevent memory leak
|
|
250
|
+
if (connection.errorHandler) {
|
|
251
|
+
connection.printer.off('error', connection.errorHandler);
|
|
252
|
+
}
|
|
253
|
+
|
|
245
254
|
try {
|
|
246
255
|
await connection.printer.disconnect();
|
|
247
256
|
} catch (error) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DI 版本的 BluetoothPrinter 工厂
|
|
3
|
+
*
|
|
4
|
+
* 使用依赖注入容器创建打印机实例
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { BluetoothPrinter } from '@/core/BluetoothPrinter';
|
|
8
|
+
import { createServiceProvider } from '@/providers';
|
|
9
|
+
import type { ServiceProviderOptions } from '@/providers';
|
|
10
|
+
import type { IConnectionManager, IPrintJobManager, ICommandBuilder } from '@/services/interfaces';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 创建打印机实例的选项
|
|
14
|
+
*/
|
|
15
|
+
export interface CreatePrinterOptions extends ServiceProviderOptions {
|
|
16
|
+
/** 自定义连接管理器 */
|
|
17
|
+
connectionManager?: IConnectionManager;
|
|
18
|
+
/** 自定义打印任务管理器 */
|
|
19
|
+
printJobManager?: IPrintJobManager;
|
|
20
|
+
/** 自定义命令构建器 */
|
|
21
|
+
commandBuilder?: ICommandBuilder;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 使用依赖注入创建 BluetoothPrinter 实例
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // 基本用法
|
|
30
|
+
* const printer = createPrinter();
|
|
31
|
+
*
|
|
32
|
+
* // 使用自定义配置
|
|
33
|
+
* const printer = createPrinter({
|
|
34
|
+
* config: { debug: true },
|
|
35
|
+
* useGlobalEventBus: true
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function createPrinter(options: CreatePrinterOptions = {}): BluetoothPrinter {
|
|
40
|
+
// 注册服务(如果尚未注册)
|
|
41
|
+
const provider = createServiceProvider(options);
|
|
42
|
+
|
|
43
|
+
// 使用提供的实例或从容器解析(需要类型断言)
|
|
44
|
+
const connectionManager =
|
|
45
|
+
options.connectionManager || (provider.getConnectionManager() as IConnectionManager);
|
|
46
|
+
const printJobManager =
|
|
47
|
+
options.printJobManager || (provider.getPrintJobManager() as IPrintJobManager);
|
|
48
|
+
const commandBuilder =
|
|
49
|
+
options.commandBuilder || (provider.getCommandBuilder() as ICommandBuilder);
|
|
50
|
+
|
|
51
|
+
return new BluetoothPrinter(connectionManager, printJobManager, commandBuilder);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 获取服务提供者(用于访问所有服务)
|
|
56
|
+
*/
|
|
57
|
+
export function getServiceProvider(options: ServiceProviderOptions = {}) {
|
|
58
|
+
return createServiceProvider(options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { createServiceProvider, ServiceProviderOptions };
|
package/src/index.ts
CHANGED
|
@@ -84,6 +84,14 @@ export { Logger, LogLevel } from './utils/logger';
|
|
|
84
84
|
export { Encoding } from './utils/encoding';
|
|
85
85
|
export { ImageProcessing } from './utils/image';
|
|
86
86
|
export { PlatformType, detectPlatform, isPlatformSupported } from './utils/platform';
|
|
87
|
+
export {
|
|
88
|
+
truncateString,
|
|
89
|
+
truncateForLog,
|
|
90
|
+
batchProcess,
|
|
91
|
+
createLimitedLogger,
|
|
92
|
+
generateSummary,
|
|
93
|
+
} from './utils/outputLimiter';
|
|
94
|
+
export type { TruncateOptions } from './utils/outputLimiter';
|
|
87
95
|
|
|
88
96
|
// Error handling - 错误处理
|
|
89
97
|
export { BluetoothPrintError, ErrorCode } from './errors/BluetoothError';
|
|
@@ -99,6 +107,48 @@ export {
|
|
|
99
107
|
type PrinterFactoryOptions,
|
|
100
108
|
} from './factory';
|
|
101
109
|
|
|
110
|
+
// DI Factory - 依赖注入工厂
|
|
111
|
+
export {
|
|
112
|
+
createPrinter,
|
|
113
|
+
getServiceProvider,
|
|
114
|
+
createServiceProvider,
|
|
115
|
+
type CreatePrinterOptions,
|
|
116
|
+
type ServiceProviderOptions,
|
|
117
|
+
} from './factory/di-factory';
|
|
118
|
+
|
|
119
|
+
// Core Architecture - 核心架构
|
|
120
|
+
export {
|
|
121
|
+
Container,
|
|
122
|
+
rootContainer,
|
|
123
|
+
injectable,
|
|
124
|
+
inject,
|
|
125
|
+
EventBus,
|
|
126
|
+
globalEventBus,
|
|
127
|
+
PluginManager,
|
|
128
|
+
} from './core';
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
ADAPTER_TOKEN,
|
|
132
|
+
DRIVER_TOKEN,
|
|
133
|
+
DEVICE_MANAGER_TOKEN,
|
|
134
|
+
CONNECTION_MANAGER_TOKEN,
|
|
135
|
+
PRINT_JOB_MANAGER_TOKEN,
|
|
136
|
+
PRINT_QUEUE_TOKEN,
|
|
137
|
+
OFFLINE_CACHE_TOKEN,
|
|
138
|
+
CONFIG_MANAGER_TOKEN,
|
|
139
|
+
LOGGER_TOKEN,
|
|
140
|
+
EVENT_BUS_TOKEN,
|
|
141
|
+
PLUGIN_MANAGER_TOKEN,
|
|
142
|
+
PERFORMANCE_MONITOR_TOKEN,
|
|
143
|
+
COMMAND_BUILDER_TOKEN,
|
|
144
|
+
PRINTER_STATUS_TOKEN,
|
|
145
|
+
PRINT_HISTORY_TOKEN,
|
|
146
|
+
PRINT_STATISTICS_TOKEN,
|
|
147
|
+
CLOUD_PRINT_MANAGER_TOKEN,
|
|
148
|
+
SCHEDULED_RETRY_MANAGER_TOKEN,
|
|
149
|
+
BATCH_PRINT_MANAGER_TOKEN,
|
|
150
|
+
} from './core';
|
|
151
|
+
|
|
102
152
|
// Configuration - 配置
|
|
103
153
|
export { DEFAULT_CONFIG, mergeConfig } from './config/PrinterConfig';
|
|
104
154
|
export type {
|
|
@@ -112,7 +162,6 @@ export { PrinterConfigManager, printerConfigManager } from './config/PrinterConf
|
|
|
112
162
|
export type { SavedPrinter, GlobalConfig, IConfigStorage } from './config/PrinterConfigManager';
|
|
113
163
|
|
|
114
164
|
// Plugin System - 插件系统
|
|
115
|
-
export { PluginManager } from './plugins/PluginManager';
|
|
116
165
|
export { createLoggingPlugin, createRetryPlugin } from './plugins';
|
|
117
166
|
export type { Plugin, PluginHooks, PluginOptions, PluginFactory } from './plugins/types';
|
|
118
167
|
|