taro-bluetooth-print 2.2.0 → 2.3.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.
- package/CHANGELOG.md +38 -0
- package/README.md +128 -22
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -6995
- package/dist/index.umd.js +1 -1
- package/dist/types/adapters/AdapterFactory.d.ts +0 -1
- package/dist/types/adapters/AlipayAdapter.d.ts +6 -34
- package/dist/types/adapters/BaiduAdapter.d.ts +6 -34
- package/dist/types/adapters/BaseAdapter.d.ts +112 -1
- package/dist/types/adapters/ByteDanceAdapter.d.ts +6 -34
- package/dist/types/adapters/TaroAdapter.d.ts +6 -34
- package/dist/types/adapters/WebBluetoothAdapter.d.ts +0 -1
- package/dist/types/config/PrinterConfig.d.ts +0 -1
- package/dist/types/core/BluetoothPrinter.d.ts +0 -1
- package/dist/types/drivers/EscPos.d.ts +0 -1
- package/dist/types/drivers/TsplDriver.d.ts +251 -0
- package/dist/types/encoding/gbk-data.d.ts +12 -0
- package/dist/types/encoding/gbk-table.d.ts +5 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/plugins/PluginManager.d.ts +87 -0
- package/dist/types/plugins/builtin/LoggingPlugin.d.ts +14 -0
- package/dist/types/plugins/builtin/RetryPlugin.d.ts +18 -0
- package/dist/types/plugins/index.d.ts +7 -0
- package/dist/types/plugins/types.d.ts +97 -0
- package/dist/types/services/CommandBuilder.d.ts +6 -1
- package/dist/types/services/ConnectionManager.d.ts +0 -1
- package/dist/types/services/PrintJobManager.d.ts +6 -2
- package/dist/types/services/interfaces/index.d.ts +0 -1
- package/dist/types/template/TemplateEngine.d.ts +0 -1
- package/package.json +16 -18
- package/src/adapters/AlipayAdapter.ts +8 -314
- package/src/adapters/BaiduAdapter.ts +8 -312
- package/src/adapters/BaseAdapter.ts +366 -0
- package/src/adapters/ByteDanceAdapter.ts +8 -316
- package/src/adapters/TaroAdapter.ts +8 -367
- package/src/core/EventEmitter.ts +9 -6
- package/src/drivers/TsplDriver.ts +417 -0
- package/src/encoding/gbk-data.ts +1911 -0
- package/src/encoding/gbk-table.ts +22 -498
- package/src/index.ts +14 -0
- package/src/plugins/PluginManager.ts +193 -0
- package/src/plugins/builtin/LoggingPlugin.ts +97 -0
- package/src/plugins/builtin/RetryPlugin.ts +109 -0
- package/src/plugins/index.ts +10 -0
- package/src/plugins/types.ts +119 -0
- package/src/preview/PreviewRenderer.ts +7 -1
- package/src/queue/PrintQueue.ts +10 -6
- package/src/services/CommandBuilder.ts +30 -0
- package/src/services/PrintJobManager.ts +51 -35
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Manager
|
|
3
|
+
* Manages plugin lifecycle and hook execution
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Plugin, PluginHooks, PluginOptions } from './types';
|
|
7
|
+
import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
|
|
8
|
+
import { PrinterState } from '@/types';
|
|
9
|
+
import { Logger } from '@/utils/logger';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Manages plugins for BluetoothPrinter
|
|
13
|
+
*/
|
|
14
|
+
export class PluginManager {
|
|
15
|
+
private plugins: Map<string, Plugin> = new Map();
|
|
16
|
+
private readonly logger = Logger.scope('PluginManager');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Register a plugin
|
|
20
|
+
* @param plugin - Plugin to register
|
|
21
|
+
* @param options - Plugin options
|
|
22
|
+
* @throws {BluetoothPrintError} If plugin with same name already exists
|
|
23
|
+
*/
|
|
24
|
+
async register(plugin: Plugin, options?: PluginOptions): Promise<void> {
|
|
25
|
+
if (this.plugins.has(plugin.name)) {
|
|
26
|
+
throw new BluetoothPrintError(
|
|
27
|
+
ErrorCode.INVALID_CONFIGURATION,
|
|
28
|
+
`Plugin "${plugin.name}" is already registered`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.logger.info(`Registering plugin: ${plugin.name}${plugin.version ? ` v${plugin.version}` : ''}`);
|
|
33
|
+
|
|
34
|
+
if (plugin.init) {
|
|
35
|
+
await plugin.init(options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.plugins.set(plugin.name, plugin);
|
|
39
|
+
this.logger.debug(`Plugin registered: ${plugin.name}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Unregister a plugin
|
|
44
|
+
* @param name - Plugin name to unregister
|
|
45
|
+
*/
|
|
46
|
+
async unregister(name: string): Promise<void> {
|
|
47
|
+
const plugin = this.plugins.get(name);
|
|
48
|
+
if (!plugin) {
|
|
49
|
+
this.logger.warn(`Plugin not found: ${name}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (plugin.destroy) {
|
|
54
|
+
await plugin.destroy();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.plugins.delete(name);
|
|
58
|
+
this.logger.info(`Plugin unregistered: ${name}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get a registered plugin
|
|
63
|
+
* @param name - Plugin name
|
|
64
|
+
* @returns Plugin instance or undefined
|
|
65
|
+
*/
|
|
66
|
+
get(name: string): Plugin | undefined {
|
|
67
|
+
return this.plugins.get(name);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get all registered plugin names
|
|
72
|
+
* @returns Array of plugin names
|
|
73
|
+
*/
|
|
74
|
+
getNames(): string[] {
|
|
75
|
+
return Array.from(this.plugins.keys());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if a plugin is registered
|
|
80
|
+
* @param name - Plugin name
|
|
81
|
+
* @returns True if registered
|
|
82
|
+
*/
|
|
83
|
+
has(name: string): boolean {
|
|
84
|
+
return this.plugins.has(name);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Execute a hook across all plugins
|
|
89
|
+
* @param hookName - Name of the hook to execute
|
|
90
|
+
* @param args - Arguments to pass to the hook
|
|
91
|
+
* @returns Result from hooks (last non-void result)
|
|
92
|
+
*/
|
|
93
|
+
async executeHook<K extends keyof PluginHooks>(
|
|
94
|
+
hookName: K,
|
|
95
|
+
...args: Parameters<NonNullable<PluginHooks[K]>>
|
|
96
|
+
): Promise<unknown> {
|
|
97
|
+
let result: unknown = undefined;
|
|
98
|
+
|
|
99
|
+
for (const [name, plugin] of this.plugins) {
|
|
100
|
+
const hook = plugin.hooks[hookName];
|
|
101
|
+
if (hook) {
|
|
102
|
+
try {
|
|
103
|
+
// @ts-expect-error - TypeScript can't infer the correct types here
|
|
104
|
+
const hookResult = await hook(...args);
|
|
105
|
+
if (hookResult !== undefined) {
|
|
106
|
+
result = hookResult;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.logger.error(`Plugin "${name}" hook "${hookName}" failed:`, error);
|
|
110
|
+
// Continue to next plugin
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Execute beforeConnect hooks
|
|
120
|
+
*/
|
|
121
|
+
async beforeConnect(deviceId: string): Promise<string> {
|
|
122
|
+
const result = await this.executeHook('beforeConnect', deviceId);
|
|
123
|
+
return (result as string) || deviceId;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Execute afterConnect hooks
|
|
128
|
+
*/
|
|
129
|
+
async afterConnect(deviceId: string): Promise<void> {
|
|
130
|
+
await this.executeHook('afterConnect', deviceId);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Execute beforeDisconnect hooks
|
|
135
|
+
*/
|
|
136
|
+
async beforeDisconnect(deviceId: string): Promise<void> {
|
|
137
|
+
await this.executeHook('beforeDisconnect', deviceId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Execute afterDisconnect hooks
|
|
142
|
+
*/
|
|
143
|
+
async afterDisconnect(deviceId: string): Promise<void> {
|
|
144
|
+
await this.executeHook('afterDisconnect', deviceId);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Execute beforePrint hooks
|
|
149
|
+
*/
|
|
150
|
+
async beforePrint(buffer: Uint8Array): Promise<Uint8Array> {
|
|
151
|
+
const result = await this.executeHook('beforePrint', buffer);
|
|
152
|
+
return (result as Uint8Array) || buffer;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Execute afterPrint hooks
|
|
157
|
+
*/
|
|
158
|
+
async afterPrint(bytesSent: number): Promise<void> {
|
|
159
|
+
await this.executeHook('afterPrint', bytesSent);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Execute onError hooks
|
|
164
|
+
* @returns True if error should be suppressed
|
|
165
|
+
*/
|
|
166
|
+
async onError(error: BluetoothPrintError): Promise<boolean> {
|
|
167
|
+
const result = await this.executeHook('onError', error);
|
|
168
|
+
return result === true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Execute onStateChange hooks
|
|
173
|
+
*/
|
|
174
|
+
async onStateChange(state: PrinterState, previousState: PrinterState): Promise<void> {
|
|
175
|
+
await this.executeHook('onStateChange', state, previousState);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Execute onProgress hooks
|
|
180
|
+
*/
|
|
181
|
+
async onProgress(sent: number, total: number): Promise<void> {
|
|
182
|
+
await this.executeHook('onProgress', sent, total);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Clear all plugins
|
|
187
|
+
*/
|
|
188
|
+
async clear(): Promise<void> {
|
|
189
|
+
for (const name of this.plugins.keys()) {
|
|
190
|
+
await this.unregister(name);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging Plugin
|
|
3
|
+
* Provides detailed logging for printer operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Plugin, PluginFactory, PluginOptions } from '../types';
|
|
7
|
+
import { Logger, LogLevel } from '@/utils/logger';
|
|
8
|
+
|
|
9
|
+
export interface LoggingPluginOptions extends PluginOptions {
|
|
10
|
+
/** Log level (default: DEBUG) */
|
|
11
|
+
level?: LogLevel;
|
|
12
|
+
/** Include timestamps (default: true) */
|
|
13
|
+
timestamps?: boolean;
|
|
14
|
+
/** Log progress updates (default: false, can be noisy) */
|
|
15
|
+
logProgress?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a logging plugin instance
|
|
20
|
+
*/
|
|
21
|
+
export const createLoggingPlugin: PluginFactory = (options?: LoggingPluginOptions): Plugin => {
|
|
22
|
+
const opts: Required<LoggingPluginOptions> = {
|
|
23
|
+
level: options?.level ?? LogLevel.DEBUG,
|
|
24
|
+
timestamps: options?.timestamps ?? true,
|
|
25
|
+
logProgress: options?.logProgress ?? false,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const logger = Logger.scope('PrinterLog');
|
|
29
|
+
let startTime: number;
|
|
30
|
+
|
|
31
|
+
const formatTime = (): string => {
|
|
32
|
+
if (!opts.timestamps) return '';
|
|
33
|
+
return `[${new Date().toISOString()}] `;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
name: 'logging',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
description: 'Detailed logging for printer operations',
|
|
40
|
+
|
|
41
|
+
hooks: {
|
|
42
|
+
beforeConnect: (deviceId: string) => {
|
|
43
|
+
startTime = Date.now();
|
|
44
|
+
logger.info(`${formatTime()}Connecting to device: ${deviceId}`);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
afterConnect: (deviceId: string) => {
|
|
48
|
+
const elapsed = Date.now() - startTime;
|
|
49
|
+
logger.info(`${formatTime()}Connected to ${deviceId} (${elapsed}ms)`);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
beforeDisconnect: (deviceId: string) => {
|
|
53
|
+
logger.info(`${formatTime()}Disconnecting from: ${deviceId}`);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
afterDisconnect: (deviceId: string) => {
|
|
57
|
+
logger.info(`${formatTime()}Disconnected from: ${deviceId}`);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
beforePrint: (buffer: Uint8Array) => {
|
|
61
|
+
startTime = Date.now();
|
|
62
|
+
logger.info(`${formatTime()}Starting print job: ${buffer.length} bytes`);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
afterPrint: (bytesSent: number) => {
|
|
66
|
+
const elapsed = Date.now() - startTime;
|
|
67
|
+
const speed = ((bytesSent / elapsed) * 1000).toFixed(2);
|
|
68
|
+
logger.info(`${formatTime()}Print complete: ${bytesSent} bytes in ${elapsed}ms (${speed} B/s)`);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
onError: (error) => {
|
|
72
|
+
logger.error(`${formatTime()}Error [${error.code}]: ${error.message}`);
|
|
73
|
+
return false; // Don't suppress the error
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
onStateChange: (state, previousState) => {
|
|
77
|
+
logger.debug(`${formatTime()}State: ${previousState} → ${state}`);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
onProgress: (sent, total) => {
|
|
81
|
+
if (opts.logProgress) {
|
|
82
|
+
const percent = ((sent / total) * 100).toFixed(1);
|
|
83
|
+
logger.debug(`${formatTime()}Progress: ${sent}/${total} (${percent}%)`);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
init: () => {
|
|
89
|
+
Logger.setLevel(opts.level);
|
|
90
|
+
logger.info('Logging plugin initialized');
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
destroy: () => {
|
|
94
|
+
logger.info('Logging plugin destroyed');
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry Plugin
|
|
3
|
+
* Provides automatic retry with exponential backoff for failed operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Plugin, PluginFactory, PluginOptions } from '../types';
|
|
7
|
+
import { Logger } from '@/utils/logger';
|
|
8
|
+
import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
|
|
9
|
+
|
|
10
|
+
export interface RetryPluginOptions extends PluginOptions {
|
|
11
|
+
/** Maximum retry attempts (default: 3) */
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
/** Initial delay in ms (default: 1000) */
|
|
14
|
+
initialDelay?: number;
|
|
15
|
+
/** Maximum delay in ms (default: 10000) */
|
|
16
|
+
maxDelay?: number;
|
|
17
|
+
/** Backoff multiplier (default: 2) */
|
|
18
|
+
backoffMultiplier?: number;
|
|
19
|
+
/** Error codes that should trigger retry */
|
|
20
|
+
retryableErrors?: ErrorCode[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a retry plugin instance
|
|
25
|
+
*/
|
|
26
|
+
export const createRetryPlugin: PluginFactory = (options?: RetryPluginOptions): Plugin => {
|
|
27
|
+
const opts: Required<RetryPluginOptions> = {
|
|
28
|
+
maxRetries: options?.maxRetries ?? 3,
|
|
29
|
+
initialDelay: options?.initialDelay ?? 1000,
|
|
30
|
+
maxDelay: options?.maxDelay ?? 10000,
|
|
31
|
+
backoffMultiplier: options?.backoffMultiplier ?? 2,
|
|
32
|
+
retryableErrors: options?.retryableErrors ?? [
|
|
33
|
+
ErrorCode.CONNECTION_FAILED,
|
|
34
|
+
ErrorCode.CONNECTION_TIMEOUT,
|
|
35
|
+
ErrorCode.WRITE_FAILED,
|
|
36
|
+
ErrorCode.WRITE_TIMEOUT,
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const logger = Logger.scope('RetryPlugin');
|
|
41
|
+
let retryCount = 0;
|
|
42
|
+
let currentDelay = opts.initialDelay;
|
|
43
|
+
|
|
44
|
+
const shouldRetry = (error: BluetoothPrintError): boolean => {
|
|
45
|
+
return (
|
|
46
|
+
retryCount < opts.maxRetries &&
|
|
47
|
+
opts.retryableErrors.includes(error.code)
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const sleep = (ms: number): Promise<void> => {
|
|
52
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
name: 'retry',
|
|
57
|
+
version: '1.0.0',
|
|
58
|
+
description: 'Automatic retry with exponential backoff',
|
|
59
|
+
|
|
60
|
+
hooks: {
|
|
61
|
+
beforeConnect: () => {
|
|
62
|
+
// Reset retry state on new connection attempt
|
|
63
|
+
retryCount = 0;
|
|
64
|
+
currentDelay = opts.initialDelay;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
beforePrint: () => {
|
|
68
|
+
// Reset retry state on new print job
|
|
69
|
+
retryCount = 0;
|
|
70
|
+
currentDelay = opts.initialDelay;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
onError: async (error: BluetoothPrintError) => {
|
|
74
|
+
if (shouldRetry(error)) {
|
|
75
|
+
retryCount++;
|
|
76
|
+
logger.warn(
|
|
77
|
+
`Retryable error occurred (attempt ${retryCount}/${opts.maxRetries}): ${error.code}`
|
|
78
|
+
);
|
|
79
|
+
logger.info(`Waiting ${currentDelay}ms before retry...`);
|
|
80
|
+
|
|
81
|
+
await sleep(currentDelay);
|
|
82
|
+
|
|
83
|
+
// Exponential backoff
|
|
84
|
+
currentDelay = Math.min(
|
|
85
|
+
currentDelay * opts.backoffMultiplier,
|
|
86
|
+
opts.maxDelay
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Note: Returning false means error is not suppressed
|
|
90
|
+
// The actual retry logic would need to be implemented in the printer
|
|
91
|
+
// This plugin mainly provides the delay and logging
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (retryCount > 0) {
|
|
96
|
+
logger.error(
|
|
97
|
+
`Failed after ${retryCount} retries: ${error.code} - ${error.message}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return false;
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
init: () => {
|
|
106
|
+
logger.info(`Retry plugin initialized (max: ${opts.maxRetries}, delay: ${opts.initialDelay}ms)`);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin System Exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { PluginManager } from './PluginManager';
|
|
6
|
+
export type { Plugin, PluginHooks, PluginOptions, PluginFactory } from './types';
|
|
7
|
+
|
|
8
|
+
// Built-in plugins
|
|
9
|
+
export { createLoggingPlugin } from './builtin/LoggingPlugin';
|
|
10
|
+
export { createRetryPlugin } from './builtin/RetryPlugin';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin System Types
|
|
3
|
+
* Defines interfaces for extending BluetoothPrinter functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BluetoothPrintError } from '@/errors/BluetoothError';
|
|
7
|
+
import { PrinterState } from '@/types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Plugin lifecycle hooks
|
|
11
|
+
*/
|
|
12
|
+
export interface PluginHooks {
|
|
13
|
+
/**
|
|
14
|
+
* Called before connecting to a device
|
|
15
|
+
* @param deviceId - Target device ID
|
|
16
|
+
* @returns Modified device ID or void
|
|
17
|
+
*/
|
|
18
|
+
beforeConnect?: (deviceId: string) => string | void | Promise<string | void>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Called after successful connection
|
|
22
|
+
* @param deviceId - Connected device ID
|
|
23
|
+
*/
|
|
24
|
+
afterConnect?: (deviceId: string) => void | Promise<void>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Called before disconnecting
|
|
28
|
+
* @param deviceId - Device ID to disconnect
|
|
29
|
+
*/
|
|
30
|
+
beforeDisconnect?: (deviceId: string) => void | Promise<void>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Called after disconnection
|
|
34
|
+
* @param deviceId - Disconnected device ID
|
|
35
|
+
*/
|
|
36
|
+
afterDisconnect?: (deviceId: string) => void | Promise<void>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Called before sending print data
|
|
40
|
+
* @param buffer - Data buffer to send
|
|
41
|
+
* @returns Modified buffer or void
|
|
42
|
+
*/
|
|
43
|
+
beforePrint?: (buffer: Uint8Array) => Uint8Array | void | Promise<Uint8Array | void>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Called after print job completes
|
|
47
|
+
* @param bytesSent - Total bytes sent
|
|
48
|
+
*/
|
|
49
|
+
afterPrint?: (bytesSent: number) => void | Promise<void>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Called when an error occurs
|
|
53
|
+
* @param error - The error that occurred
|
|
54
|
+
* @returns Whether to suppress the error (true = suppress)
|
|
55
|
+
*/
|
|
56
|
+
onError?: (error: BluetoothPrintError) => boolean | void | Promise<boolean | void>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Called when printer state changes
|
|
60
|
+
* @param state - New printer state
|
|
61
|
+
* @param previousState - Previous state
|
|
62
|
+
*/
|
|
63
|
+
onStateChange?: (state: PrinterState, previousState: PrinterState) => void | Promise<void>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Called during print progress
|
|
67
|
+
* @param sent - Bytes sent
|
|
68
|
+
* @param total - Total bytes
|
|
69
|
+
*/
|
|
70
|
+
onProgress?: (sent: number, total: number) => void | Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Plugin configuration options
|
|
75
|
+
*/
|
|
76
|
+
export interface PluginOptions {
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Plugin interface
|
|
82
|
+
*/
|
|
83
|
+
export interface Plugin {
|
|
84
|
+
/**
|
|
85
|
+
* Unique plugin name
|
|
86
|
+
*/
|
|
87
|
+
name: string;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Plugin version
|
|
91
|
+
*/
|
|
92
|
+
version?: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Plugin description
|
|
96
|
+
*/
|
|
97
|
+
description?: string;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Plugin hooks
|
|
101
|
+
*/
|
|
102
|
+
hooks: PluginHooks;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Plugin initialization
|
|
106
|
+
* @param options - Plugin options
|
|
107
|
+
*/
|
|
108
|
+
init?: (options?: PluginOptions) => void | Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Plugin cleanup
|
|
112
|
+
*/
|
|
113
|
+
destroy?: () => void | Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Plugin factory function type
|
|
118
|
+
*/
|
|
119
|
+
export type PluginFactory = (options?: PluginOptions) => Plugin;
|
|
@@ -236,7 +236,13 @@ export class PreviewRenderer implements IPreviewRenderer {
|
|
|
236
236
|
case 0x61: // ESC a - Alignment
|
|
237
237
|
flushText();
|
|
238
238
|
if (i + 2 < commands.length) {
|
|
239
|
-
|
|
239
|
+
const alignValue = commands[i + 2];
|
|
240
|
+
state.alignment =
|
|
241
|
+
alignValue === Alignment.LEFT ||
|
|
242
|
+
alignValue === Alignment.CENTER ||
|
|
243
|
+
alignValue === Alignment.RIGHT
|
|
244
|
+
? alignValue
|
|
245
|
+
: Alignment.LEFT;
|
|
240
246
|
}
|
|
241
247
|
i += 3;
|
|
242
248
|
break;
|
package/src/queue/PrintQueue.ts
CHANGED
|
@@ -385,12 +385,16 @@ export class PrintQueue implements IPrintQueue {
|
|
|
385
385
|
if (!job || job.status !== PrintJobStatus.PENDING) continue;
|
|
386
386
|
|
|
387
387
|
this.activeJobs++;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
388
|
+
this.executeJob(job)
|
|
389
|
+
.catch(error => {
|
|
390
|
+
this.logger.error(`Unhandled error in job execution for ${job.id}:`, error);
|
|
391
|
+
})
|
|
392
|
+
.finally(() => {
|
|
393
|
+
this.activeJobs--;
|
|
394
|
+
if (!this.isPaused) {
|
|
395
|
+
this.processQueue();
|
|
396
|
+
}
|
|
397
|
+
});
|
|
394
398
|
}
|
|
395
399
|
|
|
396
400
|
this.isProcessing = false;
|