@risleylima/escpos 0.2.1 → 1.0.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/README.md +94 -766
- package/dist/adapter/index.d.ts +29 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +25 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/network-adapter/index.d.ts +24 -0
- package/dist/network-adapter/index.d.ts.map +1 -0
- package/dist/network-adapter/index.js +263 -0
- package/dist/network-adapter/index.js.map +1 -0
- package/dist/printer/commands-types.d.ts +37 -0
- package/dist/printer/commands-types.d.ts.map +1 -0
- package/dist/printer/commands-types.js +63 -0
- package/dist/printer/commands-types.js.map +1 -0
- package/dist/printer/commands.d.ts +169 -0
- package/dist/printer/commands.d.ts.map +1 -0
- package/dist/printer/commands.js +192 -0
- package/dist/printer/commands.js.map +1 -0
- package/dist/printer/image-loader.d.ts +17 -0
- package/dist/printer/image-loader.d.ts.map +1 -0
- package/dist/printer/image-loader.js +462 -0
- package/dist/printer/image-loader.js.map +1 -0
- package/dist/printer/image.d.ts +43 -0
- package/dist/printer/image.d.ts.map +1 -0
- package/dist/printer/image.js +132 -0
- package/dist/printer/image.js.map +1 -0
- package/dist/printer/index.d.ts +158 -0
- package/dist/printer/index.d.ts.map +1 -0
- package/dist/printer/index.js +703 -0
- package/dist/printer/index.js.map +1 -0
- package/dist/printer/profiles/bematech/mp4200th.d.ts +13 -0
- package/dist/printer/profiles/bematech/mp4200th.d.ts.map +1 -0
- package/dist/printer/profiles/bematech/mp4200th.js +29 -0
- package/dist/printer/profiles/bematech/mp4200th.js.map +1 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.d.ts +13 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.d.ts.map +1 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.js +21 -0
- package/dist/printer/profiles/custom/bematech-mp4200th.js.map +1 -0
- package/dist/printer/profiles/custom/vkp80iii.d.ts +19 -0
- package/dist/printer/profiles/custom/vkp80iii.d.ts.map +1 -0
- package/dist/printer/profiles/custom/vkp80iii.js +87 -0
- package/dist/printer/profiles/custom/vkp80iii.js.map +1 -0
- package/dist/printer/profiles/default.d.ts +7 -0
- package/dist/printer/profiles/default.d.ts.map +1 -0
- package/dist/printer/profiles/default.js +15 -0
- package/dist/printer/profiles/default.js.map +1 -0
- package/dist/printer/profiles/index.d.ts +41 -0
- package/dist/printer/profiles/index.d.ts.map +1 -0
- package/dist/printer/profiles/index.js +98 -0
- package/dist/printer/profiles/index.js.map +1 -0
- package/dist/printer/profiles/merge.d.ts +7 -0
- package/dist/printer/profiles/merge.d.ts.map +1 -0
- package/dist/printer/profiles/merge.js +58 -0
- package/dist/printer/profiles/merge.js.map +1 -0
- package/dist/printer/profiles/types.d.ts +99 -0
- package/dist/printer/profiles/types.d.ts.map +1 -0
- package/dist/printer/profiles/types.js +8 -0
- package/dist/printer/profiles/types.js.map +1 -0
- package/dist/printer/utils.d.ts +9 -0
- package/dist/printer/utils.d.ts.map +1 -0
- package/dist/printer/utils.js +54 -0
- package/dist/printer/utils.js.map +1 -0
- package/dist/serial-adapter/index.d.ts +17 -0
- package/dist/serial-adapter/index.d.ts.map +1 -0
- package/dist/serial-adapter/index.js +172 -0
- package/dist/serial-adapter/index.js.map +1 -0
- package/dist/usb-adapter/index.d.ts +20 -0
- package/dist/usb-adapter/index.d.ts.map +1 -0
- package/dist/usb-adapter/index.js +264 -0
- package/dist/usb-adapter/index.js.map +1 -0
- package/package.json +42 -15
- package/CHANGELOG.md +0 -74
- package/docs/COVERAGE_ANALYSIS.md +0 -98
- package/docs/DEPENDENCIES_REVIEW.md +0 -127
- package/docs/JSDOC_REVIEW.md +0 -122
- package/docs/LIBRARY_OVERVIEW.md +0 -383
- package/docs/PRE_PUBLISH_CHECKLIST.md +0 -331
- package/docs/PUBLIC_API_ANALYSIS.md +0 -223
- package/docs/README.md +0 -37
- package/docs/SERIALPORT_V13_MIGRATION_COMPLETE.md +0 -127
- package/docs/TESTS_IMPLEMENTED.md +0 -129
- package/docs/USB_V2_REVIEW.md +0 -148
- package/docs/VERIFICATION_RESULTS.md +0 -172
- package/docs/VERSIONING.md +0 -102
- package/examples/printTest.js +0 -59
- package/index.js +0 -7
- package/jest.config.js +0 -16
- package/src/adapter/index.js +0 -75
- package/src/printer/commands.js +0 -199
- package/src/printer/image.js +0 -159
- package/src/printer/index.js +0 -621
- package/src/printer/utils.js +0 -58
- package/src/serial-adapter/index.js +0 -198
- package/src/usb-adapter/index.js +0 -283
- package/tests/README.md +0 -67
- package/tests/integration/printer-flow.test.js +0 -128
- package/tests/unit/adapters/adapter.test.js +0 -49
- package/tests/unit/adapters/serial-adapter.test.js +0 -238
- package/tests/unit/adapters/usb-adapter.test.js +0 -319
- package/tests/unit/image/image.test.js +0 -157
- package/tests/unit/printer/buffer.test.js +0 -60
- package/tests/unit/printer/commands.test.js +0 -109
- package/tests/unit/printer/printer.test.js +0 -405
- package/tests/unit/utils/utils.test.js +0 -96
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const { SerialPort } = require('serialport');
|
|
3
|
-
const Adapter = require('../adapter');
|
|
4
|
-
|
|
5
|
-
const debug = require('debug')('escpos:serial-adapter');
|
|
6
|
-
|
|
7
|
-
const scope = {
|
|
8
|
-
port: null,
|
|
9
|
-
/**
|
|
10
|
-
* Verify that a serial port exists
|
|
11
|
-
* @private
|
|
12
|
-
* @async
|
|
13
|
-
* @param {String} port - Serial port path to verify
|
|
14
|
-
* @returns {Promise<String>} Verified port path
|
|
15
|
-
* @throws {Error} If port does not exist
|
|
16
|
-
*/
|
|
17
|
-
verifyPort: async (port) => {
|
|
18
|
-
let ports = await SerialPort.list();
|
|
19
|
-
if (!ports.find((i) => i.path === port)) {
|
|
20
|
-
throw new Error('The specified port does not exist!')
|
|
21
|
-
}
|
|
22
|
-
return port;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Create Adapter instance first, so it's the same object used internally and exported
|
|
27
|
-
const Serial = new Adapter();
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* List all available serial ports
|
|
31
|
-
* @async
|
|
32
|
-
* @returns {Promise<Array<Object>>} Array of serial port objects with properties like path, manufacturer, vendorId, productId, etc.
|
|
33
|
-
* @example
|
|
34
|
-
* const ports = await Serial.listSerial();
|
|
35
|
-
* console.log(ports);
|
|
36
|
-
* // [
|
|
37
|
-
* // { path: '/dev/ttyUSB0', manufacturer: 'FTDI', vendorId: '0403', productId: '6001' },
|
|
38
|
-
* // { path: '/dev/ttyUSB1', manufacturer: 'Prolific', vendorId: '067b', productId: '2303' }
|
|
39
|
-
* // ]
|
|
40
|
-
*/
|
|
41
|
-
Serial.listSerial = async () => {
|
|
42
|
-
return await SerialPort.list();
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Connect to a serial port printer
|
|
47
|
-
* @async
|
|
48
|
-
* @param {String} port - Serial port path (e.g., '/dev/ttyUSB0' or 'COM3')
|
|
49
|
-
* @param {Object} [options] - Serial port options (baudRate, dataBits, etc.)
|
|
50
|
-
* @returns {Promise<Boolean>} True if connection successful
|
|
51
|
-
* @throws {Error} If port does not exist or cannot be opened
|
|
52
|
-
* @fires Serial#connect
|
|
53
|
-
* @fires Serial#close (if reconnecting)
|
|
54
|
-
*/
|
|
55
|
-
Serial.connect = async (port, options) => {
|
|
56
|
-
// Close existing connection if any
|
|
57
|
-
if (scope.port) {
|
|
58
|
-
try {
|
|
59
|
-
await scope.port.close();
|
|
60
|
-
} catch (e) {
|
|
61
|
-
debug('Error closing existing port: ', e);
|
|
62
|
-
}
|
|
63
|
-
Serial.emit('close');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Verify port exists
|
|
67
|
-
const portVerified = await scope.verifyPort(port);
|
|
68
|
-
|
|
69
|
-
// Create SerialPort instance (v13: no callback, autoOpen: false)
|
|
70
|
-
scope.port = new SerialPort(Object.assign(options || {}, {
|
|
71
|
-
path: portVerified,
|
|
72
|
-
autoOpen: false
|
|
73
|
-
}));
|
|
74
|
-
|
|
75
|
-
// Handle errors via events
|
|
76
|
-
scope.port.on('error', (err) => {
|
|
77
|
-
debug('Error on Serial Port: ', err);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Handle close event
|
|
81
|
-
let clearPort = () => {
|
|
82
|
-
Serial.emit('disconnect', scope.port);
|
|
83
|
-
scope.port.removeListener('close', clearPort);
|
|
84
|
-
scope.port = null;
|
|
85
|
-
};
|
|
86
|
-
scope.port.on('close', clearPort);
|
|
87
|
-
|
|
88
|
-
// Open the port manually (v13: returns Promise)
|
|
89
|
-
try {
|
|
90
|
-
await scope.port.open();
|
|
91
|
-
debug('Device Connected and Open!');
|
|
92
|
-
Serial.emit('connect', scope.port);
|
|
93
|
-
return true;
|
|
94
|
-
} catch (err) {
|
|
95
|
-
debug('Error Opening the Selected Port: ', err);
|
|
96
|
-
if (scope.port) {
|
|
97
|
-
try {
|
|
98
|
-
await scope.port.close();
|
|
99
|
-
} catch (closeErr) {
|
|
100
|
-
debug('Error closing port after open failure: ', closeErr);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
throw err;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Open the serial port if it's closed
|
|
109
|
-
* @async
|
|
110
|
-
* @returns {Promise<Boolean>} True if port is open (or was already open)
|
|
111
|
-
* @throws {Error} If port cannot be opened
|
|
112
|
-
*/
|
|
113
|
-
Serial.open = async () => {
|
|
114
|
-
if (!scope.port.isOpen) {
|
|
115
|
-
try {
|
|
116
|
-
await scope.port.open();
|
|
117
|
-
debug('Device Opened!');
|
|
118
|
-
return scope.port.isOpen;
|
|
119
|
-
} catch (err) {
|
|
120
|
-
throw err;
|
|
121
|
-
}
|
|
122
|
-
} else {
|
|
123
|
-
debug('Device is already Opened!');
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Write data to the serial port
|
|
130
|
-
* @async
|
|
131
|
-
* @param {Buffer} data - Data buffer to send to printer
|
|
132
|
-
* @returns {Promise<Boolean>} True if write successful
|
|
133
|
-
* @throws {Error} If write fails
|
|
134
|
-
*/
|
|
135
|
-
Serial.write = async (data) => {
|
|
136
|
-
try {
|
|
137
|
-
await scope.port.write(data);
|
|
138
|
-
await scope.port.drain();
|
|
139
|
-
return true;
|
|
140
|
-
} catch (e) {
|
|
141
|
-
throw e;
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Close the serial port connection
|
|
147
|
-
* @async
|
|
148
|
-
* @param {Number} [timeout] - Timeout in milliseconds before closing (default: 50ms)
|
|
149
|
-
* @returns {Promise<Boolean>} True if port closed successfully
|
|
150
|
-
* @fires Serial#close
|
|
151
|
-
*/
|
|
152
|
-
Serial.close = async (timeout) => {
|
|
153
|
-
let time = Number(timeout);
|
|
154
|
-
if (Number.isNaN(time)) {
|
|
155
|
-
time = 50;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
try {
|
|
159
|
-
await scope.port.flush();
|
|
160
|
-
await new Promise(resolve => setTimeout(resolve, time));
|
|
161
|
-
await scope.port.drain();
|
|
162
|
-
await scope.port.close();
|
|
163
|
-
// Emit event synchronously - this ensures listeners are called immediately
|
|
164
|
-
Serial.emit('close');
|
|
165
|
-
return true;
|
|
166
|
-
} catch (e) {
|
|
167
|
-
debug('Error while closing device: ', e);
|
|
168
|
-
// Emit event synchronously even on error - this ensures listeners are called immediately
|
|
169
|
-
Serial.emit('close');
|
|
170
|
-
return true; // Still resolve to allow cleanup
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Disconnect from the serial port (calls close internally)
|
|
176
|
-
* @param {Number} [timeout] - Timeout in milliseconds before closing (default: 50ms)
|
|
177
|
-
* @returns {Promise<Boolean>} True if disconnection successful
|
|
178
|
-
*/
|
|
179
|
-
Serial.disconnect = (timeout) => {
|
|
180
|
-
return Serial.close(timeout);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Read data from the serial port
|
|
185
|
-
* @returns {Promise<Buffer>} Data received from the port
|
|
186
|
-
*/
|
|
187
|
-
Serial.read = () => {
|
|
188
|
-
return new Promise((resolve, reject) => {
|
|
189
|
-
let dataHandler = (data) => {
|
|
190
|
-
scope.port.removeListener('data', dataHandler);
|
|
191
|
-
resolve(data);
|
|
192
|
-
}
|
|
193
|
-
scope.port.on('data', dataHandler);
|
|
194
|
-
});
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Serial is already an Adapter instance, so export it directly
|
|
198
|
-
module.exports = Serial;
|
package/src/usb-adapter/index.js
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
const Adapter = require('../adapter');
|
|
3
|
-
const usb = require('usb');
|
|
4
|
-
const os = require('os');
|
|
5
|
-
|
|
6
|
-
const debug = require('debug')('escpos:usb-adapter');
|
|
7
|
-
|
|
8
|
-
const scope = {
|
|
9
|
-
device: null,
|
|
10
|
-
endpoint: null
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* [USB Class Codes ]
|
|
15
|
-
* @type {Object}
|
|
16
|
-
* @docs http://www.usb.org/developers/defined_class
|
|
17
|
-
*/
|
|
18
|
-
const IFACE_CLASS = {
|
|
19
|
-
AUDIO: 0x01,
|
|
20
|
-
HID: 0x03,
|
|
21
|
-
PRINTER: 0x07,
|
|
22
|
-
HUB: 0x09
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Create Adapter instance first, so it's the same object used internally and exported
|
|
26
|
-
const USB = new Adapter();
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* List all available USB printer devices
|
|
30
|
-
* @async
|
|
31
|
-
* @returns {Promise<Array>} Array of USB printer devices with manufacturer and product information
|
|
32
|
-
*/
|
|
33
|
-
USB.listUSB = async () => {
|
|
34
|
-
const devices = usb.getDeviceList().filter((device) => {
|
|
35
|
-
try {
|
|
36
|
-
// In v2, we need to check configDescriptor for interface class
|
|
37
|
-
const configDescriptor = device.configDescriptor;
|
|
38
|
-
if (!configDescriptor || !configDescriptor.interfaces) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
// configDescriptor.interfaces is an array of arrays (alternate settings)
|
|
42
|
-
return configDescriptor.interfaces.some((ifaceArray) => {
|
|
43
|
-
return ifaceArray.some((iface) => {
|
|
44
|
-
return iface.bInterfaceClass === IFACE_CLASS.PRINTER;
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
} catch (e) {
|
|
48
|
-
debug('Error while get device info: ', e);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
let retorno = [];
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Get string descriptor from USB device
|
|
57
|
-
* @private
|
|
58
|
-
* @async
|
|
59
|
-
* @param {Object} device - USB device object
|
|
60
|
-
* @param {Number} type - Descriptor type index
|
|
61
|
-
* @returns {Promise<String|Boolean>} Descriptor string or false on error
|
|
62
|
-
*/
|
|
63
|
-
const getDescriptor = async (device, type) => {
|
|
64
|
-
try {
|
|
65
|
-
await device.open();
|
|
66
|
-
const data = await device.getStringDescriptor(type);
|
|
67
|
-
await device.close();
|
|
68
|
-
return data;
|
|
69
|
-
} catch (e) {
|
|
70
|
-
debug('Error while read device description: ', e);
|
|
71
|
-
try {
|
|
72
|
-
await device.close();
|
|
73
|
-
} catch (closeErr) {
|
|
74
|
-
// Ignore close errors
|
|
75
|
-
}
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
for (let device of devices) {
|
|
81
|
-
device.manufacturer = await getDescriptor(device, device.deviceDescriptor.iManufacturer);
|
|
82
|
-
device.product = await getDescriptor(device, device.deviceDescriptor.iProduct);
|
|
83
|
-
if (device.manufacturer && device.product) {
|
|
84
|
-
retorno.push(device);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return retorno;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Connect to a USB printer device
|
|
93
|
-
* @async
|
|
94
|
-
* @param {Number} [vid] - Vendor ID (optional, if not provided, uses first available printer)
|
|
95
|
-
* @param {Number} [pid] - Product ID (optional, if not provided, uses first available printer)
|
|
96
|
-
* @returns {Promise<Boolean>} True if connection successful
|
|
97
|
-
* @throws {Error} If printer cannot be found
|
|
98
|
-
* @fires USB#connect
|
|
99
|
-
*/
|
|
100
|
-
USB.connect = async (vid, pid) => {
|
|
101
|
-
scope.device = null;
|
|
102
|
-
scope.endpoint = null;
|
|
103
|
-
if (vid && pid) {
|
|
104
|
-
scope.device = usb.findByIds(vid, pid);
|
|
105
|
-
}else{
|
|
106
|
-
let devices = await USB.listUSB();
|
|
107
|
-
if(devices && devices.length)
|
|
108
|
-
scope.device = devices[0];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!scope.device) {
|
|
112
|
-
throw new Error("Cannot find printer!");
|
|
113
|
-
}
|
|
114
|
-
USB.emit('connect', scope.device);
|
|
115
|
-
|
|
116
|
-
usb.on('detach', (device) => {
|
|
117
|
-
if (device === scope.device) {
|
|
118
|
-
debug('Device Unplugged!');
|
|
119
|
-
USB.emit('detach');
|
|
120
|
-
scope.device = null;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
debug('Device Connected!');
|
|
125
|
-
return true;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Open the USB device and claim the printer interface
|
|
130
|
-
* @async
|
|
131
|
-
* @returns {Promise<Boolean>} True if device opened successfully
|
|
132
|
-
* @throws {Error} If interfaces cannot be accessed or endpoint not found
|
|
133
|
-
* @fires USB#connect
|
|
134
|
-
*/
|
|
135
|
-
USB.open = async () => {
|
|
136
|
-
await scope.device.open();
|
|
137
|
-
|
|
138
|
-
// In v2, device.interfaces is a direct array of Interface objects
|
|
139
|
-
// We need to iterate through all interfaces to find the printer interface
|
|
140
|
-
const interfaces = scope.device.interfaces;
|
|
141
|
-
if (!interfaces || interfaces.length === 0) {
|
|
142
|
-
throw new Error('Cannot access device interfaces');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
for (let interfaceObj of interfaces) {
|
|
146
|
-
if (scope.endpoint) {
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Check if this interface is a printer interface
|
|
151
|
-
const descriptor = interfaceObj.descriptor;
|
|
152
|
-
if (descriptor && descriptor.bInterfaceClass !== IFACE_CLASS.PRINTER) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Claim interface (required on all platforms)
|
|
157
|
-
if ("win32" !== os.platform()) {
|
|
158
|
-
// On Linux/macOS, detach kernel driver first if active
|
|
159
|
-
if (interfaceObj.isKernelDriverActive()) {
|
|
160
|
-
try {
|
|
161
|
-
await interfaceObj.detachKernelDriver();
|
|
162
|
-
} catch (e) {
|
|
163
|
-
throw new Error(`[ERROR] Could not detach kernel driver: ${e.message}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Claim interface (required on all platforms before using endpoints)
|
|
168
|
-
await interfaceObj.claim();
|
|
169
|
-
|
|
170
|
-
for (let endpoint of interfaceObj.endpoints) {
|
|
171
|
-
if (scope.endpoint) {
|
|
172
|
-
break;
|
|
173
|
-
} else if (endpoint.direction === 'out') {
|
|
174
|
-
scope.endpoint = endpoint;
|
|
175
|
-
USB.emit('connect', scope.device);
|
|
176
|
-
debug('Device Opened!');
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (!scope.endpoint) {
|
|
182
|
-
throw new Error('Can not find endpoint from printer');
|
|
183
|
-
}
|
|
184
|
-
return true;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Close the USB device connection and release interfaces
|
|
189
|
-
* @async
|
|
190
|
-
* @returns {Promise<Boolean>} True if device closed successfully
|
|
191
|
-
* @fires USB#close
|
|
192
|
-
*/
|
|
193
|
-
USB.close = async () => {
|
|
194
|
-
const device = scope.device; // Save device reference before cleanup
|
|
195
|
-
|
|
196
|
-
if (scope.device) {
|
|
197
|
-
try {
|
|
198
|
-
// Release interfaces before closing
|
|
199
|
-
// Only release the interface we actually claimed
|
|
200
|
-
if (scope.endpoint && scope.endpoint.interface) {
|
|
201
|
-
const interfaceObj = scope.endpoint.interface;
|
|
202
|
-
try {
|
|
203
|
-
// Check if interface is still valid and was claimed
|
|
204
|
-
if (interfaceObj && typeof interfaceObj.release === 'function') {
|
|
205
|
-
await interfaceObj.release();
|
|
206
|
-
}
|
|
207
|
-
} catch (e) {
|
|
208
|
-
debug('Error releasing interface: ', e);
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
// Fallback: try to release all interfaces
|
|
212
|
-
const interfaces = scope.device.interfaces;
|
|
213
|
-
if (interfaces && interfaces.length > 0) {
|
|
214
|
-
for (let interfaceObj of interfaces) {
|
|
215
|
-
try {
|
|
216
|
-
// Only release if we claimed it (kernel driver was detached)
|
|
217
|
-
if (interfaceObj && typeof interfaceObj.release === 'function' && !interfaceObj.isKernelDriverActive()) {
|
|
218
|
-
await interfaceObj.release();
|
|
219
|
-
}
|
|
220
|
-
} catch (e) {
|
|
221
|
-
debug('Error releasing interface: ', e);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
await scope.device.close();
|
|
227
|
-
} catch (e) {
|
|
228
|
-
debug('Error closing device: ', e);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Clear endpoint before emitting event
|
|
233
|
-
scope.endpoint = null;
|
|
234
|
-
|
|
235
|
-
// Emit event synchronously - this ensures listeners are called immediately
|
|
236
|
-
USB.emit('close', device);
|
|
237
|
-
debug('Device Closed!');
|
|
238
|
-
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Disconnect from the USB device (calls close internally)
|
|
244
|
-
* @async
|
|
245
|
-
* @returns {Promise<Boolean>} True if disconnection successful
|
|
246
|
-
* @fires USB#disconnect
|
|
247
|
-
*/
|
|
248
|
-
USB.disconnect = async () => {
|
|
249
|
-
const device = scope.device; // Save device reference before cleanup
|
|
250
|
-
|
|
251
|
-
if (scope.device) {
|
|
252
|
-
await USB.close().catch(e => { debug(e); return true });
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Clear scope before emitting event
|
|
256
|
-
scope.endpoint = null;
|
|
257
|
-
scope.device = null;
|
|
258
|
-
|
|
259
|
-
// Emit event synchronously - this ensures listeners are called immediately
|
|
260
|
-
USB.emit('disconnect', device);
|
|
261
|
-
debug('Device Disconnected!');
|
|
262
|
-
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Write data to the USB printer
|
|
268
|
-
* @async
|
|
269
|
-
* @param {Buffer} data - Data buffer to send to printer
|
|
270
|
-
* @returns {Promise<Boolean>} True if write successful
|
|
271
|
-
* @throws {Error} If write fails
|
|
272
|
-
*/
|
|
273
|
-
USB.write = async (data) => {
|
|
274
|
-
try {
|
|
275
|
-
await scope.endpoint.transfer(data);
|
|
276
|
-
return true;
|
|
277
|
-
} catch (e) {
|
|
278
|
-
throw e;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// USB is already an Adapter instance, so export it directly
|
|
283
|
-
module.exports = USB;
|
package/tests/README.md
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Tests - EscPos
|
|
2
|
-
|
|
3
|
-
This directory contains the test suite for the EscPos library.
|
|
4
|
-
|
|
5
|
-
## Structure
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
tests/
|
|
9
|
-
├── unit/ # Isolated unit tests
|
|
10
|
-
│ ├── printer/ # Printer class tests
|
|
11
|
-
│ ├── adapters/ # Adapter tests (USB/Serial)
|
|
12
|
-
│ ├── image/ # Image processing tests
|
|
13
|
-
│ └── utils/ # Utility functions tests
|
|
14
|
-
└── integration/ # Integration tests
|
|
15
|
-
└── printer-flow/ # Complete flow tests
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Running Tests
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
# Run all tests
|
|
22
|
-
npm test
|
|
23
|
-
|
|
24
|
-
# Run in watch mode
|
|
25
|
-
npm run test:watch
|
|
26
|
-
|
|
27
|
-
# Run with coverage
|
|
28
|
-
npm run test:coverage
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Coverage
|
|
32
|
-
|
|
33
|
-
The tests cover:
|
|
34
|
-
- ✅ Buffer operations
|
|
35
|
-
- ✅ Text formatting
|
|
36
|
-
- ✅ ESC/POS commands
|
|
37
|
-
- ✅ Image processing
|
|
38
|
-
- ✅ Barcodes
|
|
39
|
-
- ✅ Adapters (with mocks)
|
|
40
|
-
- ✅ Complete print flows
|
|
41
|
-
|
|
42
|
-
## Mocks
|
|
43
|
-
|
|
44
|
-
The tests use mocks for:
|
|
45
|
-
- **USB Adapter**: Mock of `usb` module
|
|
46
|
-
- **Serial Adapter**: Mock of `serialport` module
|
|
47
|
-
- **Image**: Mock of `get-pixels` module
|
|
48
|
-
|
|
49
|
-
This allows running tests without requiring real hardware.
|
|
50
|
-
|
|
51
|
-
## Adding New Tests
|
|
52
|
-
|
|
53
|
-
1. Create the test file in `tests/unit/` or `tests/integration/`
|
|
54
|
-
2. Use the `*.test.js` convention
|
|
55
|
-
3. Follow Jest's describe/it pattern
|
|
56
|
-
4. Use mocks for external dependencies
|
|
57
|
-
|
|
58
|
-
## Example
|
|
59
|
-
|
|
60
|
-
```javascript
|
|
61
|
-
describe('MyFeature', () => {
|
|
62
|
-
it('should do something', () => {
|
|
63
|
-
// test here
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
```
|
|
67
|
-
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const Printer = require('../../src/printer');
|
|
4
|
-
|
|
5
|
-
describe('Printer Integration', () => {
|
|
6
|
-
let printer;
|
|
7
|
-
let mockAdapter;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
mockAdapter = {
|
|
11
|
-
write: jest.fn().mockResolvedValue(true),
|
|
12
|
-
close: jest.fn().mockResolvedValue(true)
|
|
13
|
-
};
|
|
14
|
-
printer = new Printer(mockAdapter);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('Complete Print Flow', () => {
|
|
18
|
-
it('should print a complete receipt', async () => {
|
|
19
|
-
printer
|
|
20
|
-
.hardware('init')
|
|
21
|
-
.beep(1, 1)
|
|
22
|
-
.encode(860)
|
|
23
|
-
.size(2, 2)
|
|
24
|
-
.align('ct')
|
|
25
|
-
.textln('TEST RECEIPT')
|
|
26
|
-
.size(1, 1)
|
|
27
|
-
.align('lt')
|
|
28
|
-
.textln('Item 1: R$ 10,00')
|
|
29
|
-
.textln('Item 2: R$ 20,00')
|
|
30
|
-
.align('rt')
|
|
31
|
-
.textln('Total: R$ 30,00')
|
|
32
|
-
.align('lt')
|
|
33
|
-
.drawLine()
|
|
34
|
-
.cut(true);
|
|
35
|
-
|
|
36
|
-
await printer.flush();
|
|
37
|
-
|
|
38
|
-
expect(mockAdapter.write).toHaveBeenCalled();
|
|
39
|
-
const writtenBuffer = mockAdapter.write.mock.calls[0][0];
|
|
40
|
-
expect(writtenBuffer.length).toBeGreaterThan(0);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should print receipt with barcode', async () => {
|
|
44
|
-
printer
|
|
45
|
-
.hardware('init')
|
|
46
|
-
.align('ct')
|
|
47
|
-
.textln('PRODUCT')
|
|
48
|
-
.barcode('123456789012', 'EAN13', {
|
|
49
|
-
width: 2,
|
|
50
|
-
height: 50,
|
|
51
|
-
position: 'BLW'
|
|
52
|
-
})
|
|
53
|
-
.cut(true);
|
|
54
|
-
|
|
55
|
-
await printer.flush();
|
|
56
|
-
|
|
57
|
-
expect(mockAdapter.write).toHaveBeenCalled();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should handle multiple prints in sequence', async () => {
|
|
61
|
-
// First print
|
|
62
|
-
printer
|
|
63
|
-
.hardware('init')
|
|
64
|
-
.textln('Print 1')
|
|
65
|
-
.cut(true);
|
|
66
|
-
await printer.flush();
|
|
67
|
-
|
|
68
|
-
// Second print
|
|
69
|
-
printer
|
|
70
|
-
.hardware('init')
|
|
71
|
-
.textln('Print 2')
|
|
72
|
-
.cut(true);
|
|
73
|
-
await printer.flush();
|
|
74
|
-
|
|
75
|
-
expect(mockAdapter.write).toHaveBeenCalledTimes(2);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('Complex Formatting', () => {
|
|
80
|
-
it('should handle mixed formatting', async () => {
|
|
81
|
-
printer
|
|
82
|
-
.align('ct')
|
|
83
|
-
.size(2, 2)
|
|
84
|
-
.style('B')
|
|
85
|
-
.textln('BOLD TITLE')
|
|
86
|
-
.style('NORMAL')
|
|
87
|
-
.size(1, 1)
|
|
88
|
-
.align('lt')
|
|
89
|
-
.textln('Normal text')
|
|
90
|
-
.style('U')
|
|
91
|
-
.textln('Underlined text')
|
|
92
|
-
.style('NORMAL')
|
|
93
|
-
.cut(true);
|
|
94
|
-
|
|
95
|
-
await printer.flush();
|
|
96
|
-
expect(mockAdapter.write).toHaveBeenCalled();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should handle encoding changes', async () => {
|
|
100
|
-
printer
|
|
101
|
-
.encode('UTF-8')
|
|
102
|
-
.textln('UTF-8 Text')
|
|
103
|
-
.encode('GB18030')
|
|
104
|
-
.textln('GB18030 Text')
|
|
105
|
-
.cut(true);
|
|
106
|
-
|
|
107
|
-
await printer.flush();
|
|
108
|
-
expect(mockAdapter.write).toHaveBeenCalled();
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('Error Handling', () => {
|
|
113
|
-
it('should handle adapter write errors', async () => {
|
|
114
|
-
mockAdapter.write.mockRejectedValueOnce(new Error('Write failed'));
|
|
115
|
-
|
|
116
|
-
printer.text('Test');
|
|
117
|
-
await expect(printer.flush()).rejects.toThrow('Write failed');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should handle adapter close errors gracefully', async () => {
|
|
121
|
-
mockAdapter.close.mockRejectedValueOnce(new Error('Close failed'));
|
|
122
|
-
|
|
123
|
-
printer.text('Test');
|
|
124
|
-
await expect(printer.close()).rejects.toThrow('Close failed');
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|