@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.
package/dist/printer.js CHANGED
@@ -1,7 +1,22 @@
1
- import { DEFAULT_MEDIA, DEVICES, ROTATE_DIRECTION, STATUS_REQUEST, createPreviewOffline, encodeJobForEngine, findDevice, flipHorizontal, parseStatus, pickRotation, renderImage, renderMultiPlaneImage, } from '@thermal-label/brother-ql-core';
2
- import { MediaNotSpecifiedError } from '@thermal-label/contracts';
1
+ import { DEFAULT_MEDIA, DEVICES, ROTATE_DIRECTION, STATUS_REQUEST, buildInitialize, buildInvalidate, createPreviewOffline, encodeJobForEngine, findDevice, flipHorizontal, parseStatus, pickRotation, renderImage, renderMultiPlaneImage, } from '@thermal-label/brother-ql-core';
2
+ import { MediaNotSpecifiedError, pollingOnStatus, WriteSerializer } from '@thermal-label/contracts';
3
3
  import { WebUsbTransport } from '@thermal-label/transport/web';
4
+ // Detect transport errors across module boundaries — under pnpm link /
5
+ // multi-version installs `instanceof` checks against the contracts
6
+ // classes can return false even for the right class. The contracts
7
+ // classes pin `this.name`, so name-checking is the portable test.
8
+ function isTransportClosedError(err) {
9
+ return err instanceof Error && err.name === 'TransportClosedError';
10
+ }
4
11
  const STATUS_BYTE_COUNT = 32;
12
+ // A persistent read loop is the sole reader of the bulk-IN pipe: it
13
+ // parses every 32-byte status frame the printer emits and dispatches it
14
+ // to `statusListeners`. `getStatus()` writes ESC iS and awaits the next
15
+ // frame from that stream; spontaneous frames (lid open/close, media
16
+ // insert, end of job, errors) flow through the same loop. `onStatus()`
17
+ // is a polling shim over `getStatus()` — see that method.
18
+ const STATUS_RESPONSE_TIMEOUT_MS = 1500;
19
+ const READ_LOOP_BACKOFF_MS = 100;
5
20
  // Same OUT-pipe chunking as the Node driver — see packages/node/src/printer.ts
6
21
  // for the rationale. WebUSB rides the same libusb-style bulk transfer path,
7
22
  // so the firmware buffer-overrun risk is identical.
@@ -20,9 +35,34 @@ export class WebBrotherQLPrinter {
20
35
  device;
21
36
  transport;
22
37
  lastStatus;
38
+ statusListeners = new Set();
39
+ readLoopStarted = false;
40
+ readLoopStopped = false;
41
+ /**
42
+ * Serialises every bulk-OUT operation (print + getStatus). Required
43
+ * because `getStatus()` may write the QL preamble (`buildInvalidate` —
44
+ * 200×0x00 + `ESC @`) to put the parser in a known state, and the
45
+ * preamble's "invalidate" cancels any in-flight print job. Polling
46
+ * concurrently with a print would shred the raster stream.
47
+ *
48
+ * Plan 15 A4: this was a hand-rolled `writeLock`/`runLocked` pair;
49
+ * it is now the shared `WriteSerializer` from `@thermal-label/contracts`
50
+ * so all four drivers serialise identically. Behaviour is unchanged —
51
+ * `WriteSerializer.run()` is exactly the old `runLocked` semantics.
52
+ */
53
+ serializer = new WriteSerializer();
54
+ /**
55
+ * Whether the printer's command parser is in a known-clean state.
56
+ * Set to `true` after a successful `getStatus()`; flipped back to
57
+ * `false` on timeout so the next attempt re-sends the preamble. The
58
+ * preamble (200-byte invalidate + `ESC @`) is only needed once per
59
+ * session unless the parser falls back into a confused state.
60
+ */
61
+ parserReady = false;
23
62
  constructor(device, transport) {
24
63
  this.device = device;
25
64
  this.transport = transport;
65
+ this.startReadLoop();
26
66
  }
27
67
  get model() {
28
68
  return this.device.name;
@@ -39,7 +79,16 @@ export class WebBrotherQLPrinter {
39
79
  // on the right side of the printed face when the leading edge is held
40
80
  // up. Mirror the rendered bitmap so the input image's x-axis matches
41
81
  // the printed x-axis. Verified on QL-820NWBc + DK-22251.
42
- const pageOptions = options?.highRes === true ? { highResolution: true } : undefined;
82
+ // PackBits compression on by default for the web path. Bench
83
+ // experience: WebUSB pacing alone (1024 / 20 ms) isn't enough on
84
+ // QL_700-class firmware — uncompressed raster rows can stall the
85
+ // print engine even when bytes are arriving. Compression keeps
86
+ // each row small enough to ride the firmware's buffer comfortably
87
+ // and matches the encoding the Brother driver itself emits.
88
+ const pageOptions = {
89
+ compress: true,
90
+ ...(options?.highRes === true ? { highResolution: true } : {}),
91
+ };
43
92
  let page;
44
93
  if (resolvedMedia.palette) {
45
94
  const { black, red } = renderMultiPlaneImage(image, {
@@ -50,7 +99,7 @@ export class WebBrotherQLPrinter {
50
99
  bitmap: flipHorizontal(black),
51
100
  redBitmap: flipHorizontal(red),
52
101
  media: resolvedMedia,
53
- ...(pageOptions ? { options: pageOptions } : {}),
102
+ options: pageOptions,
54
103
  };
55
104
  }
56
105
  else {
@@ -58,13 +107,13 @@ export class WebBrotherQLPrinter {
58
107
  page = {
59
108
  bitmap,
60
109
  media: resolvedMedia,
61
- ...(pageOptions ? { options: pageOptions } : {}),
110
+ options: pageOptions,
62
111
  };
63
112
  }
64
113
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- every brother-ql device has at least one engine (data invariant)
65
114
  const engine = this.device.engines[0];
66
115
  const bytes = encodeJobForEngine([page], {}, engine, this.device.name);
67
- await this.writeChunked(bytes);
116
+ await this.serializer.run(() => this.writeChunked(bytes));
68
117
  }
69
118
  async writeChunked(bytes) {
70
119
  for (let off = 0; off < bytes.length; off += USB_CHUNK_SIZE) {
@@ -87,14 +136,125 @@ export class WebBrotherQLPrinter {
87
136
  assumed: true,
88
137
  });
89
138
  }
139
+ /**
140
+ * Send ESC iS and resolve with the next status frame the printer
141
+ * emits. The read loop is the sole reader of the bulk-IN pipe —
142
+ * `getStatus()` subscribes transiently, writes the request, and the
143
+ * subscription receives the response (alongside any spontaneous
144
+ * frames; see `onStatus()`).
145
+ *
146
+ * The QL preamble (200×0x00 invalidate + `ESC @` initialize) is
147
+ * sent only when the parser hasn't been confirmed clean — first
148
+ * call of the session, or after a previous timeout. Steady-state
149
+ * polls send bare `ESC iS`. Bench observation: a freshly-opened QL
150
+ * doesn't reply to bare `ESC iS` until the invalidate flushes any
151
+ * stale parser state from a prior session.
152
+ */
90
153
  async getStatus() {
91
- await this.transport.write(STATUS_REQUEST);
92
- const bytes = await this.transport.read(STATUS_BYTE_COUNT);
93
- const status = parseStatus(bytes, this.device.engines[0]);
94
- this.lastStatus = status;
95
- return status;
154
+ return this.serializer.run(async () => {
155
+ const next = this.nextStatusFrame(STATUS_RESPONSE_TIMEOUT_MS);
156
+ if (!this.parserReady) {
157
+ await this.transport.write(buildInvalidate());
158
+ await this.transport.write(buildInitialize());
159
+ }
160
+ await this.transport.write(STATUS_REQUEST);
161
+ try {
162
+ const status = await next;
163
+ this.parserReady = true;
164
+ return status;
165
+ }
166
+ catch (err) {
167
+ // Timeout / read failure — printer may be in a confused
168
+ // state. Re-arm the preamble for the next attempt.
169
+ this.parserReady = false;
170
+ throw err;
171
+ }
172
+ });
173
+ }
174
+ /**
175
+ * Subscribe to status updates. A polling shim built on
176
+ * `pollingOnStatus` from contracts — it calls `getStatus()` on first
177
+ * subscribe and then every `DEFAULT_POLLING_INTERVAL_MS`, matching the
178
+ * labelwriter and labelmanager web drivers (plan 11 §`onStatus`
179
+ * parity).
180
+ *
181
+ * An earlier revision skipped polling and relied solely on the
182
+ * printer's unsolicited frames. That was a workaround for the Chromium
183
+ * WebUSB sub-packet stall — `getStatus()`'s `read()` would hang, so
184
+ * polling looked unreliable and was dropped. `WebUsbTransport.read()`
185
+ * now rounds reads up to the endpoint packet size, so `getStatus()` is
186
+ * dependable and polling is restored.
187
+ */
188
+ onStatus(cb) {
189
+ return pollingOnStatus(this, cb);
190
+ }
191
+ nextStatusFrame(timeoutMs) {
192
+ return new Promise((resolve, reject) => {
193
+ const timer = setTimeout(() => {
194
+ this.statusListeners.delete(handler);
195
+ reject(new Error(`Printer did not respond to status request within ${String(timeoutMs)}ms`));
196
+ }, timeoutMs);
197
+ const handler = (s) => {
198
+ clearTimeout(timer);
199
+ this.statusListeners.delete(handler);
200
+ resolve(s);
201
+ };
202
+ this.statusListeners.add(handler);
203
+ });
204
+ }
205
+ startReadLoop() {
206
+ /* v8 ignore next -- defensive: startReadLoop is only ever called once, from the constructor, where readLoopStarted is still false; the re-entry guard can never be true */
207
+ if (this.readLoopStarted)
208
+ return;
209
+ this.readLoopStarted = true;
210
+ void this.readLoop();
211
+ }
212
+ async readLoop() {
213
+ while (!this.readLoopStopped && this.transport.connected) {
214
+ let bytes;
215
+ try {
216
+ bytes = await this.transport.read(STATUS_BYTE_COUNT);
217
+ }
218
+ catch (err) {
219
+ if (isTransportClosedError(err))
220
+ return;
221
+ // If close() ran while the read was pending, the next iteration's
222
+ // while-guard exits the loop — no early return needed.
223
+ // Transient error — back off briefly to avoid hot-spinning.
224
+ // eslint-disable-next-line no-console -- driver-level diagnostic
225
+ console.warn('[brother-ql-web] status read error, backing off:', err);
226
+ await new Promise(r => setTimeout(r, READ_LOOP_BACKOFF_MS));
227
+ continue;
228
+ }
229
+ if (bytes.length < STATUS_BYTE_COUNT)
230
+ continue;
231
+ let status;
232
+ try {
233
+ status = parseStatus(bytes, this.device.engines[0]);
234
+ }
235
+ catch (err) {
236
+ // eslint-disable-next-line no-console -- driver-level diagnostic
237
+ console.warn('[brother-ql-web] failed to parse status frame:', err);
238
+ continue;
239
+ }
240
+ this.lastStatus = status;
241
+ // Snapshot listeners so unsubscribes during dispatch don't skip siblings.
242
+ const snapshot = Array.from(this.statusListeners);
243
+ for (const cb of snapshot) {
244
+ try {
245
+ cb(status);
246
+ /* v8 ignore next 4 -- defensive: the only registrant of statusListeners is nextStatusFrame's internal handler (clearTimeout + delete + resolve), which cannot throw; kept for future persistent-listener callers */
247
+ }
248
+ catch (err) {
249
+ // eslint-disable-next-line no-console -- driver-level diagnostic
250
+ console.warn('[brother-ql-web] status listener threw:', err);
251
+ }
252
+ }
253
+ }
96
254
  }
97
255
  async close() {
256
+ this.readLoopStopped = true;
257
+ this.statusListeners.clear();
98
258
  await this.transport.close();
99
259
  }
100
260
  }
@@ -107,16 +267,46 @@ export const DEFAULT_FILTERS = Object.values(DEVICES)
107
267
  *
108
268
  * Requires a user gesture. Opens the device and claims interface 0 via
109
269
  * `WebUsbTransport.fromDevice()`.
270
+ *
271
+ * Single-instance entry point — preserved for back-compat with existing
272
+ * consumers (CLIs, ad-hoc scripts). For the symmetric driver-web shape
273
+ * (1-key map keyed by engine role) call `requestPrinters()` instead;
274
+ * the harness shell uses that path.
275
+ *
276
+ * @deprecated Use `requestPrinters({ transport: 'usb' })` from
277
+ * `./request-printers.ts`. Removed once consumers migrate (plan 11).
110
278
  */
111
279
  export async function requestPrinter(options = {}) {
112
280
  const filters = options.filters ?? DEFAULT_FILTERS;
113
281
  const usbDevice = await navigator.usb.requestDevice({ filters });
282
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy alias chain; the deprecation guidance is aimed at external callers, not internal usage
114
283
  return fromUSBDevice(usbDevice);
115
284
  }
285
+ /**
286
+ * Show the browser's USB picker and return one `PrinterAdapter` per
287
+ * drivable engine on the selected device, keyed by engine role.
288
+ *
289
+ * Brother QL devices are always single-engine — this returns a 1-key
290
+ * record keyed by the device's `engines[0].role` (typically `'primary'`).
291
+ *
292
+ * @deprecated Use the generic `requestPrinters({ transport: 'usb' })`
293
+ * from `./request-printers.ts` — the legacy USB-only name is preserved
294
+ * as `requestPrintersUsbLegacy` for back-compat. Removed once
295
+ * consumers migrate (plan 11).
296
+ */
297
+ export async function requestPrintersUsbLegacy(options = {}) {
298
+ const filters = options.filters ?? DEFAULT_FILTERS;
299
+ const usbDevice = await navigator.usb.requestDevice({ filters });
300
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy alias chain
301
+ return fromUSBDeviceAll(usbDevice);
302
+ }
116
303
  /**
117
304
  * Wrap an already-selected `USBDevice`.
118
305
  *
119
306
  * @throws when the VID/PID is not in the Brother QL registry.
307
+ *
308
+ * @deprecated Use `requestPrinters({ transport: 'usb' })` from
309
+ * `./request-printers.ts`. Removed once consumers migrate (plan 11).
120
310
  */
121
311
  export async function fromUSBDevice(usbDevice) {
122
312
  const descriptor = findDevice(usbDevice.vendorId, usbDevice.productId);
@@ -126,4 +316,29 @@ export async function fromUSBDevice(usbDevice) {
126
316
  const transport = await WebUsbTransport.fromDevice(usbDevice);
127
317
  return new WebBrotherQLPrinter(descriptor, transport);
128
318
  }
319
+ /**
320
+ * Wrap an already-selected `USBDevice` and return a 1-key adapter map
321
+ * keyed by the device's `engines[0].role`. Public surface for
322
+ * `requestPrinters()`; exported so harnesses that already hold a
323
+ * `USBDevice` (e.g. picked-up via `navigator.usb.getDevices()` on a
324
+ * returning visit) can skip the picker.
325
+ *
326
+ * Brother QL is single-engine and single-interface (IF 0).
327
+ *
328
+ * @deprecated Use `requestPrinters({ transport: 'usb' })` from
329
+ * `./request-printers.ts`. Removed once consumers migrate (plan 11).
330
+ */
331
+ export async function fromUSBDeviceAll(usbDevice) {
332
+ const descriptor = findDevice(usbDevice.vendorId, usbDevice.productId);
333
+ if (!descriptor) {
334
+ throw new Error(`Unsupported USB device: VID=0x${usbDevice.vendorId.toString(16)} PID=0x${usbDevice.productId.toString(16)}`);
335
+ }
336
+ const engine = descriptor.engines[0];
337
+ /* v8 ignore next 3 -- defensive: every brother-ql registry entry declares at least one engine (data invariant); guard kept against a malformed future registry edit */
338
+ if (!engine) {
339
+ throw new Error(`Device ${descriptor.key} has no engines.`);
340
+ }
341
+ const transport = await WebUsbTransport.fromDevice(usbDevice);
342
+ return { [engine.role]: new WebBrotherQLPrinter(descriptor, transport) };
343
+ }
129
344
  //# sourceMappingURL=printer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"printer.js","sourceRoot":"","sources":["../src/printer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"printer.js","sourceRoot":"","sources":["../src/printer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { type ConnectOptions, type DeviceEntry, type PrinterAdapterMap, type TransportType } from '@thermal-label/contracts';
2
+ /**
3
+ * Unified browser-picker factory for the brother-ql driver family.
4
+ *
5
+ * Dispatches on `opts.transport`:
6
+ *
7
+ * - `'usb'` — opens `navigator.usb` picker. Auto-identifies via
8
+ * `usbDevice.vendorId/productId` against the registry. Throws
9
+ * `DeviceIdentificationRequiredError` only if the picked device's
10
+ * VID/PID is not in the brother-ql registry.
11
+ * - `'bluetooth-spp'` — always-ask (Web Serial has no BT name
12
+ * surface). `opts.deviceKey` required; if omitted, throws
13
+ * `DeviceIdentificationRequiredError` with the BT-SPP-capable
14
+ * subset of `DEVICES` (e.g. QL_820NWBc, PT_P910BT).
15
+ * `continueWith(deviceKey)` opens the Web Serial picker for the
16
+ * chosen device.
17
+ * - `'serial'` — not declared in the brother-ql registry today;
18
+ * throws unconditionally.
19
+ * - `'bluetooth-gatt'` — not declared in the brother-ql registry today;
20
+ * throws unconditionally.
21
+ *
22
+ * Returns a 1-key `PrinterAdapterMap` keyed by the device's primary
23
+ * engine role.
24
+ */
25
+ export declare function requestPrinters(opts: ConnectOptions): Promise<PrinterAdapterMap>;
26
+ /**
27
+ * Filter the registry to entries declaring `transport`. Used to
28
+ * populate `DeviceIdentificationRequiredError.candidates`.
29
+ */
30
+ export declare function devicesForTransport(transport: TransportType): readonly DeviceEntry[];
31
+ //# sourceMappingURL=request-printers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-printers.d.ts","sourceRoot":"","sources":["../src/request-printers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EAEtB,KAAK,aAAa,EACnB,MAAM,0BAA0B,CAAC;AAKlC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAWtF;AAgFD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,WAAW,EAAE,CAEpF"}
@@ -0,0 +1,109 @@
1
+ import { DeviceIdentificationRequiredError, } from '@thermal-label/contracts';
2
+ import { DEVICES, findDevice } from '@thermal-label/brother-ql-core';
3
+ import { WebSerialTransport, WebUsbTransport } from '@thermal-label/transport/web';
4
+ import { DEFAULT_FILTERS, WebBrotherQLPrinter } from './printer.js';
5
+ /**
6
+ * Unified browser-picker factory for the brother-ql driver family.
7
+ *
8
+ * Dispatches on `opts.transport`:
9
+ *
10
+ * - `'usb'` — opens `navigator.usb` picker. Auto-identifies via
11
+ * `usbDevice.vendorId/productId` against the registry. Throws
12
+ * `DeviceIdentificationRequiredError` only if the picked device's
13
+ * VID/PID is not in the brother-ql registry.
14
+ * - `'bluetooth-spp'` — always-ask (Web Serial has no BT name
15
+ * surface). `opts.deviceKey` required; if omitted, throws
16
+ * `DeviceIdentificationRequiredError` with the BT-SPP-capable
17
+ * subset of `DEVICES` (e.g. QL_820NWBc, PT_P910BT).
18
+ * `continueWith(deviceKey)` opens the Web Serial picker for the
19
+ * chosen device.
20
+ * - `'serial'` — not declared in the brother-ql registry today;
21
+ * throws unconditionally.
22
+ * - `'bluetooth-gatt'` — not declared in the brother-ql registry today;
23
+ * throws unconditionally.
24
+ *
25
+ * Returns a 1-key `PrinterAdapterMap` keyed by the device's primary
26
+ * engine role.
27
+ */
28
+ export async function requestPrinters(opts) {
29
+ switch (opts.transport) {
30
+ case 'usb':
31
+ return requestPrintersUsb(opts);
32
+ case 'bluetooth-spp':
33
+ return requestPrintersBluetoothSpp(opts);
34
+ case 'serial':
35
+ throw new Error('brother-ql: serial transport not declared in the registry');
36
+ case 'bluetooth-gatt':
37
+ throw new Error('brother-ql: bluetooth-gatt transport not declared in the registry');
38
+ }
39
+ }
40
+ async function requestPrintersUsb(opts) {
41
+ const filters = DEFAULT_FILTERS;
42
+ const usbDevice = await navigator.usb.requestDevice({ filters });
43
+ if (opts.deviceKey !== undefined) {
44
+ const entry = entryByKey(opts.deviceKey);
45
+ if (!entry) {
46
+ throw new Error(`requestPrinters(usb): unknown deviceKey "${opts.deviceKey}"`);
47
+ }
48
+ const transport = await WebUsbTransport.fromDevice(usbDevice);
49
+ return adapterMap(entry, transport);
50
+ }
51
+ const entry = findDevice(usbDevice.vendorId, usbDevice.productId);
52
+ if (entry) {
53
+ const transport = await WebUsbTransport.fromDevice(usbDevice);
54
+ return adapterMap(entry, transport);
55
+ }
56
+ // No registry match — let the caller pick from the USB-capable
57
+ // candidates and keep the same USBDevice across the
58
+ // `continueWith` resume.
59
+ throw new DeviceIdentificationRequiredError(devicesForTransport('usb'), async (deviceKey) => {
60
+ const chosen = entryByKey(deviceKey);
61
+ if (!chosen) {
62
+ throw new Error(`continueWith: unknown deviceKey "${deviceKey}"`);
63
+ }
64
+ const transport = await WebUsbTransport.fromDevice(usbDevice);
65
+ return adapterMap(chosen, transport);
66
+ });
67
+ }
68
+ function requestPrintersBluetoothSpp(opts) {
69
+ if (opts.deviceKey === undefined) {
70
+ return Promise.reject(new DeviceIdentificationRequiredError(devicesForTransport('bluetooth-spp'), deviceKey => openBluetoothSpp(deviceKey, opts.baudRate)));
71
+ }
72
+ return openBluetoothSpp(opts.deviceKey, opts.baudRate);
73
+ }
74
+ async function openBluetoothSpp(deviceKey, baudRate) {
75
+ const entry = entryByKey(deviceKey);
76
+ if (!entry) {
77
+ throw new Error(`requestPrinters(bluetooth-spp): unknown deviceKey "${deviceKey}"`);
78
+ }
79
+ if (!entry.transports['bluetooth-spp']) {
80
+ throw new Error(`requestPrinters(bluetooth-spp): ${deviceKey} does not declare bluetooth-spp transport`);
81
+ }
82
+ // SPP baud is negotiated on the link; default to 9600 when not
83
+ // specified.
84
+ const transport = await WebSerialTransport.request(undefined, baudRate ?? 9600);
85
+ return adapterMap(entry, transport);
86
+ }
87
+ function adapterMap(entry, transport) {
88
+ const engine = entry.engines[0];
89
+ /* v8 ignore next -- defensive: every brother-ql registry entry declares at least one engine (data invariant); guard kept against a malformed future registry edit */
90
+ if (!engine)
91
+ throw new Error(`Device ${entry.key} has no engines.`);
92
+ // WebBrotherQLPrinter accepts a generic Transport — the encoder is
93
+ // transport-agnostic, so the BT-SPP path is just the same printer
94
+ // class wrapped around a WebSerialTransport. Bench-confirmed on
95
+ // QL-820NWBc.
96
+ const printer = new WebBrotherQLPrinter(entry, transport);
97
+ return { [engine.role]: printer };
98
+ }
99
+ /**
100
+ * Filter the registry to entries declaring `transport`. Used to
101
+ * populate `DeviceIdentificationRequiredError.candidates`.
102
+ */
103
+ export function devicesForTransport(transport) {
104
+ return Object.values(DEVICES).filter(d => transport in d.transports);
105
+ }
106
+ function entryByKey(key) {
107
+ return DEVICES[key];
108
+ }
109
+ //# sourceMappingURL=request-printers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-printers.js","sourceRoot":"","sources":["../src/request-printers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thermal-label/brother-ql-web",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "WebUSB browser driver for Brother QL label printers",
5
5
  "keywords": [
6
6
  "brother",
@@ -13,7 +13,7 @@
13
13
  "type": "module",
14
14
  "author": "Mannes Brak",
15
15
  "license": "MIT",
16
- "homepage": "https://thermal-label.github.io/brother-ql/",
16
+ "homepage": "https://thermal-label.github.io/brother-ql/web",
17
17
  "repository": {
18
18
  "type": "git",
19
19
  "url": "https://github.com/thermal-label/brother-ql.git",
@@ -52,9 +52,9 @@
52
52
  }
53
53
  },
54
54
  "dependencies": {
55
- "@thermal-label/contracts": "^0.3.1",
56
- "@thermal-label/transport": "^0.3.0",
57
- "@thermal-label/brother-ql-core": "0.4.0"
55
+ "@thermal-label/contracts": "^0.6.0",
56
+ "@thermal-label/transport": "^0.6.0",
57
+ "@thermal-label/brother-ql-core": "0.6.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "typescript": ">=5.0"