@thermal-label/brother-ql-core 0.3.0 → 0.4.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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/data/devices.json +823 -0
  3. package/data/media.json +823 -0
  4. package/dist/__tests__/devices.test.js +112 -31
  5. package/dist/__tests__/devices.test.js.map +1 -1
  6. package/dist/__tests__/media.test.js +251 -1
  7. package/dist/__tests__/media.test.js.map +1 -1
  8. package/dist/__tests__/protocol.test.js +168 -1
  9. package/dist/__tests__/protocol.test.js.map +1 -1
  10. package/dist/__tests__/status.test.js +71 -0
  11. package/dist/__tests__/status.test.js.map +1 -1
  12. package/dist/devices.d.ts +13 -270
  13. package/dist/devices.d.ts.map +1 -1
  14. package/dist/devices.generated.d.ts +696 -0
  15. package/dist/devices.generated.d.ts.map +1 -0
  16. package/dist/devices.generated.js +831 -0
  17. package/dist/devices.generated.js.map +1 -0
  18. package/dist/devices.js +28 -272
  19. package/dist/devices.js.map +1 -1
  20. package/dist/index.d.ts +6 -5
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +3 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/media.d.ts +37 -22
  25. package/dist/media.d.ts.map +1 -1
  26. package/dist/media.generated.d.ts +4 -0
  27. package/dist/media.generated.d.ts.map +1 -0
  28. package/dist/media.generated.js +1640 -0
  29. package/dist/media.generated.js.map +1 -0
  30. package/dist/media.js +74 -281
  31. package/dist/media.js.map +1 -1
  32. package/dist/protocol.d.ts +54 -3
  33. package/dist/protocol.d.ts.map +1 -1
  34. package/dist/protocol.js +117 -18
  35. package/dist/protocol.js.map +1 -1
  36. package/dist/status.d.ts +4 -1
  37. package/dist/status.d.ts.map +1 -1
  38. package/dist/status.js +6 -2
  39. package/dist/status.js.map +1 -1
  40. package/dist/types.d.ts +92 -27
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +13 -9
  43. package/src/__tests__/devices.test.ts +122 -32
  44. package/src/__tests__/media.test.ts +287 -1
  45. package/src/__tests__/protocol.test.ts +209 -0
  46. package/src/__tests__/status.test.ts +87 -0
  47. package/src/devices.generated.ts +840 -0
  48. package/src/devices.ts +30 -272
  49. package/src/index.ts +20 -4
  50. package/src/media.generated.ts +1644 -0
  51. package/src/media.ts +86 -282
  52. package/src/protocol.ts +196 -18
  53. package/src/status.ts +10 -3
  54. package/src/types.ts +93 -27
@@ -0,0 +1,840 @@
1
+ // AUTO-GENERATED by scripts/compile-data.mjs — do not edit by hand.
2
+ // Regenerate with `pnpm --filter @thermal-label/brother-ql-core compile-data`.
3
+ import type { DeviceRegistry } from '@thermal-label/contracts';
4
+ import type { BrotherQLDevice } from './types.js';
5
+
6
+ export const DEVICE_REGISTRY = {
7
+ "schemaVersion": 1,
8
+ "driver": "brother-ql",
9
+ "devices": [
10
+ {
11
+ "key": "PT_E550W",
12
+ "name": "PT-E550W",
13
+ "family": "brother-ql",
14
+ "transports": {
15
+ "usb": {
16
+ "vid": "0x04f9",
17
+ "pid": "0x2060"
18
+ },
19
+ "tcp": {
20
+ "port": 9100
21
+ }
22
+ },
23
+ "engines": [
24
+ {
25
+ "role": "primary",
26
+ "protocol": "pt-raster",
27
+ "dpi": 180,
28
+ "headDots": 128,
29
+ "mediaCompatibility": [
30
+ "tze",
31
+ "hse-2to1",
32
+ "hse-3to1"
33
+ ],
34
+ "capabilities": {
35
+ "autocut": true,
36
+ "mediaDetection": true,
37
+ "highResDpi": 360
38
+ }
39
+ }
40
+ ],
41
+ "hardwareQuirks": "Cutter requires compression to be enabled — encoder enforces this when (autocut: true && compress: false). Source: nbuchwitz/ptouch:PTE550W (\"E550W requires compression ON for cutting to work\"). Pin configurations from Brother spec cv_pte550wp750wp710bt_eng_raster_102.pdf p.20 §2.3, transcribed into media.json5 via nbuchwitz/ptouch.",
42
+ "support": {
43
+ "status": "untested"
44
+ }
45
+ },
46
+ {
47
+ "key": "PT_P750W",
48
+ "name": "PT-P750W",
49
+ "family": "brother-ql",
50
+ "transports": {
51
+ "usb": {
52
+ "vid": "0x04f9",
53
+ "pid": "0x2062"
54
+ },
55
+ "tcp": {
56
+ "port": 9100
57
+ }
58
+ },
59
+ "engines": [
60
+ {
61
+ "role": "primary",
62
+ "protocol": "pt-raster",
63
+ "dpi": 180,
64
+ "headDots": 128,
65
+ "mediaCompatibility": [
66
+ "tze",
67
+ "hse-2to1",
68
+ "hse-3to1"
69
+ ],
70
+ "capabilities": {
71
+ "autocut": true,
72
+ "mediaDetection": true,
73
+ "highResDpi": 360
74
+ }
75
+ }
76
+ ],
77
+ "capabilities": {
78
+ "massStoragePid": "0x2065"
79
+ },
80
+ "hardwareQuirks": "PID disagreement between driver projects on which mode 0x2062 vs 0x2065 represents: libptouch.c says 0x2062 is the printer PID and 0x2065 is PLite mass-storage; nbuchwitz/ptouch says 0x2065 is the printer PID. We treat libptouch.c as authoritative; flip via a contributor report if discovery fails on real hardware. Pin configurations from Brother spec cv_pte550wp750wp710bt_eng_raster_102.pdf p.20 §2.3.",
81
+ "support": {
82
+ "status": "untested"
83
+ }
84
+ },
85
+ {
86
+ "key": "PT_P900",
87
+ "name": "PT-P900",
88
+ "family": "brother-ql",
89
+ "transports": {
90
+ "usb": {
91
+ "vid": "0x04f9",
92
+ "pid": "0x2083"
93
+ }
94
+ },
95
+ "engines": [
96
+ {
97
+ "role": "primary",
98
+ "protocol": "pt-raster",
99
+ "dpi": 360,
100
+ "headDots": 560,
101
+ "mediaCompatibility": [
102
+ "tze",
103
+ "hse-2to1",
104
+ "hse-3to1"
105
+ ],
106
+ "capabilities": {
107
+ "autocut": true,
108
+ "mediaDetection": true,
109
+ "highResDpi": 720
110
+ }
111
+ }
112
+ ],
113
+ "hardwareQuirks": "Pin configurations from Brother spec cv_ptp900_eng_raster_102.pdf pp.23-24 §2.3.5, transcribed into media.json5 via nbuchwitz/ptouch:PTP900Series. Note nbuchwitz/ptouch:PTP900Series.DEFAULT_USE_COMPRESSION = False — unlike PT-E550W, P900 does NOT require compression for the cutter.",
114
+ "support": {
115
+ "status": "untested"
116
+ }
117
+ },
118
+ {
119
+ "key": "PT_P900W",
120
+ "name": "PT-P900W",
121
+ "family": "brother-ql",
122
+ "transports": {
123
+ "usb": {
124
+ "vid": "0x04f9",
125
+ "pid": "0x2085"
126
+ },
127
+ "tcp": {
128
+ "port": 9100
129
+ }
130
+ },
131
+ "engines": [
132
+ {
133
+ "role": "primary",
134
+ "protocol": "pt-raster",
135
+ "dpi": 360,
136
+ "headDots": 560,
137
+ "mediaCompatibility": [
138
+ "tze",
139
+ "hse-2to1",
140
+ "hse-3to1"
141
+ ],
142
+ "capabilities": {
143
+ "autocut": true,
144
+ "mediaDetection": true,
145
+ "highResDpi": 720
146
+ }
147
+ }
148
+ ],
149
+ "hardwareQuirks": "PID 0x2085 sourced from nbuchwitz/ptouch:PTP900W; not in most public USB-ID databases — phase 4 hardware verification should confirm.",
150
+ "support": {
151
+ "status": "untested"
152
+ }
153
+ },
154
+ {
155
+ "key": "PT_P910BT",
156
+ "name": "PT-P910BT",
157
+ "family": "brother-ql",
158
+ "transports": {
159
+ "usb": {
160
+ "vid": "0x04f9",
161
+ "pid": "0x20c7"
162
+ },
163
+ "bluetooth-spp": {
164
+ "namePrefix": "PT-P910"
165
+ }
166
+ },
167
+ "engines": [
168
+ {
169
+ "role": "primary",
170
+ "protocol": "pt-raster",
171
+ "dpi": 360,
172
+ "headDots": 560,
173
+ "mediaCompatibility": [
174
+ "tze"
175
+ ],
176
+ "capabilities": {
177
+ "autocut": true,
178
+ "mediaDetection": true,
179
+ "highResDpi": 720
180
+ }
181
+ }
182
+ ],
183
+ "hardwareQuirks": "Bluetooth assumed classic SPP per nbuchwitz/ptouch — phase 4 hardware verification should confirm classic vs BLE GATT before users rely on it. HSe heat-shrink unsupported (TZe-only); engine.mediaCompatibility excludes hse-2to1 and hse-3to1 so the lookup never returns those entries.",
184
+ "support": {
185
+ "status": "untested"
186
+ }
187
+ },
188
+ {
189
+ "key": "PT_P950NW",
190
+ "name": "PT-P950NW",
191
+ "family": "brother-ql",
192
+ "transports": {
193
+ "usb": {
194
+ "vid": "0x04f9",
195
+ "pid": "0x2086"
196
+ },
197
+ "tcp": {
198
+ "port": 9100
199
+ }
200
+ },
201
+ "engines": [
202
+ {
203
+ "role": "primary",
204
+ "protocol": "pt-raster",
205
+ "dpi": 360,
206
+ "headDots": 560,
207
+ "mediaCompatibility": [
208
+ "tze",
209
+ "hse-2to1",
210
+ "hse-3to1"
211
+ ],
212
+ "capabilities": {
213
+ "autocut": true,
214
+ "mediaDetection": true,
215
+ "highResDpi": 720
216
+ }
217
+ }
218
+ ],
219
+ "hardwareQuirks": "PID 0x2086 sourced from nbuchwitz/ptouch:PTP950NW. Pin configurations from Brother spec cv_ptp900_eng_raster_102.pdf via nbuchwitz/ptouch.",
220
+ "support": {
221
+ "status": "untested"
222
+ }
223
+ },
224
+ {
225
+ "key": "QL_1050",
226
+ "name": "QL-1050",
227
+ "family": "brother-ql",
228
+ "transports": {
229
+ "usb": {
230
+ "vid": "0x04f9",
231
+ "pid": "0x2027"
232
+ }
233
+ },
234
+ "engines": [
235
+ {
236
+ "role": "primary",
237
+ "protocol": "ql-raster",
238
+ "dpi": 300,
239
+ "headDots": 1296,
240
+ "mediaCompatibility": [
241
+ "dk",
242
+ "dk-wide"
243
+ ],
244
+ "capabilities": {
245
+ "autocut": true,
246
+ "mediaDetection": true
247
+ }
248
+ }
249
+ ],
250
+ "support": {
251
+ "status": "untested"
252
+ }
253
+ },
254
+ {
255
+ "key": "QL_1060N",
256
+ "name": "QL-1060N",
257
+ "family": "brother-ql",
258
+ "transports": {
259
+ "usb": {
260
+ "vid": "0x04f9",
261
+ "pid": "0x2028"
262
+ },
263
+ "tcp": {
264
+ "port": 9100
265
+ }
266
+ },
267
+ "engines": [
268
+ {
269
+ "role": "primary",
270
+ "protocol": "ql-raster",
271
+ "dpi": 300,
272
+ "headDots": 1296,
273
+ "mediaCompatibility": [
274
+ "dk",
275
+ "dk-wide"
276
+ ],
277
+ "capabilities": {
278
+ "autocut": true,
279
+ "mediaDetection": true
280
+ }
281
+ }
282
+ ],
283
+ "support": {
284
+ "status": "untested"
285
+ }
286
+ },
287
+ {
288
+ "key": "QL_1100",
289
+ "name": "QL-1100",
290
+ "family": "brother-ql",
291
+ "transports": {
292
+ "usb": {
293
+ "vid": "0x04f9",
294
+ "pid": "0x20a7"
295
+ }
296
+ },
297
+ "engines": [
298
+ {
299
+ "role": "primary",
300
+ "protocol": "ql-raster",
301
+ "dpi": 300,
302
+ "headDots": 1296,
303
+ "mediaCompatibility": [
304
+ "dk",
305
+ "dk-wide"
306
+ ],
307
+ "capabilities": {
308
+ "autocut": true,
309
+ "mediaDetection": true
310
+ }
311
+ }
312
+ ],
313
+ "capabilities": {
314
+ "editorLite": true,
315
+ "massStoragePid": "0x20a9"
316
+ },
317
+ "support": {
318
+ "status": "untested"
319
+ }
320
+ },
321
+ {
322
+ "key": "QL_1110NWB",
323
+ "name": "QL-1110NWB",
324
+ "family": "brother-ql",
325
+ "transports": {
326
+ "usb": {
327
+ "vid": "0x04f9",
328
+ "pid": "0x20a8"
329
+ },
330
+ "tcp": {
331
+ "port": 9100
332
+ }
333
+ },
334
+ "engines": [
335
+ {
336
+ "role": "primary",
337
+ "protocol": "ql-raster",
338
+ "dpi": 300,
339
+ "headDots": 1296,
340
+ "mediaCompatibility": [
341
+ "dk",
342
+ "dk-wide"
343
+ ],
344
+ "capabilities": {
345
+ "autocut": true,
346
+ "mediaDetection": true
347
+ }
348
+ }
349
+ ],
350
+ "capabilities": {
351
+ "editorLite": true,
352
+ "massStoragePid": "0x20aa"
353
+ },
354
+ "support": {
355
+ "status": "untested"
356
+ }
357
+ },
358
+ {
359
+ "key": "QL_1115NWB",
360
+ "name": "QL-1115NWB",
361
+ "family": "brother-ql",
362
+ "transports": {
363
+ "usb": {
364
+ "vid": "0x04f9",
365
+ "pid": "0x20ab"
366
+ },
367
+ "tcp": {
368
+ "port": 9100
369
+ }
370
+ },
371
+ "engines": [
372
+ {
373
+ "role": "primary",
374
+ "protocol": "ql-raster",
375
+ "dpi": 300,
376
+ "headDots": 1296,
377
+ "mediaCompatibility": [
378
+ "dk",
379
+ "dk-wide"
380
+ ],
381
+ "capabilities": {
382
+ "autocut": true,
383
+ "mediaDetection": true
384
+ }
385
+ }
386
+ ],
387
+ "capabilities": {
388
+ "editorLite": true,
389
+ "massStoragePid": "0x20ac"
390
+ },
391
+ "support": {
392
+ "status": "untested"
393
+ }
394
+ },
395
+ {
396
+ "key": "QL_500",
397
+ "name": "QL-500",
398
+ "family": "brother-ql",
399
+ "transports": {
400
+ "usb": {
401
+ "vid": "0x04f9",
402
+ "pid": "0x2013"
403
+ }
404
+ },
405
+ "engines": [
406
+ {
407
+ "role": "primary",
408
+ "protocol": "ql-raster",
409
+ "dpi": 300,
410
+ "headDots": 720,
411
+ "mediaCompatibility": [
412
+ "dk"
413
+ ],
414
+ "capabilities": {
415
+ "mediaDetection": true
416
+ }
417
+ }
418
+ ],
419
+ "support": {
420
+ "status": "untested"
421
+ }
422
+ },
423
+ {
424
+ "key": "QL_550",
425
+ "name": "QL-550",
426
+ "family": "brother-ql",
427
+ "transports": {
428
+ "usb": {
429
+ "vid": "0x04f9",
430
+ "pid": "0x2016"
431
+ }
432
+ },
433
+ "engines": [
434
+ {
435
+ "role": "primary",
436
+ "protocol": "ql-raster",
437
+ "dpi": 300,
438
+ "headDots": 720,
439
+ "mediaCompatibility": [
440
+ "dk"
441
+ ],
442
+ "capabilities": {
443
+ "mediaDetection": true
444
+ }
445
+ }
446
+ ],
447
+ "support": {
448
+ "status": "untested"
449
+ }
450
+ },
451
+ {
452
+ "key": "QL_560",
453
+ "name": "QL-560",
454
+ "family": "brother-ql",
455
+ "transports": {
456
+ "usb": {
457
+ "vid": "0x04f9",
458
+ "pid": "0x2018"
459
+ }
460
+ },
461
+ "engines": [
462
+ {
463
+ "role": "primary",
464
+ "protocol": "ql-raster",
465
+ "dpi": 300,
466
+ "headDots": 720,
467
+ "mediaCompatibility": [
468
+ "dk"
469
+ ],
470
+ "capabilities": {
471
+ "autocut": true,
472
+ "mediaDetection": true
473
+ }
474
+ }
475
+ ],
476
+ "support": {
477
+ "status": "untested"
478
+ }
479
+ },
480
+ {
481
+ "key": "QL_570",
482
+ "name": "QL-570",
483
+ "family": "brother-ql",
484
+ "transports": {
485
+ "usb": {
486
+ "vid": "0x04f9",
487
+ "pid": "0x2019"
488
+ }
489
+ },
490
+ "engines": [
491
+ {
492
+ "role": "primary",
493
+ "protocol": "ql-raster",
494
+ "dpi": 300,
495
+ "headDots": 720,
496
+ "mediaCompatibility": [
497
+ "dk"
498
+ ],
499
+ "capabilities": {
500
+ "autocut": true,
501
+ "mediaDetection": true
502
+ }
503
+ }
504
+ ],
505
+ "support": {
506
+ "status": "untested"
507
+ }
508
+ },
509
+ {
510
+ "key": "QL_580N",
511
+ "name": "QL-580N",
512
+ "family": "brother-ql",
513
+ "transports": {
514
+ "usb": {
515
+ "vid": "0x04f9",
516
+ "pid": "0x201b"
517
+ },
518
+ "tcp": {
519
+ "port": 9100
520
+ }
521
+ },
522
+ "engines": [
523
+ {
524
+ "role": "primary",
525
+ "protocol": "ql-raster",
526
+ "dpi": 300,
527
+ "headDots": 720,
528
+ "mediaCompatibility": [
529
+ "dk"
530
+ ],
531
+ "capabilities": {
532
+ "autocut": true,
533
+ "mediaDetection": true
534
+ }
535
+ }
536
+ ],
537
+ "support": {
538
+ "status": "untested"
539
+ }
540
+ },
541
+ {
542
+ "key": "QL_600",
543
+ "name": "QL-600",
544
+ "family": "brother-ql",
545
+ "transports": {
546
+ "usb": {
547
+ "vid": "0x04f9",
548
+ "pid": "0x2100"
549
+ }
550
+ },
551
+ "engines": [
552
+ {
553
+ "role": "primary",
554
+ "protocol": "ql-raster",
555
+ "dpi": 300,
556
+ "headDots": 720,
557
+ "mediaCompatibility": [
558
+ "dk"
559
+ ],
560
+ "capabilities": {
561
+ "autocut": true,
562
+ "mediaDetection": true
563
+ }
564
+ }
565
+ ],
566
+ "support": {
567
+ "status": "untested"
568
+ }
569
+ },
570
+ {
571
+ "key": "QL_650TD",
572
+ "name": "QL-650TD",
573
+ "family": "brother-ql",
574
+ "transports": {
575
+ "usb": {
576
+ "vid": "0x04f9",
577
+ "pid": "0x201c"
578
+ }
579
+ },
580
+ "engines": [
581
+ {
582
+ "role": "primary",
583
+ "protocol": "ql-raster",
584
+ "dpi": 300,
585
+ "headDots": 720,
586
+ "mediaCompatibility": [
587
+ "dk"
588
+ ],
589
+ "capabilities": {
590
+ "autocut": true,
591
+ "mediaDetection": true
592
+ }
593
+ }
594
+ ],
595
+ "support": {
596
+ "status": "untested"
597
+ }
598
+ },
599
+ {
600
+ "key": "QL_700",
601
+ "name": "QL-700",
602
+ "family": "brother-ql",
603
+ "transports": {
604
+ "usb": {
605
+ "vid": "0x04f9",
606
+ "pid": "0x2042"
607
+ }
608
+ },
609
+ "engines": [
610
+ {
611
+ "role": "primary",
612
+ "protocol": "ql-raster",
613
+ "dpi": 300,
614
+ "headDots": 720,
615
+ "mediaCompatibility": [
616
+ "dk"
617
+ ],
618
+ "capabilities": {
619
+ "autocut": true,
620
+ "mediaDetection": true
621
+ }
622
+ }
623
+ ],
624
+ "capabilities": {
625
+ "editorLite": true
626
+ },
627
+ "hardwareQuirks": "Cassette-based media detection. Printer hard-rejects with an error code if the host job dimensions do not match the loaded DK cassette. Apps that compare detected vs selected media should switch to the matching cassette before sending.",
628
+ "support": {
629
+ "status": "untested"
630
+ }
631
+ },
632
+ {
633
+ "key": "QL_710W",
634
+ "name": "QL-710W",
635
+ "family": "brother-ql",
636
+ "transports": {
637
+ "usb": {
638
+ "vid": "0x04f9",
639
+ "pid": "0x2044"
640
+ },
641
+ "tcp": {
642
+ "port": 9100
643
+ }
644
+ },
645
+ "engines": [
646
+ {
647
+ "role": "primary",
648
+ "protocol": "ql-raster",
649
+ "dpi": 300,
650
+ "headDots": 720,
651
+ "mediaCompatibility": [
652
+ "dk"
653
+ ],
654
+ "capabilities": {
655
+ "autocut": true,
656
+ "mediaDetection": true
657
+ }
658
+ }
659
+ ],
660
+ "capabilities": {
661
+ "editorLite": true
662
+ },
663
+ "support": {
664
+ "status": "untested"
665
+ }
666
+ },
667
+ {
668
+ "key": "QL_720NW",
669
+ "name": "QL-720NW",
670
+ "family": "brother-ql",
671
+ "transports": {
672
+ "usb": {
673
+ "vid": "0x04f9",
674
+ "pid": "0x2045"
675
+ },
676
+ "tcp": {
677
+ "port": 9100
678
+ }
679
+ },
680
+ "engines": [
681
+ {
682
+ "role": "primary",
683
+ "protocol": "ql-raster",
684
+ "dpi": 300,
685
+ "headDots": 720,
686
+ "mediaCompatibility": [
687
+ "dk"
688
+ ],
689
+ "capabilities": {
690
+ "autocut": true,
691
+ "mediaDetection": true
692
+ }
693
+ }
694
+ ],
695
+ "support": {
696
+ "status": "untested"
697
+ }
698
+ },
699
+ {
700
+ "key": "QL_800",
701
+ "name": "QL-800",
702
+ "family": "brother-ql",
703
+ "transports": {
704
+ "usb": {
705
+ "vid": "0x04f9",
706
+ "pid": "0x209b"
707
+ }
708
+ },
709
+ "engines": [
710
+ {
711
+ "role": "primary",
712
+ "protocol": "ql-raster",
713
+ "dpi": 300,
714
+ "headDots": 720,
715
+ "mediaCompatibility": [
716
+ "dk"
717
+ ],
718
+ "capabilities": {
719
+ "autocut": true,
720
+ "mediaDetection": true,
721
+ "twoColor": true
722
+ }
723
+ }
724
+ ],
725
+ "capabilities": {
726
+ "editorLite": true
727
+ },
728
+ "support": {
729
+ "status": "untested"
730
+ }
731
+ },
732
+ {
733
+ "key": "QL_810W",
734
+ "name": "QL-810W",
735
+ "family": "brother-ql",
736
+ "transports": {
737
+ "usb": {
738
+ "vid": "0x04f9",
739
+ "pid": "0x209c"
740
+ },
741
+ "tcp": {
742
+ "port": 9100
743
+ }
744
+ },
745
+ "engines": [
746
+ {
747
+ "role": "primary",
748
+ "protocol": "ql-raster",
749
+ "dpi": 300,
750
+ "headDots": 720,
751
+ "mediaCompatibility": [
752
+ "dk"
753
+ ],
754
+ "capabilities": {
755
+ "autocut": true,
756
+ "mediaDetection": true,
757
+ "twoColor": true
758
+ }
759
+ }
760
+ ],
761
+ "capabilities": {
762
+ "editorLite": true
763
+ },
764
+ "support": {
765
+ "status": "untested"
766
+ }
767
+ },
768
+ {
769
+ "key": "QL_820NWBc",
770
+ "name": "QL-820NWBc",
771
+ "family": "brother-ql",
772
+ "transports": {
773
+ "usb": {
774
+ "vid": "0x04f9",
775
+ "pid": "0x209d"
776
+ },
777
+ "tcp": {
778
+ "port": 9100
779
+ },
780
+ "bluetooth-spp": {
781
+ "namePrefix": "QL-820NWB"
782
+ }
783
+ },
784
+ "engines": [
785
+ {
786
+ "role": "primary",
787
+ "protocol": "ql-raster",
788
+ "dpi": 300,
789
+ "headDots": 720,
790
+ "mediaCompatibility": [
791
+ "dk"
792
+ ],
793
+ "capabilities": {
794
+ "autocut": true,
795
+ "mediaDetection": true,
796
+ "twoColor": true
797
+ }
798
+ }
799
+ ],
800
+ "capabilities": {
801
+ "editorLite": true
802
+ },
803
+ "hardwareQuirks": "QL-820NWB and QL-820NWBc share PID 0x209d; this entry covers both marketing names. Bluetooth on this device is classic SPP (paired at the OS level via RFCOMM), not GATT — the runtime serial implementation opens the OS-paired RFCOMM device path. macOS dropped classic Bluetooth SPP support so that route is unavailable there; Linux and Windows both work.",
804
+ "support": {
805
+ "status": "verified",
806
+ "transports": {
807
+ "usb": "verified",
808
+ "tcp": "verified",
809
+ "bluetooth-spp": "untested"
810
+ },
811
+ "lastVerified": "2026-04-27",
812
+ "packageVersion": "0.2.1",
813
+ "quirks": "Reference test device. Two-colour printing on DK-22251 confirmed, auto-cut works on USB and TCP, WebUSB demo prints correctly. The QL-820NWB and QL-820NWBc share PID 0x209d; this entry covers both marketing names.",
814
+ "reports": [
815
+ {
816
+ "issue": 0,
817
+ "reporter": "@mannes",
818
+ "date": "2026-04-27",
819
+ "result": "verified",
820
+ "os": "Linux",
821
+ "selfVerified": true,
822
+ "notes": "Bench verification by the maintainer at the time of seeding this entry. Issue #0 is a placeholder — replace with the real issue number when a public verification report is filed."
823
+ }
824
+ ]
825
+ }
826
+ }
827
+ ]
828
+ } as const satisfies DeviceRegistry;
829
+
830
+ type DeviceKey = (typeof DEVICE_REGISTRY)['devices'][number]['key'];
831
+
832
+ /**
833
+ * Per-key map of device entries. Built from the registry array via
834
+ * `Object.fromEntries`, but typed as `Record<DeviceKey, BrotherQLDevice>`
835
+ * so consumers can write `DEVICES.QL_820NWBc` and get a precise type
836
+ * back without literal narrowing leaking into engine capability access.
837
+ */
838
+ export const DEVICES = Object.fromEntries(
839
+ DEVICE_REGISTRY.devices.map(d => [d.key, d]),
840
+ ) as unknown as Record<DeviceKey, BrotherQLDevice>;