@thermal-label/brother-ql-web 0.4.0 → 0.6.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.
@@ -0,0 +1,250 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { DeviceIdentificationRequiredError } from '@thermal-label/contracts';
3
+ import { DEVICES } from '@thermal-label/brother-ql-core';
4
+ import { devicesForTransport, requestPrinters } from '../request-printers.js';
5
+ import { createMockUSBDevice } from './webusb-mock.js';
6
+ /**
7
+ * Minimal Web Serial `SerialPort` double for the Web Serial picker
8
+ * path. Typed structurally (not as the DOM `SerialPort`) because the
9
+ * web package only pulls in `w3c-web-usb`, not `w3c-web-serial`.
10
+ *
11
+ * `WebSerialTransport.fromPort` opens the port and locks a
12
+ * reader/writer; its pump loop awaits `reader.read()` indefinitely.
13
+ * This double's reader hangs until `cancel()` fires (mirroring a real
14
+ * idle SPP link) so `close()` can unwind the pump cleanly.
15
+ */
16
+ function createMockSerialPort() {
17
+ let cancelReader;
18
+ const reader = {
19
+ read: () => new Promise(resolve => {
20
+ cancelReader = () => {
21
+ resolve({ value: new Uint8Array(0), done: true });
22
+ };
23
+ }),
24
+ cancel: () => {
25
+ cancelReader?.();
26
+ return Promise.resolve();
27
+ },
28
+ releaseLock: () => {
29
+ /* no-op */
30
+ },
31
+ };
32
+ const writer = {
33
+ write: () => Promise.resolve(),
34
+ close: () => Promise.resolve(),
35
+ releaseLock: () => {
36
+ /* no-op */
37
+ },
38
+ };
39
+ return {
40
+ open: vi.fn(() => Promise.resolve()),
41
+ close: vi.fn(() => Promise.resolve()),
42
+ readable: { getReader: () => reader },
43
+ writable: { getWriter: () => writer },
44
+ };
45
+ }
46
+ describe('requestPrinters(opts) — brother-ql generic factory', () => {
47
+ afterEach(() => {
48
+ vi.restoreAllMocks();
49
+ });
50
+ describe('transport: bluetooth-spp', () => {
51
+ it('throws DeviceIdentificationRequiredError when deviceKey omitted (no picker open)', async () => {
52
+ // No navigator.serial stubbed — a picker call would TypeError.
53
+ await expect(requestPrinters({ transport: 'bluetooth-spp' })).rejects.toBeInstanceOf(DeviceIdentificationRequiredError);
54
+ });
55
+ it('candidates are filtered to bluetooth-spp-capable entries (QL_820NWBc, PT_P910BT)', async () => {
56
+ try {
57
+ await requestPrinters({ transport: 'bluetooth-spp' });
58
+ throw new Error('expected DeviceIdentificationRequiredError');
59
+ }
60
+ catch (err) {
61
+ if (!(err instanceof DeviceIdentificationRequiredError))
62
+ throw err;
63
+ const keys = err.candidates.map(c => c.key);
64
+ // Bench-confirmed SPP-capable models in the brother-ql registry.
65
+ expect(keys).toContain(DEVICES.QL_820NWBc.key);
66
+ expect(keys).toContain(DEVICES.PT_P910BT.key);
67
+ for (const candidate of err.candidates) {
68
+ expect(candidate.transports['bluetooth-spp']).toBeDefined();
69
+ }
70
+ }
71
+ });
72
+ it('rejects an unknown deviceKey on bluetooth-spp', async () => {
73
+ await expect(requestPrinters({ transport: 'bluetooth-spp', deviceKey: 'NOT_A_KEY' })).rejects.toThrow(/unknown deviceKey/);
74
+ });
75
+ it('rejects a deviceKey for a model that does not declare bluetooth-spp', async () => {
76
+ // PT_P750W is a registered USB-only model — no bluetooth-spp entry.
77
+ expect(DEVICES.PT_P750W.transports['bluetooth-spp']).toBeUndefined();
78
+ await expect(requestPrinters({ transport: 'bluetooth-spp', deviceKey: DEVICES.PT_P750W.key })).rejects.toThrow(/does not declare bluetooth-spp/);
79
+ });
80
+ it('opens the Web Serial picker and returns a 1-key adapter map for a known SPP model', async () => {
81
+ const port = createMockSerialPort();
82
+ const requestPort = vi.fn().mockResolvedValue(port);
83
+ vi.stubGlobal('navigator', { serial: { requestPort } });
84
+ const printers = await requestPrinters({
85
+ transport: 'bluetooth-spp',
86
+ deviceKey: DEVICES.QL_820NWBc.key,
87
+ });
88
+ const roles = Object.keys(printers);
89
+ expect(roles).toHaveLength(1);
90
+ expect(roles[0]).toBe(DEVICES.QL_820NWBc.engines[0].role);
91
+ expect(printers[roles[0]].family).toBe('brother-ql');
92
+ expect(requestPort).toHaveBeenCalledOnce();
93
+ await printers[roles[0]].close();
94
+ });
95
+ it('continueWith opens the Web Serial picker with the operator-chosen SPP model', async () => {
96
+ const port = createMockSerialPort();
97
+ const requestPort = vi.fn().mockResolvedValue(port);
98
+ vi.stubGlobal('navigator', { serial: { requestPort } });
99
+ try {
100
+ await requestPrinters({ transport: 'bluetooth-spp' });
101
+ throw new Error('expected DeviceIdentificationRequiredError');
102
+ }
103
+ catch (err) {
104
+ if (!(err instanceof DeviceIdentificationRequiredError))
105
+ throw err;
106
+ // No picker is opened until the operator resolves the choice.
107
+ expect(requestPort).not.toHaveBeenCalled();
108
+ const printers = await err.continueWith(DEVICES.PT_P910BT.key);
109
+ const roles = Object.keys(printers);
110
+ expect(roles).toHaveLength(1);
111
+ expect(requestPort).toHaveBeenCalledOnce();
112
+ await printers[roles[0]].close();
113
+ }
114
+ });
115
+ it('continueWith rejects an unknown deviceKey', async () => {
116
+ try {
117
+ await requestPrinters({ transport: 'bluetooth-spp' });
118
+ throw new Error('expected DeviceIdentificationRequiredError');
119
+ }
120
+ catch (err) {
121
+ if (!(err instanceof DeviceIdentificationRequiredError))
122
+ throw err;
123
+ await expect(err.continueWith('NOT_A_KEY')).rejects.toThrow(/unknown deviceKey/);
124
+ }
125
+ });
126
+ });
127
+ describe('transport: serial', () => {
128
+ it('throws because brother-ql does not declare serial', async () => {
129
+ await expect(requestPrinters({ transport: 'serial' })).rejects.toThrow(/not declared/);
130
+ });
131
+ });
132
+ describe('transport: bluetooth-gatt', () => {
133
+ it('throws because brother-ql does not declare bluetooth-gatt', async () => {
134
+ await expect(requestPrinters({ transport: 'bluetooth-gatt' })).rejects.toThrow(/not declared/);
135
+ });
136
+ });
137
+ describe('transport: usb — auto-identify', () => {
138
+ it('returns a 1-key adapter map when the picked USBDevice matches a registry entry', async () => {
139
+ // QL_820NWBc canonical VID/PID — registered.
140
+ const known = DEVICES.QL_820NWBc.transports.usb;
141
+ if (!known)
142
+ throw new Error('QL_820NWBc missing usb transport in registry');
143
+ const device = createMockUSBDevice({
144
+ vendorId: Number.parseInt(known.vid, 16),
145
+ productId: Number.parseInt(known.pid, 16),
146
+ });
147
+ const usbStub = {
148
+ requestDevice: vi.fn().mockResolvedValue(device),
149
+ getDevices: vi.fn().mockResolvedValue([device]),
150
+ };
151
+ vi.stubGlobal('navigator', { usb: usbStub });
152
+ const printers = await requestPrinters({ transport: 'usb' });
153
+ const roles = Object.keys(printers);
154
+ expect(roles).toHaveLength(1);
155
+ const printer = printers[roles[0]];
156
+ expect(printer.family).toBe('brother-ql');
157
+ await printer.close();
158
+ });
159
+ it('uses an explicit deviceKey to wrap the picked USBDevice without registry auto-match', async () => {
160
+ // Pass a deviceKey alongside a device whose VID/PID would NOT
161
+ // auto-match — the explicit key takes the priority branch.
162
+ const device = createMockUSBDevice({ vendorId: 0x04f9, productId: 0xabcd });
163
+ const usbStub = {
164
+ requestDevice: vi.fn().mockResolvedValue(device),
165
+ getDevices: vi.fn().mockResolvedValue([device]),
166
+ };
167
+ vi.stubGlobal('navigator', { usb: usbStub });
168
+ const printers = await requestPrinters({
169
+ transport: 'usb',
170
+ deviceKey: DEVICES.QL_820NWBc.key,
171
+ });
172
+ const roles = Object.keys(printers);
173
+ expect(roles).toHaveLength(1);
174
+ expect(roles[0]).toBe(DEVICES.QL_820NWBc.engines[0].role);
175
+ await printers[roles[0]].close();
176
+ });
177
+ it('rejects an explicit unknown deviceKey on usb', async () => {
178
+ const device = createMockUSBDevice({ vendorId: 0x04f9, productId: 0x209d });
179
+ const usbStub = {
180
+ requestDevice: vi.fn().mockResolvedValue(device),
181
+ getDevices: vi.fn().mockResolvedValue([device]),
182
+ };
183
+ vi.stubGlobal('navigator', { usb: usbStub });
184
+ await expect(requestPrinters({ transport: 'usb', deviceKey: 'NOT_A_KEY' })).rejects.toThrow(/unknown deviceKey/);
185
+ });
186
+ it('continueWith rejects an unknown deviceKey on the usb path', async () => {
187
+ const device = createMockUSBDevice({ vendorId: 0x04f9, productId: 0xabcd });
188
+ const usbStub = {
189
+ requestDevice: vi.fn().mockResolvedValue(device),
190
+ getDevices: vi.fn().mockResolvedValue([device]),
191
+ };
192
+ vi.stubGlobal('navigator', { usb: usbStub });
193
+ try {
194
+ await requestPrinters({ transport: 'usb' });
195
+ throw new Error('expected DeviceIdentificationRequiredError');
196
+ }
197
+ catch (err) {
198
+ if (!(err instanceof DeviceIdentificationRequiredError))
199
+ throw err;
200
+ await expect(err.continueWith('NOT_A_KEY')).rejects.toThrow(/unknown deviceKey/);
201
+ }
202
+ });
203
+ it('throws DeviceIdentificationRequiredError when the picked device has unknown VID/PID', async () => {
204
+ const device = createMockUSBDevice({ vendorId: 0xdead, productId: 0xbeef });
205
+ const usbStub = {
206
+ requestDevice: vi.fn().mockResolvedValue(device),
207
+ getDevices: vi.fn().mockResolvedValue([device]),
208
+ };
209
+ vi.stubGlobal('navigator', { usb: usbStub });
210
+ await expect(requestPrinters({ transport: 'usb' })).rejects.toBeInstanceOf(DeviceIdentificationRequiredError);
211
+ });
212
+ it('continueWith resumes with the chosen deviceKey reusing the picked USBDevice', async () => {
213
+ // Use a real Brother VID with an unknown PID — the registry
214
+ // won't match, the picker fires once, and continueWith wraps
215
+ // the same device with the operator's choice.
216
+ const device = createMockUSBDevice({ vendorId: 0x04f9, productId: 0xabcd });
217
+ const requestDevice = vi.fn().mockResolvedValue(device);
218
+ const usbStub = {
219
+ requestDevice,
220
+ getDevices: vi.fn().mockResolvedValue([device]),
221
+ };
222
+ vi.stubGlobal('navigator', { usb: usbStub });
223
+ try {
224
+ await requestPrinters({ transport: 'usb' });
225
+ throw new Error('expected DeviceIdentificationRequiredError');
226
+ }
227
+ catch (err) {
228
+ if (!(err instanceof DeviceIdentificationRequiredError))
229
+ throw err;
230
+ const printers = await err.continueWith(DEVICES.QL_820NWBc.key);
231
+ const roles = Object.keys(printers);
232
+ expect(roles).toHaveLength(1);
233
+ // continueWith must not open the picker a second time.
234
+ expect(requestDevice).toHaveBeenCalledTimes(1);
235
+ await printers[roles[0]].close();
236
+ }
237
+ });
238
+ });
239
+ });
240
+ describe('devicesForTransport — brother-ql', () => {
241
+ it('returns only entries declaring the given transport', () => {
242
+ for (const entry of devicesForTransport('usb')) {
243
+ expect(entry.transports.usb).toBeDefined();
244
+ }
245
+ for (const entry of devicesForTransport('bluetooth-spp')) {
246
+ expect(entry.transports['bluetooth-spp']).toBeDefined();
247
+ }
248
+ });
249
+ });
250
+ //# sourceMappingURL=request-printers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-printers.test.js","sourceRoot":"","sources":["../../src/__tests__/request-printers.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;;;GASG;AACH,SAAS,oBAAoB;IAC3B,IAAI,YAAsC,CAAC;IAC3C,MAAM,MAAM,GAAG;QACb,IAAI,EAAE,GAAG,EAAE,CACT,IAAI,OAAO,CAAuC,OAAO,CAAC,EAAE;YAC1D,YAAY,GAAG,GAAG,EAAE;gBAClB,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC,CAAC;QACJ,CAAC,CAAC;QACJ,MAAM,EAAE,GAAG,EAAE;YACX,YAAY,EAAE,EAAE,CAAC;YACjB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QACD,WAAW,EAAE,GAAG,EAAE;YAChB,WAAW;QACb,CAAC;KACF,CAAC;IACF,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;QAC9B,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;QAC9B,WAAW,EAAE,GAAG,EAAE;YAChB,WAAW;QACb,CAAC;KACF,CAAC;IACF,OAAO;QACL,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,QAAQ,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACrC,QAAQ,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;KACtC,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAClE,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;YAChG,+DAA+D;YAC/D,MAAM,MAAM,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAClF,iCAAiC,CAClC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;YAChG,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;gBACtD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iCAAiC,CAAC;oBAAE,MAAM,GAAG,CAAC;gBACnE,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC5C,iEAAiE;gBACjE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC9C,KAAK,MAAM,SAAS,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;oBACvC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,MAAM,CACV,eAAe,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CACxE,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;YACnF,oEAAoE;YACpE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;YACrE,MAAM,MAAM,CACV,eAAe,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CACjF,CAAC,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;YACjG,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;YACpC,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACpD,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;YAExD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC;gBACrC,SAAS,EAAE,eAAe;gBAC1B,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG;aAClC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC;YAC3D,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,CAAC,WAAW,CAAC,CAAC,oBAAoB,EAAE,CAAC;YAC3C,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAE,CAAC,KAAK,EAAE,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;YAC3F,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;YACpC,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACpD,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;YAExD,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;gBACtD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iCAAiC,CAAC;oBAAE,MAAM,GAAG,CAAC;gBACnE,8DAA8D;gBAC9D,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;gBAC3C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;gBAC9B,MAAM,CAAC,WAAW,CAAC,CAAC,oBAAoB,EAAE,CAAC;gBAC3C,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAE,CAAC,KAAK,EAAE,CAAC;YACrC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;gBACtD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iCAAiC,CAAC;oBAAE,MAAM,GAAG,CAAC;gBACnE,MAAM,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;YACnF,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,MAAM,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,MAAM,MAAM,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC5E,cAAc,CACf,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC9C,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;YAC9F,6CAA6C;YAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;YAChD,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC5E,MAAM,MAAM,GAAG,mBAAmB,CAAC;gBACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;gBACxC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;aAC1C,CAAC,CAAC;YACH,MAAM,OAAO,GAAG;gBACd,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;gBAChD,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,CAAC;YACF,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE7C,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAE,CAAC;YACrC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1C,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;YACnG,8DAA8D;YAC9D,2DAA2D;YAC3D,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,OAAO,GAAG;gBACd,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;gBAChD,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,CAAC;YACF,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE7C,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC;gBACrC,SAAS,EAAE,KAAK;gBAChB,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG;aAClC,CAAC,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC;YAC3D,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAE,CAAC,KAAK,EAAE,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,OAAO,GAAG;gBACd,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;gBAChD,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,CAAC;YACF,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE7C,MAAM,MAAM,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACzF,mBAAmB,CACpB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,OAAO,GAAG;gBACd,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;gBAChD,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,CAAC;YACF,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE7C,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iCAAiC,CAAC;oBAAE,MAAM,GAAG,CAAC;gBACnE,MAAM,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;YACnF,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;YACnG,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,OAAO,GAAG;gBACd,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC;gBAChD,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,CAAC;YACF,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE7C,MAAM,MAAM,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CACxE,iCAAiC,CAClC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;YAC3F,4DAA4D;YAC5D,6DAA6D;YAC7D,8CAA8C;YAC9C,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5E,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACxD,MAAM,OAAO,GAAG;gBACd,aAAa;gBACb,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;aAChD,CAAC;YACF,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAE7C,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iCAAiC,CAAC;oBAAE,MAAM,GAAG,CAAC;gBACnE,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;gBAChE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;gBAC9B,uDAAuD;gBACvD,MAAM,CAAC,aAAa,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;gBAC/C,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,CAAE,CAAC,KAAK,EAAE,CAAC;YACrC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,KAAK,MAAM,KAAK,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,mBAAmB,CAAC,eAAe,CAAC,EAAE,CAAC;YACzD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -4,10 +4,21 @@ export interface MockUSBDevice extends USBDevice {
4
4
  data: Uint8Array;
5
5
  }[];
6
6
  __statusBytes: Uint8Array;
7
+ /**
8
+ * Inject an unsolicited status frame into the bulk-IN pipe. Used by
9
+ * tests to simulate spontaneous events the printer pushes outside of
10
+ * a getStatus request (lid open/close, media insert, etc.).
11
+ */
12
+ __pushUnsolicited: (frame: Uint8Array) => void;
7
13
  }
8
14
  export declare function createMockUSBDevice(overrides?: {
9
15
  vendorId?: number;
10
16
  productId?: number;
17
+ /**
18
+ * Response template for ESC iS (`1B 69 53`). Each status request
19
+ * enqueues a copy of this frame — modelling the real printer's
20
+ * "request → one response" cycle. Defaults to a 32-byte zero frame.
21
+ */
11
22
  statusBytes?: Uint8Array;
12
23
  }): MockUSBDevice;
13
24
  //# sourceMappingURL=webusb-mock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webusb-mock.d.ts","sourceRoot":"","sources":["../../src/__tests__/webusb-mock.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,WAAW,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC;IAC5D,aAAa,EAAE,UAAU,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B,GAAG,aAAa,CAoEhB"}
1
+ {"version":3,"file":"webusb-mock.d.ts","sourceRoot":"","sources":["../../src/__tests__/webusb-mock.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,WAAW,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC;IAC5D,aAAa,EAAE,UAAU,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAChD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B,GAAG,aAAa,CAoHhB"}
@@ -2,6 +2,19 @@ import { vi } from 'vitest';
2
2
  export function createMockUSBDevice(overrides) {
3
3
  const transfers = [];
4
4
  let opened = false;
5
+ // Single in-flight pipe queue. Each ESC iS write appends a response
6
+ // copy. Pending readers (calls to `transferIn` made while the queue
7
+ // is empty) park in `waiters` and are resolved as soon as a frame is
8
+ // appended — matching real WebUSB bulk-IN behaviour.
9
+ const frameQueue = [];
10
+ const waiters = [];
11
+ function enqueue(frame) {
12
+ const waiter = waiters.shift();
13
+ if (waiter)
14
+ waiter(frame);
15
+ else
16
+ frameQueue.push(frame);
17
+ }
5
18
  const endpoints = [
6
19
  { endpointNumber: 2, direction: 'out' },
7
20
  { endpointNumber: 1, direction: 'in' },
@@ -40,6 +53,20 @@ export function createMockUSBDevice(overrides) {
40
53
  }),
41
54
  close: vi.fn(() => {
42
55
  opened = false;
56
+ // Reject any pending readers with a TransportClosed-shaped error
57
+ // so the read loop can exit cleanly.
58
+ while (waiters.length > 0) {
59
+ const waiter = waiters.shift();
60
+ if (waiter) {
61
+ const err = new Error('Transport closed');
62
+ err.name = 'TransportClosedError';
63
+ // The mock's transferIn signals via the resolved Promise — to
64
+ // surface a rejection, route via the waiter as a thrown call.
65
+ // Using a sentinel "empty" frame works because the read loop
66
+ // checks `transport.connected` which flips to false on close.
67
+ waiter(new Uint8Array(0));
68
+ }
69
+ }
43
70
  return Promise.resolve();
44
71
  }),
45
72
  selectConfiguration: vi.fn(() => Promise.resolve()),
@@ -48,18 +75,40 @@ export function createMockUSBDevice(overrides) {
48
75
  transferOut: vi.fn((endpointNumber, data) => {
49
76
  const array = data instanceof Uint8Array ? data : new Uint8Array(data);
50
77
  transfers.push({ endpointNumber, data: Uint8Array.from(array) });
78
+ // Brother ESC iS = `1B 69 53`. Enqueue a response copy.
79
+ if (array.length >= 3 && array[0] === 0x1b && array[1] === 0x69 && array[2] === 0x53) {
80
+ enqueue(Uint8Array.from(statusBytes));
81
+ }
51
82
  return Promise.resolve({ bytesWritten: array.byteLength, status: 'ok' });
52
83
  }),
53
84
  transferIn: vi.fn((_endpointNumber, length) => {
54
- const out = new Uint8Array(length);
55
- out.set(statusBytes.subarray(0, length));
56
- return Promise.resolve({
57
- data: new DataView(out.buffer),
58
- status: 'ok',
85
+ // Drain any pre-queued frames first.
86
+ const next = frameQueue.shift();
87
+ if (next) {
88
+ const out = new Uint8Array(length);
89
+ out.set(next.subarray(0, length));
90
+ return Promise.resolve({
91
+ data: new DataView(out.buffer),
92
+ status: 'ok',
93
+ });
94
+ }
95
+ // Park as a waiter; resolve when a future frame is enqueued.
96
+ return new Promise(resolve => {
97
+ waiters.push(frame => {
98
+ const out = new Uint8Array(length);
99
+ out.set(frame.subarray(0, Math.min(length, frame.length)));
100
+ resolve({
101
+ data: new DataView(out.buffer, 0, frame.length),
102
+ status: 'ok',
103
+ });
104
+ });
59
105
  });
60
106
  }),
61
107
  __transfers: transfers,
62
108
  __statusBytes: statusBytes,
109
+ __pushUnsolicited: (frame) => {
110
+ enqueue(Uint8Array.from(frame));
111
+ },
63
112
  };
64
113
  return device;
65
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"webusb-mock.js","sourceRoot":"","sources":["../../src/__tests__/webusb-mock.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webusb-mock.js","sourceRoot":"","sources":["../../src/__tests__/webusb-mock.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,6 @@
1
- export { DEFAULT_FILTERS, WebBrotherQLPrinter, fromUSBDevice, requestPrinter, type RequestOptions, } from './printer.js';
1
+ export { DEFAULT_FILTERS, WebBrotherQLPrinter, type RequestOptions } from './printer.js';
2
+ export { fromUSBDevice, fromUSBDeviceAll, requestPrinter, requestPrintersUsbLegacy, } from './printer.js';
3
+ export { devicesForTransport, requestPrinters } from './request-printers.js';
4
+ export type { ConnectOptions, PrinterAdapterMap } from '@thermal-label/contracts';
5
+ export { DeviceIdentificationRequiredError } from '@thermal-label/contracts';
2
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAGzF,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7E,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,7 @@
1
- export { DEFAULT_FILTERS, WebBrotherQLPrinter, fromUSBDevice, requestPrinter, } from './printer.js';
1
+ export { DEFAULT_FILTERS, WebBrotherQLPrinter } from './printer.js';
2
+ /* eslint-disable @typescript-eslint/no-deprecated -- legacy factories re-exported during plan-10 transition */
3
+ export { fromUSBDevice, fromUSBDeviceAll, requestPrinter, requestPrintersUsbLegacy, } from './printer.js';
4
+ /* eslint-enable @typescript-eslint/no-deprecated */
5
+ export { devicesForTransport, requestPrinters } from './request-printers.js';
6
+ export { DeviceIdentificationRequiredError } from '@thermal-label/contracts';
2
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,cAAc,GAEf,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAuB,MAAM,cAAc,CAAC;AAEzF,+GAA+G;AAC/G,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,oDAAoD;AAEpD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/printer.d.ts CHANGED
@@ -15,13 +15,69 @@ export declare class WebBrotherQLPrinter implements PrinterAdapter {
15
15
  readonly device: BrotherQLDevice;
16
16
  private readonly transport;
17
17
  private lastStatus;
18
+ private readonly statusListeners;
19
+ private readLoopStarted;
20
+ private readLoopStopped;
21
+ /**
22
+ * Serialises every bulk-OUT operation (print + getStatus). Required
23
+ * because `getStatus()` may write the QL preamble (`buildInvalidate` —
24
+ * 200×0x00 + `ESC @`) to put the parser in a known state, and the
25
+ * preamble's "invalidate" cancels any in-flight print job. Polling
26
+ * concurrently with a print would shred the raster stream.
27
+ *
28
+ * Plan 15 A4: this was a hand-rolled `writeLock`/`runLocked` pair;
29
+ * it is now the shared `WriteSerializer` from `@thermal-label/contracts`
30
+ * so all four drivers serialise identically. Behaviour is unchanged —
31
+ * `WriteSerializer.run()` is exactly the old `runLocked` semantics.
32
+ */
33
+ private readonly serializer;
34
+ /**
35
+ * Whether the printer's command parser is in a known-clean state.
36
+ * Set to `true` after a successful `getStatus()`; flipped back to
37
+ * `false` on timeout so the next attempt re-sends the preamble. The
38
+ * preamble (200-byte invalidate + `ESC @`) is only needed once per
39
+ * session unless the parser falls back into a confused state.
40
+ */
41
+ private parserReady;
18
42
  constructor(device: BrotherQLDevice, transport: Transport);
19
43
  get model(): string;
20
44
  get connected(): boolean;
21
45
  print(image: RawImageData, media?: MediaDescriptor, options?: BrotherQLPrintOptions): Promise<void>;
22
46
  private writeChunked;
23
47
  createPreview(image: RawImageData, options?: PreviewOptions): Promise<PreviewResult>;
48
+ /**
49
+ * Send ESC iS and resolve with the next status frame the printer
50
+ * emits. The read loop is the sole reader of the bulk-IN pipe —
51
+ * `getStatus()` subscribes transiently, writes the request, and the
52
+ * subscription receives the response (alongside any spontaneous
53
+ * frames; see `onStatus()`).
54
+ *
55
+ * The QL preamble (200×0x00 invalidate + `ESC @` initialize) is
56
+ * sent only when the parser hasn't been confirmed clean — first
57
+ * call of the session, or after a previous timeout. Steady-state
58
+ * polls send bare `ESC iS`. Bench observation: a freshly-opened QL
59
+ * doesn't reply to bare `ESC iS` until the invalidate flushes any
60
+ * stale parser state from a prior session.
61
+ */
24
62
  getStatus(): Promise<BrotherQLStatus>;
63
+ /**
64
+ * Subscribe to status updates. A polling shim built on
65
+ * `pollingOnStatus` from contracts — it calls `getStatus()` on first
66
+ * subscribe and then every `DEFAULT_POLLING_INTERVAL_MS`, matching the
67
+ * labelwriter and labelmanager web drivers (plan 11 §`onStatus`
68
+ * parity).
69
+ *
70
+ * An earlier revision skipped polling and relied solely on the
71
+ * printer's unsolicited frames. That was a workaround for the Chromium
72
+ * WebUSB sub-packet stall — `getStatus()`'s `read()` would hang, so
73
+ * polling looked unreliable and was dropped. `WebUsbTransport.read()`
74
+ * now rounds reads up to the endpoint packet size, so `getStatus()` is
75
+ * dependable and polling is restored.
76
+ */
77
+ onStatus(cb: (status: BrotherQLStatus) => void): () => void;
78
+ private nextStatusFrame;
79
+ private startReadLoop;
80
+ private readLoop;
25
81
  close(): Promise<void>;
26
82
  }
27
83
  export declare const DEFAULT_FILTERS: USBDeviceFilter[];
@@ -30,12 +86,49 @@ export declare const DEFAULT_FILTERS: USBDeviceFilter[];
30
86
  *
31
87
  * Requires a user gesture. Opens the device and claims interface 0 via
32
88
  * `WebUsbTransport.fromDevice()`.
89
+ *
90
+ * Single-instance entry point — preserved for back-compat with existing
91
+ * consumers (CLIs, ad-hoc scripts). For the symmetric driver-web shape
92
+ * (1-key map keyed by engine role) call `requestPrinters()` instead;
93
+ * the harness shell uses that path.
94
+ *
95
+ * @deprecated Use `requestPrinters({ transport: 'usb' })` from
96
+ * `./request-printers.ts`. Removed once consumers migrate (plan 11).
33
97
  */
34
98
  export declare function requestPrinter(options?: RequestOptions): Promise<WebBrotherQLPrinter>;
99
+ /**
100
+ * Show the browser's USB picker and return one `PrinterAdapter` per
101
+ * drivable engine on the selected device, keyed by engine role.
102
+ *
103
+ * Brother QL devices are always single-engine — this returns a 1-key
104
+ * record keyed by the device's `engines[0].role` (typically `'primary'`).
105
+ *
106
+ * @deprecated Use the generic `requestPrinters({ transport: 'usb' })`
107
+ * from `./request-printers.ts` — the legacy USB-only name is preserved
108
+ * as `requestPrintersUsbLegacy` for back-compat. Removed once
109
+ * consumers migrate (plan 11).
110
+ */
111
+ export declare function requestPrintersUsbLegacy(options?: RequestOptions): Promise<Record<string, WebBrotherQLPrinter>>;
35
112
  /**
36
113
  * Wrap an already-selected `USBDevice`.
37
114
  *
38
115
  * @throws when the VID/PID is not in the Brother QL registry.
116
+ *
117
+ * @deprecated Use `requestPrinters({ transport: 'usb' })` from
118
+ * `./request-printers.ts`. Removed once consumers migrate (plan 11).
39
119
  */
40
120
  export declare function fromUSBDevice(usbDevice: USBDevice): Promise<WebBrotherQLPrinter>;
121
+ /**
122
+ * Wrap an already-selected `USBDevice` and return a 1-key adapter map
123
+ * keyed by the device's `engines[0].role`. Public surface for
124
+ * `requestPrinters()`; exported so harnesses that already hold a
125
+ * `USBDevice` (e.g. picked-up via `navigator.usb.getDevices()` on a
126
+ * returning visit) can skip the picker.
127
+ *
128
+ * Brother QL is single-engine and single-interface (IF 0).
129
+ *
130
+ * @deprecated Use `requestPrinters({ transport: 'usb' })` from
131
+ * `./request-printers.ts`. Removed once consumers migrate (plan 11).
132
+ */
133
+ export declare function fromUSBDeviceAll(usbDevice: USBDevice): Promise<Record<string, WebBrotherQLPrinter>>;
41
134
  //# sourceMappingURL=printer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"printer.d.ts","sourceRoot":"","sources":["../src/printer.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,eAAe,EAEf,qBAAqB,EACrB,eAAe,EAEf,eAAe,EAEf,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,SAAS,EACV,MAAM,gCAAgC,CAAC;AAYxC,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACxD,QAAQ,CAAC,MAAM,eAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,UAAU,CAA8B;gBAEpC,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS;IAKzD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,KAAK,CACT,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC;YAuCF,YAAY;IAU1B,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAW9E,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAQrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED,eAAO,MAAM,eAAe,EAAE,eAAe,EAGmC,CAAC;AAEjF;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAI/F;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAStF"}
1
+ {"version":3,"file":"printer.d.ts","sourceRoot":"","sources":["../src/printer.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,eAAe,EAEf,qBAAqB,EACrB,eAAe,EAEf,eAAe,EAEf,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,SAAS,EACV,MAAM,gCAAgC,CAAC;AA4BxC,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACxD,QAAQ,CAAC,MAAM,eAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgD;IAChF,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,eAAe,CAAS;IAChC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD;;;;;;OAMG;IACH,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS;IAMzD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,KAAK,CACT,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC;YAgDF,YAAY;IAU1B,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAWpF;;;;;;;;;;;;;OAaG;IACG,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAqB3C;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI;IAI3D,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,aAAa;YAOP,QAAQ;IAuChB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAED,eAAO,MAAM,eAAe,EAAE,eAAe,EAGmC,CAAC;AAEjF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAK/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAK9C;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAStF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAc9C"}