@bobfrankston/brother-label 1.1.18 → 1.1.19
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/cli.js +33 -6
- package/cli.js.map +1 -1
- package/cli.ts +38 -6
- package/package.json +1 -1
- package/ptdirect.d.ts +10 -0
- package/ptdirect.d.ts.map +1 -1
- package/ptdirect.js +20 -4
- package/ptdirect.js.map +1 -1
- package/ptdirect.ts +30 -4
package/cli.js
CHANGED
|
@@ -610,7 +610,7 @@ async function cmdPrint(opts, segments, inputs) {
|
|
|
610
610
|
outln(`Printed ${segments.length} segment(s) on ${opts.printer || "QL"} @ ${qlHost}, ${tape}mm (direct TCP/9100)`);
|
|
611
611
|
}
|
|
612
612
|
else if (segDirectPort = await tryPrintDirect(await renderSegments(segments, tape, opts.height, opts.space), opts, tape)) {
|
|
613
|
-
reportPrinted(`${segments.length} segment(s) `, opts, tape, segDirectPort);
|
|
613
|
+
reportPrinted(`${segments.length} segment(s) `, opts, tape, segDirectPort, lastDirectConfirmed);
|
|
614
614
|
}
|
|
615
615
|
else {
|
|
616
616
|
await printSegments(segments, segOpts);
|
|
@@ -653,8 +653,9 @@ async function cmdPrint(opts, segments, inputs) {
|
|
|
653
653
|
return;
|
|
654
654
|
}
|
|
655
655
|
const directPort = await tryPrintDirect(await render(printOpts), opts, tape);
|
|
656
|
+
const directConfirmed = lastDirectConfirmed;
|
|
656
657
|
if (directPort) {
|
|
657
|
-
reportPrinted("", opts, tape, directPort);
|
|
658
|
+
reportPrinted("", opts, tape, directPort, directConfirmed);
|
|
658
659
|
return;
|
|
659
660
|
}
|
|
660
661
|
await print(printOpts);
|
|
@@ -682,9 +683,25 @@ function describeTarget(opts, tape, port) {
|
|
|
682
683
|
* another dead job and never reach the tape, which is exactly how labels have
|
|
683
684
|
* been going missing. Say "Queued" for that, and name the printer either way.
|
|
684
685
|
*/
|
|
685
|
-
function reportPrinted(what, opts, tape, port) {
|
|
686
|
-
|
|
687
|
-
|
|
686
|
+
function reportPrinted(what, opts, tape, port, confirmed) {
|
|
687
|
+
let verb;
|
|
688
|
+
let tail;
|
|
689
|
+
if (!port) {
|
|
690
|
+
verb = "Queued";
|
|
691
|
+
tail = " -- the spooler has it; check the queue if nothing comes out";
|
|
692
|
+
}
|
|
693
|
+
else if (confirmed) {
|
|
694
|
+
verb = "Printed";
|
|
695
|
+
tail = "";
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
/* The bytes were delivered, and that is all we know. This printer does
|
|
699
|
+
* not report back, so it can still have refused the job -- wrong tape,
|
|
700
|
+
* cover open, cutter jam -- with nothing to tell us. Claiming "Printed"
|
|
701
|
+
* here is what made earlier failures look like successes. */
|
|
702
|
+
verb = "Sent";
|
|
703
|
+
tail = " -- this printer does not confirm, so check the tape";
|
|
704
|
+
}
|
|
688
705
|
outln(`${verb} ${what}on ${describeTarget(opts, tape, port)}${tail}`);
|
|
689
706
|
}
|
|
690
707
|
/**
|
|
@@ -713,11 +730,13 @@ async function tryPrintDirect(png, opts, tape) {
|
|
|
713
730
|
if (!printerName)
|
|
714
731
|
return null;
|
|
715
732
|
const { resolvePtModel, printPtDirect } = await import("./ptdirect.js");
|
|
716
|
-
|
|
733
|
+
const model = resolvePtModel(printerName);
|
|
734
|
+
if (!model)
|
|
717
735
|
return null;
|
|
718
736
|
const port = opts.port || (await resolvePtPort(printerName))?.port;
|
|
719
737
|
if (!port)
|
|
720
738
|
return null;
|
|
739
|
+
lastDirectConfirmed = model.confirmsPrint;
|
|
721
740
|
const jobsBefore = await listJobIds(printerName);
|
|
722
741
|
try {
|
|
723
742
|
await printPtDirect(png, {
|
|
@@ -736,6 +755,14 @@ async function tryPrintDirect(png, opts, tape) {
|
|
|
736
755
|
}
|
|
737
756
|
return port;
|
|
738
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* Whether the last direct print was confirmed by the printer. Set by
|
|
760
|
+
* tryPrintDirect; read by the reporting call right after it. A module-level
|
|
761
|
+
* value rather than a return field because the port string is already the
|
|
762
|
+
* caller's success signal and threading a second one through both dispatch
|
|
763
|
+
* sites obscures them.
|
|
764
|
+
*/
|
|
765
|
+
let lastDirectConfirmed = false;
|
|
739
766
|
/** Ports that swallow whatever the spooler sends them. */
|
|
740
767
|
const DISCARD_PORTS = /^(nul|null|nul:|null:)$/i;
|
|
741
768
|
/**
|
package/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,EACH,sBAAsB,EACtB,SAAS,GACZ,MAAM,0BAA0B,CAAC;AAQlC,OAAO,EACH,KAAK,EACL,MAAM,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,aAAa,EACb,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,cAAc,GACjB,MAAM,UAAU,CAAC;AAGlB,MAAM,OAAO,GAAG,OAAO,CAAC;AACxB,MAAM,WAAW,GAAe,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAEnD,SAAS,SAAS,CAAC,KAAa;IAC5B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAa,CAAC;IAC9D,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,SAAS,CAAC,sBAAsB,KAAK,YAAY,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,MAAM,GAAuB;IAC/B,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAiB,GAAG,EAAE,MAAM,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,EAAQ,GAAG,EAAE,SAAS,EAAE;IACnD,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,EAAS,GAAG,EAAE,QAAQ,EAAE;IAClD,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,EAAS,GAAG,EAAE,QAAQ,EAAE;IAClD,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,EAAQ,GAAG,EAAE,QAAQ,EAAE;IAClD,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAU,GAAG,EAAE,OAAO,EAAE;IACjD,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC,EAAc,GAAG,EAAE,SAAS,EAAE;IACnD,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAa,GAAG,EAAE,UAAU,EAAE;IACpD,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAmB,GAAG,EAAE,IAAI,EAAE;IAC9C,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAa,GAAG,EAAE,SAAS,EAAE;IACnD,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAiB,GAAG,EAAE,MAAM,EAAE;CACnD,CAAC;AAEF,MAAM,OAAO,GAAsB;IAC/B,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAW,GAAG,EAAE,MAAM,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAU,GAAG,EAAE,OAAO,EAAE;IACjD,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAoB,GAAG,EAAE,MAAM,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAW,GAAG,EAAE,MAAM,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,EAAG,GAAG,EAAE,QAAQ,EAAE;IAClD,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAiB,GAAG,EAAE,MAAM,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,EAAO,GAAG,EAAE,SAAS,EAAE;IACnD,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAgB,GAAG,EAAE,OAAO,EAAE;IACjD,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAK,GAAG,EAAE,OAAO,EAAE;IACjD,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAK,GAAG,EAAE,MAAM,EAAE;IAChD,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAQ,GAAG,EAAE,SAAS,EAAE;CACtD,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAEjD,SAAS,QAAQ;IACb,OAAO,CAAC,GAAG,CAAC,8BAA8B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA6CjB,CAAC,CAAC;AACtC,CAAC;AAED,0EAA0E;AAC1E,MAAM,SAAU,SAAQ,KAAK;IAChB,WAAW,GAAG,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,UAAU,GAAG;IACf,MAAM,EAAE,KAAK;IACb,MAAM,CAAC,GAAW;QACd,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACvB,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;IACD,KAAK;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACxB,CAAC;IACL,CAAC;CACJ,CAAC;AAEF,8DAA8D;AAC9D,SAAS,KAAK,CAAC,GAAW;IACtB,UAAU,CAAC,KAAK,EAAE,CAAC;IACnB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,IAAI;IACf,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACD,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,sBAAsB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED;;uDAEmD;IACnD,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,gCAAgC,CAAC,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;QACrE,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,iCAAiC,CAAC,GAAG,CAAC,CAAC;QAC/E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAC,QAAQ,EAAE,CAAC;QAAC,OAAO;IAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAAC,OAAO;IAAC,CAAC;IAE1D,IAAI,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChE;;mFAEuE;YACvE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACtD,IAAI,IAAI;oBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;YACpC,CAAC;QACL,CAAC;QACD,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,KAAK,MAAM;gBAAI,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO;YAClD,KAAK,QAAQ;gBAAE,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO;YACpD,KAAK,QAAQ;gBAAE,MAAM,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO;QACxD,CAAC;QACD,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACd,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB;mCAC2B;QAC3B,IAAI,CAAC,EAAE,WAAW,IAAI,8CAA8C,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;YAC1F,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAAC,WAAmB;IAC5C,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GACtD,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,EAAE,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG;;8BAEW,IAAI;;CAEjC,CAAC;IACE,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,OAAO,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,SAAS,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,cAAc,CAAC,IAAY;IACtC,IAAI,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,CAAC;QACD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,MAAM,cAAc,EAAE,CAAC;QACxC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAChE,IAAI,YAAY,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACjE,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC;YACtC,IAAI,CAAC;gBACD,MAAM,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC5B,OAAO,SAAS,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACd,CAAC;IACL,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC;IAC7D,IAAI,CAAC;QACD,MAAM,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3B,OAAO,QAAQ,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,IAAI,CAAC;QACD,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,GAAG,QAAQ,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;QACjF,CAAC,CAAC,CAAC;QACH,IAAI,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,IAAI,cAAoC,CAAC;AACzC;;;;;GAKG;AACH,KAAK,UAAU,cAAc;IACzB,IAAI,cAAc;QAAE,OAAO,cAAc,CAAC;IAC1C,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG;;;;;;;CAOlB,CAAC;IACE,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC3C,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACL,cAAc,GAAG,EAAE,CAAC;IACxB,CAAC;IACD,OAAO,cAAc,CAAC;AAC1B,CAAC;AAGD;;;GAGG;AACH;;;;;GAKG;AACH,SAAS,WAAW,CAAC,CAAM;IACvB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACvD,KAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrD,KAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC5C,KAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,EAAE,CAAC;QACT,MAAM,MAAM,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC;QACnD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;QAC9C,IAAI,GAAG,CAAC,IAAI;YAAG,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,GAAG,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACnD,IAAI,GAAG,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC;QAChB,KAAK,EAAE,CAAC;QACR,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;IACpF,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,uBAAuB,IAAI,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;IAChG,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAAC,SAAS,GAAG,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;IAEpF,IAAI,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,UAAU,QAAQ,EAAE,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACJ,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACnE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;AACL,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,IAAsC;IACzD,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC;;+BAE2B;IAC3B,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAElD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAClD,CAAC;SAAM,CAAC;QACJ,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,SAAS,GAAG,GAAG,CAAC;YACpB,IAAI,YAAY,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC;gBACD,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjD,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;gBAC9F,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;YAClC,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,SAAS,GAAG,iBAAiB,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7C,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;YAC7D,MAAM,MAAM,GAAG,IAAI,EAAE,UAAU,IAAI,WAAW,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,IAAI,IAAI,EAAE,OAAO;gBAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YACxE,IAAI,IAAI,EAAE,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;YAC5C,IAAI,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAC3C,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;YAC9C,IAAI,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC/C,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,wBAAwB,SAAS,EAAE,CAAC,CAAC;YACjD,IAAI,YAAY;gBAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,YAAY,EAAE,CAAC,CAAC;QAC1E,CAAC;IACL,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,MAAM,EAAE,mBAAmB,EAAE,SAAS,EAAE,YAAY,EAAE,oBAAoB,EAAE,GACxE,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;YACvD,OAAO;QACX,CAAC;QACD,MAAM,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/F,MAAM,OAAO,GAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAE/F,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,aAAa,WAAW,CAAC,MAAM,iBAAiB,CAAC,CAAC;QACjH,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC;YAC3C,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,cAAc,CAAC;YAC3D,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,YAAY,GAAG,GAAG,EAAE,CAAC,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;YACrE,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClE,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;IACL,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,QAAgB;IACjC,IAAI,CAAC,QAAQ;QAAE,OAAO,WAAW,CAAC;IAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACpD,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;IAChG,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,cAAc,CAAC,WAAmB,EAAE,MAA0B;IACzE,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IAEnD,OAAO,CAAC,GAAG,CAAC,gBAAgB,WAAW,EAAE,CAAC,CAAC;IAC3C,IAAI,IAAI,EAAE,QAAQ,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,IAAI,EAAE,UAAU,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,CAAC;QACD,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;QACpG,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,WAAW,SAAS,CAAC,CAAC;QACpD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7G,CAAC;QACD,IAAI,CAAC,CAAC,YAAY;YAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QAChD,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,IAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,8CAA8C,EAAE,CAAC,CAAC;QAC7H,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,sBAAsB,4BAA4B,CAAC,CAAC;QAC/I,CAAC;IACL,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,aAAa,CAAC,CAAC;QACjD,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,MAAM,wBAAwB,CAAC,WAAW,CAAC,CAAC;QAC1D,IAAI,KAAK,EAAE,CAAC;YACR,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC;YAC1I,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,CAAC,aAAa,QAAQ,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,oBAAoB,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxJ,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;QACzF,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sCAAsC,CAAC;QAC1G,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC/F,CAAC;AACL,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAsC;IAC3D,MAAM,IAAI,GAAI,IAAI,CAAC,OAAkB,IAAI,SAAS,CAAC;IACnD,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,CAAC,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,WAAW,SAAS,CAAC,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3G,CAAC;IACD,IAAI,CAAC,CAAC,YAAY;QAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC,CAAC,WAAW;QAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;AAC9D,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAsC;IAC3D,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,sBAAsB,aAAa,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5F,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,CAAC,cAAc,IAAI,WAAW,EAAE,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;QACpD,OAAO;IACX,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC;QACzC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,SAAS,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,OAAiB,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;AACL,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAsC;IAC7D;qEACiE;IACjE,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,OAA6B,CAAC,CAAC;IACtE,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,IAAI,CAAC;IACnC,OAAO,SAAS,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,QAAQ,CACnB,IAAsC,EACtC,QAAmB,EACnB,MAAgB;IAEhB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IAErC,mEAAmE;IACnE,0EAA0E;IAC1E,2BAA2B;IAC3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACnE,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,QAAQ,GAAG;QACb,OAAO,EAAE,IAAI,CAAC,OAA6B;QAC3C,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM;QAClB,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,OAAiB,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;QAC/F,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAkB,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;QAClG,SAAS,EAAE,gBAAgB,EAAE;QAC7B,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;KACnC,CAAC;IAEF,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE5F,kFAAkF;IAClF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,OAAO,GAAmB;YAC5B,GAAG,QAAQ;YACX,IAAI;YACJ,UAAU,EAAE,IAAI,CAAC,MAA4B;YAC7C,KAAK,EAAE,IAAI,CAAC,KAA2B;SAC1C,CAAC;QACF,IAAI,aAAqB,CAAC;QAC1B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAA4B,EAAE,IAAI,CAAC,KAA2B,CAAC,CAAC;YACzH,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,MAAgB,EAAE,MAAM,CAAC,CAAC;YAChD,KAAK,CAAC,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACrC,CAAC;aAAM,IAAI,MAAM,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAA4B,EAAE,IAAI,CAAC,KAA2B,CAAC,CAAC;YACzH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,OAA6B,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClG,KAAK,CAAC,WAAW,QAAQ,CAAC,MAAM,kBAAmB,IAAI,CAAC,OAAkB,IAAI,IAAI,MAAM,MAAM,KAAK,IAAI,sBAAsB,CAAC,CAAC;QACnI,CAAC;aAAM,IAAI,aAAa,GAAG,MAAM,cAAc,CAC3C,MAAM,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAA4B,EAAE,IAAI,CAAC,KAA2B,CAAC,EACzG,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACd,aAAa,CAAC,GAAG,QAAQ,CAAC,MAAM,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACJ,MAAM,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACvC,aAAa,CAAC,GAAG,QAAQ,CAAC,MAAM,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACtE,CAAC;QACD,OAAO;IACX,CAAC;IAED,oBAAoB;IACpB,MAAM,SAAS,GAAiB;QAC5B,GAAG,QAAQ;QACX,IAAI;QACJ,MAAM,EAAE,IAAI,CAAC,MAA4B;QACzC,UAAU,EAAE,IAAI,CAAC,MAA4B;KAChD,CAAC;IAEF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC;IAC1B,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7D,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,MAAM,cAAc,CAAC,IAAI,CAAC,OAAiB,EAAE,MAAM,CAAC,CAAC;YACrD,OAAO;QACX,CAAC;QACD,QAAQ,EAAE,CAAC;QACX,OAAO;IACX,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QACvC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,MAAgB,EAAE,MAAM,CAAC,CAAC;QAChD,KAAK,CAAC,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACjC,OAAO;IACX,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACT,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,OAA6B,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClG,KAAK,CAAC,cAAe,IAAI,CAAC,OAAkB,IAAI,IAAI,MAAM,MAAM,oBAAoB,CAAC,CAAC;QACtF,OAAO;IACX,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7E,IAAI,UAAU,EAAE,CAAC;QACb,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAC1C,OAAO;IACX,CAAC;IACD,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACvB,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,cAAc,CAAC,IAAsC,EAAE,IAAc,EAAE,IAAY;IACxF,MAAM,OAAO,GAAI,IAAI,CAAC,OAAkB,IAAI,iBAAiB,EAAE,CAAC;IAChE,OAAO,IAAI;QACP,CAAC,CAAC,GAAG,OAAO,KAAK,IAAI,sBAAsB,IAAI,GAAG;QAClD,CAAC,CAAC,GAAG,OAAO,KAAK,IAAI,SAAS,CAAC;AACvC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,IAAsC,EAAE,IAAc,EAAE,IAAY;IACrG,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,8DAA8D,CAAC;IACxF,KAAK,CAAC,GAAG,IAAI,IAAI,IAAI,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,cAAc,CACzB,GAAW,EACX,IAAsC,EACtC,IAAc;IAEd,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACb;;;0DAGkD;QAClD,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAiB,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD;qDACiD;IACjD,MAAM,WAAW,GAAI,IAAI,CAAC,OAAkB,IAAI,iBAAiB,EAAE,CAAC;IACpE,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAE9B,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IACxE,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,MAAM,IAAI,GAAI,IAAI,CAAC,IAAe,IAAI,CAAC,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC;IAC/E,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;IACjD,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,GAAG,EAAE;YACrB,IAAI;YACJ,WAAW;YACX,IAAI;YACJ,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK;YACpB,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;SAC1D,CAAC,CAAC;IACP,CAAC;YAAS,CAAC;QACP;;mCAE2B;QAC3B,MAAM,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,0DAA0D;AAC1D,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD;;;GAGG;AACH,KAAK,UAAU,UAAU,CAAC,WAAmB;IACzC,IAAI,CAAC;QACD,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;QACtE,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAC9B,gCAAgC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,+CAA+C,CACjH,CAAC;QACF,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,KAAK,UAAU,gBAAgB,CAAC,WAAmB,EAAE,MAAmB;IACpE,IAAI,CAAC;QACD,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;QAC3C;;;mCAG2B;QAC3B,MAAM,gBAAgB,CAAC;;cAEjB,IAAI;;6BAEW,IAAI;qDACoB,IAAI;;CAExD,CAAC,CAAC;IACC,CAAC;IAAC,MAAM,CAAC,CAAC,kDAAkD,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,kBAAkB,CAAC,WAAmB;IACjD,MAAM,IAAI,GAAG,WAAW,IAAI,iBAAiB,EAAE,CAAC;IAChD,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,CAAC,MAAM,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO;IAC1C,MAAM,IAAI,SAAS,CACf,GAAG,IAAI,+BAA+B,IAAI,CAAC,QAAQ,4CAA4C;QAC/F,gFAAgF;QAChF,uEAAuE,IAAI,qBAAqB,CACnG,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,KAAK,UAAU,aAAa,CAAC,WAAmB;IAC5C,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GACtD,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;IAE7C;;8EAE0E;IAC1E,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,IAAI,KAAK,EAAE,CAAC;QACR,MAAM,MAAM,GAAG;YACX,6CAA6C;YAC7C,kHAAkH;YAClH,yDAAyD;YACzD,uCAAuC;YACvC,4HAA4H;YAC5H,mEAAmE;YACnE,gCAAgC;YAChC,iFAAiF;YACjF,yCAAyC;YACzC,WAAW;YACX,OAAO;YACP,GAAG;SACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,CAAC,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACpD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC7B,IAAI,EAAE,GAAG,CAAC;oBAAE,SAAS;gBACrB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;gBACjE,IAAI,GAAG,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;oBAChD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,oBAAoB,IAAI,EAAE,EAAE,CAAC;gBAC7D,CAAC;YACL,CAAC;QACL,CAAC;QAAC,MAAM,CAAC,CAAC,sCAAsC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IACzF,MAAM,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;IACrD,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,sBAAsB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC5E,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,KAAa,EAAE,IAAsC;IACxE,IAAI,IAAI,CAAC,IAAI;QAAG,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI;QAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACzD,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IAE1D,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IAChG,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,IAAI,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAW,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACjG,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChF,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,gBAAgB;IACrB,IAAI,UAAU,GAAG,CAAC,QAAQ,CAAC;IAC3B,OAAO,CAAC,MAAqB,EAAE,SAAiB,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QAC1C,uEAAuE;QACvE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,IAAI,GAAG,UAAU,GAAG,EAAE;gBAAE,OAAO;YACnC,UAAU,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,UAAU,CAAC,MAAM,CAAC,+BAA+B,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;IAC/I,CAAC,CAAC;AACN,CAAC;AAED,IAAI,EAAE,CAAC","sourcesContent":["#!/usr/bin/env node\n/**\n * Brother Label Printer CLI\n * Thin wrapper around api.ts using shared CLI primitives from label-core.\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport * as dns from \"dns/promises\";\nimport {\n preprocessSingleQuotes,\n parseArgs,\n} from \"@bobfrankston/label-core\";\nimport type {\n PrintOptions as CorePrintOptions,\n PrinterStatus,\n Segment,\n ValuedOptionSpec,\n BooleanFlagSpec,\n} from \"@bobfrankston/label-core\";\nimport {\n print,\n render,\n renderSegments,\n printSegments,\n getConfig,\n setConfig,\n getConfigPath,\n listPrinters,\n resolvePrinter,\n getDefaultPrinter,\n detectTape,\n brotherPrinter,\n} from \"./api.js\";\nimport type { TapeSize, PrintOptions, SegmentOptions } from \"./api.js\";\n\nconst VERSION = \"1.1.0\";\nconst VALID_TAPES: TapeSize[] = [6, 9, 12, 18, 24];\n\nfunction parseTape(value: string): TapeSize {\n const num = parseInt(value.replace(\"mm\", \"\"), 10) as TapeSize;\n if (!VALID_TAPES.includes(num)) {\n throw new UserError(`Invalid tape size: ${value}. Valid: ${VALID_TAPES.join(\", \")}`);\n }\n return num;\n}\n\nconst VALUED: ValuedOptionSpec[] = [\n { names: [\"-tape\"], key: \"tape\" },\n { names: [\"-p\", \"-printer\"], key: \"printer\" },\n { names: [\"-o\", \"-output\"], key: \"output\" },\n { names: [\"-a\", \"-aspect\"], key: \"aspect\" },\n { names: [\"-ht\", \"-height\"], key: \"height\" },\n { names: [\"-s\", \"-space\"], key: \"space\" },\n { names: [\"-timeout\"], key: \"timeout\" },\n { names: [\"-interval\"], key: \"interval\" },\n { names: [\"-ql\"], key: \"ql\" },\n { names: [\"-ql-media\"], key: \"qlMedia\" },\n { names: [\"-port\"], key: \"port\" },\n];\n\nconst BOOLEAN: BooleanFlagSpec[] = [\n { names: [\"-w\", \"-html\"], key: \"html\" },\n { names: [\"-i\", \"-image\"], key: \"image\" },\n { names: [\"-t\"], key: \"text\" },\n { names: [\"-c\", \"-clip\"], key: \"clip\" },\n { names: [\"-no-wait\", \"-nowait\"], key: \"nowait\" },\n { names: [\"-mdns\"], key: \"mdns\" },\n { names: [\"-vb\", \"-verbose\"], key: \"verbose\" },\n { names: [\"-queue\"], key: \"queue\" },\n { names: [\"-no-cut\", \"-nocut\"], key: \"nocut\" },\n { names: [\"-help\", \"-h\", \"-?\"], key: \"help\" },\n { names: [\"-version\", \"-v\"], key: \"version\" },\n];\n\nconst SUBCOMMANDS = [\"list\", \"status\", \"config\"];\n\nfunction showHelp(): void {\n console.log(`Brother Label Printer CLI v${VERSION}\n\nUsage:\n brother-print [options] <text> Print text\n brother-print [options] <file> Print html/txt/image file (auto-detected)\n brother-print -clip [options] Print clipboard contents (image or text)\n brother-print -text <v> -qr <v> ... Print ordered text/qr segments\n brother-print -list [-mdns] List Brother printers and status\n (-mdns also discovers network printers)\n brother-print -status [-p <name>] Show printer status\n brother-print -config Show / set defaults\n\nCommands take a leading dash so they can never be confused with label content:\na bare word is always something to print, a dashed word is always a command.\n\nTape size is auto-detected from the printer. Use -tape to override.\nSingle quotes can wrap arguments: '7\"' 'line1\\\\nline2'\n\nOptions:\n -tape <size> Tape size: 6, 9, 12, 18, 24 (mm) [auto-detected]\n -p, -printer <name> Printer queue name\n -o, -output <file> Save PNG to file instead of printing\n -a, -aspect <r> Aspect ratio width:height for HTML (e.g. 3.5:2)\n -ht, -height <size> Text height: 12mm, .5in, or 50% (of tape height)\n -s, -space <size> Space between segments: 12px, 1mm, .2in\n -t, -text Force input as literal text (-text <v> for segments)\n -qr <data> QR code segment\n -w, -html Force input as HTML file path\n -i, -image Force input as image file path\n -c, -clip Read content from clipboard (image preferred, then text)\n -no-wait Fail immediately if printer is offline (default: wait)\n -timeout <secs> Max wait time when printer is offline\n -interval <secs> Polling interval while waiting (default 2)\n -mdns With -list: also discover network printers via mDNS\n -ql <host> Print to a Brother QL series printer over TCP/9100\n (bypasses Windows queue; e.g. -ql ql720 or -ql 1.2.3.4)\n -ql-media <id> QL media id (e.g. 271=DK-11201 29×90mm, 259=DK-22205 62mm)\n Required when the printer doesn't respond to status\n -queue Print through the Windows spooler (XPS) instead of\n writing raster bytes straight to the printer's port\n -port <COMn> Serial/Bluetooth port for direct printing (overrides\n the port looked up from the printer queue)\n -no-cut Do not cut the tape after the label (direct path only)\n -vb, -verbose Print extra QL diagnostic messages (status, media source)\n -help Show this help\n -version Show version`);\n}\n\n/** Expected user-facing error: prints one line, no log file, no stack. */\nclass UserError extends Error {\n readonly isUserError = true;\n}\n\n/**\n * Single-line status display: on a TTY the line is rewritten in place\n * (CR + erase-line) instead of scrolling; callers must clear() before any\n * normal output so it doesn't collide with the pending status line.\n */\nconst statusLine = {\n active: false,\n update(msg: string): void {\n if (process.stderr.isTTY) {\n process.stderr.write(`\\r\\x1b[2K${msg}`);\n this.active = true;\n } else {\n console.error(msg);\n }\n },\n clear(): void {\n if (this.active) {\n process.stderr.write(`\\r\\x1b[2K`);\n this.active = false;\n }\n },\n};\n\n/** console.log that first clears any in-place status line. */\nfunction outln(msg: string): void {\n statusLine.clear();\n console.log(msg);\n}\n\nasync function main(): Promise<void> {\n const argv = preprocessSingleQuotes(process.argv.slice(2));\n let parsed;\n try {\n parsed = parseArgs(argv, VALUED, BOOLEAN, SUBCOMMANDS);\n } catch (e: any) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n\n if (parsed.unknown.length > 0) {\n console.error(`Unknown option(s): ${parsed.unknown.join(\", \")}`);\n console.error(`Run \"brother-print -help\" for usage.`);\n process.exit(1);\n }\n\n /* Bare `list` used to be the command syntax; it is now `-list`. Refusing\n * beats guessing: printing a label that says \"list\" and listing printers\n * are both plausible readings of the same word. */\n if (parsed.ambiguous.length > 0) {\n const w = parsed.ambiguous[0];\n console.error(`Ambiguous argument: \"${w}\" -- commands now take a dash.`);\n console.error(` brother-print -${w} run the ${w} command`);\n console.error(` brother-print -text ${w} print a label that says \"${w}\"`);\n process.exit(1);\n }\n\n if (parsed.opts.help) { showHelp(); return; }\n if (parsed.opts.version) { console.log(VERSION); return; }\n\n try {\n if (typeof parsed.opts.printer === \"string\") {\n parsed.opts.printer = await resolvePrinter(parsed.opts.printer);\n /* Auto-route QL series through the QL TCP path. The XPS / CustomMediaSize\n * pipeline is P-touch-only; sending it to a QL queue produces an XPS\n * error. If the user already passed -ql explicitly, leave it alone. */\n if (!parsed.opts.ql && isQLPrinter(parsed.opts.printer)) {\n const host = await resolveQLHost(parsed.opts.printer);\n if (host) parsed.opts.ql = host;\n }\n }\n switch (parsed.command) {\n case \"list\": await cmdList(parsed.opts); return;\n case \"status\": await cmdStatus(parsed.opts); return;\n case \"config\": await cmdConfig(parsed.opts); return;\n }\n await cmdPrint(parsed.opts, parsed.segments, parsed.inputs);\n } catch (e: any) {\n statusLine.clear();\n /* Timeout/offline are expected conditions, not crashes — one line,\n * no error-report file. */\n if (e?.isUserError || /^Timed out waiting for printer|is offline \\(/.test(e?.message ?? \"\")) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n reportError(e);\n process.exit(1);\n }\n}\n\nfunction isQLPrinter(name: string): boolean {\n return /\\bQL-/i.test(name);\n}\n\n/**\n * Look up the TCP host for a QL printer's installed Windows queue. Pulls the\n * port name via label-core, then asks PowerShell for that port's\n * PrinterHostAddress. Returns undefined if the port isn't a TCP/IP port (e.g.\n * the QL-820NWB's BRWxxxx Bluetooth port has no host address).\n */\nasync function resolveQLHost(printerName: string): Promise<string | undefined> {\n const { listPrinters: coreListPrinters, runScriptOrThrow } =\n await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n if (!info?.portName) return undefined;\n const port = info.portName.replace(/'/g, \"''\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$p = Get-PrinterPort -Name '${port}'\nif ($p -and $p.PrinterHostAddress) { Write-Output $p.PrinterHostAddress }\n`;\n try {\n const out = (await runScriptOrThrow(script)).trim();\n if (!out) return undefined;\n return await resolvableHost(out);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Brother's installer often writes a bare NetBIOS-style hostname (BRWxxxx)\n * as the port's PrinterHostAddress, which won't resolve in environments where\n * the DNS suffix isn't auto-appended. Resolution tiers, in order:\n * 1. as-is via the OS resolver\n * 2. <host>.<suffix> for every DNS suffix the OS knows about (global search\n * list, per-connection suffix, computer domain) — covers the case where\n * the user pings as FQDN manually because Windows isn't auto-appending\n * 3. <host>.local via the OS resolver (Windows 10 1803+ native mDNS)\n * 4. multicast mDNS query directly (bypasses OS resolver — works even when\n * Windows .local resolution is disabled, common in AD/domain environments)\n */\nasync function resolvableHost(host: string): Promise<string> {\n if (/^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host)) return host;\n try {\n await dns.lookup(host);\n return host;\n } catch {}\n if (!host.includes(\".\")) {\n const suffixes = await getDnsSuffixes();\n const { default_fqdn } = await import(\"@bobfrankston/miscinfo\");\n if (default_fqdn && !suffixes.includes(default_fqdn.toLowerCase())) {\n suffixes.push(default_fqdn.toLowerCase());\n }\n for (const suffix of suffixes) {\n const candidate = `${host}.${suffix}`;\n try {\n await dns.lookup(candidate);\n return candidate;\n } catch {}\n }\n }\n const mdnsName = host.includes(\".\") ? host : host + \".local\";\n try {\n await dns.lookup(mdnsName);\n return mdnsName;\n } catch {}\n try {\n const { discoverPdlPrinters } = await import(\"./mdns.js\");\n const printers = await discoverPdlPrinters(2500);\n const target = host.toLowerCase();\n const hit = printers.find(p => {\n const h = p.hostname.toLowerCase().replace(/\\.$/, \"\");\n return h === target || h === target + \".local\" || h.startsWith(target + \".\");\n });\n if (hit && hit.addresses.length > 0) return hit.addresses[0];\n } catch {}\n return host;\n}\n\nlet dnsSuffixCache: string[] | undefined;\n/**\n * Read every DNS suffix the system knows about: the global SuffixSearchList,\n * each adapter's ConnectionSpecificSuffix, and the computer's AD/workgroup\n * domain. Cached per-process. Returns lowercased, de-duplicated, in priority\n * order (search list first since that's what the user explicitly configured).\n */\nasync function getDnsSuffixes(): Promise<string[]> {\n if (dnsSuffixCache) return dnsSuffixCache;\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$out = @()\n$out += (Get-DnsClientGlobalSetting).SuffixSearchList\n$out += Get-DnsClient | Select-Object -ExpandProperty ConnectionSpecificSuffix\n$out += (Get-CimInstance Win32_ComputerSystem).Domain\n$out | Where-Object { $_ -and $_ -ne 'WORKGROUP' } | ForEach-Object { $_.ToLower() } | Select-Object -Unique\n`;\n try {\n const out = await runScriptOrThrow(script);\n dnsSuffixCache = out.split(/\\r?\\n/).map(s => s.trim()).filter(Boolean);\n } catch {\n dnsSuffixCache = [];\n }\n return dnsSuffixCache;\n}\n\n\n/**\n * Print an error with full context: message, cause chain (Node's fetch and\n * other newer APIs put the actual cause in error.cause), and stack on -v.\n */\n/**\n * Report an error: print a one-line summary to stderr and write the full\n * cause-chain (with stacks, codes, paths) to a temp file. Avoids dumping\n * walls of error text into the user's terminal. Set BROTHER_PRINT_DEBUG=1\n * to also include the full chain inline.\n */\nfunction reportError(e: any): void {\n const lines: string[] = [];\n lines.push(`# brother-print error report`);\n lines.push(`# timestamp: ${new Date().toISOString()}`);\n lines.push(`# argv: ${process.argv.join(\" \")}`);\n lines.push(`# cwd: ${process.cwd()}`);\n lines.push(`# pid: ${process.pid}`);\n lines.push(\"\");\n\n let cur = e;\n let depth = 0;\n while (cur) {\n const prefix = depth === 0 ? \"Error\" : \"Caused by\";\n const name = cur.name && cur.name !== \"Error\" ? ` ${cur.name}` : \"\";\n const code = cur.code ? ` [${cur.code}]` : \"\";\n const msg = cur.message ?? String(cur);\n lines.push(`${prefix}${name}${code}: ${msg}`);\n if (cur.path) lines.push(` path: ${cur.path}`);\n if (cur.input) lines.push(` input: ${cur.input}`);\n if (cur.stack) lines.push(String(cur.stack));\n lines.push(\"\");\n cur = cur.cause;\n depth++;\n if (depth > 8) { lines.push(\"... (cause chain truncated at 8 levels)\"); break; }\n }\n\n const dump = lines.join(\"\\n\");\n const tempPath = path.join(os.tmpdir(), `brother-print-error-${Date.now()}-${process.pid}.log`);\n let wroteFile = false;\n try { fs.writeFileSync(tempPath, dump); wroteFile = true; } catch { /* fallback */ }\n\n if (wroteFile) {\n console.error(`Error: ${tempPath}`);\n } else {\n console.error(dump);\n }\n if (process.env.BROTHER_PRINT_DEBUG) {\n console.error(\"--- full report (BROTHER_PRINT_DEBUG enabled) ---\");\n console.error(dump);\n }\n}\n\nasync function cmdList(opts: Record<string, string | boolean>): Promise<void> {\n const printers = await listPrinters();\n /* Brother's listPrinters returns just { name } (legacy shape). To get\n * portName/driverName for the diagnostic line, query label-core's\n * coreListPrinters too. */\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const byName = new Map(all.map(p => [p.name, p]));\n\n if (printers.length === 0) {\n console.log(\"No installed Brother printers.\");\n } else {\n const sorted = [...printers].sort((a, b) => a.name.localeCompare(b.name));\n console.log(`Brother printers (${sorted.length}):`);\n for (let i = 0; i < sorted.length; i++) {\n const p = sorted[i];\n const info = byName.get(p.name);\n let statusStr = \"?\";\n let queueWarning = \"\";\n try {\n const s = await brotherPrinter.getStatus(p.name);\n statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n queueWarning = s.queueWarning;\n } catch (e: any) {\n statusStr = `status error: ${e.message}`;\n }\n const port = info ? shortenPort(info.portName) : \"(unknown)\";\n const driver = info?.driverName ?? \"(unknown)\";\n console.log(` #${i + 1} ${p.name}`);\n if (info?.comment) console.log(` description: ${info.comment}`);\n if (info?.location) console.log(` location: ${info.location}`);\n console.log(` port: ${port}`);\n if (info?.portName && info.portName !== port) {\n console.log(` full port: ${info.portName}`);\n }\n console.log(` driver: ${driver}`);\n if (info?.shareName && info.shareName !== p.name) {\n console.log(` share name: ${info.shareName}`);\n }\n console.log(` status: ${statusStr}`);\n if (queueWarning) console.log(` warning: ${queueWarning}`);\n }\n }\n\n if (opts.mdns) {\n const { discoverPdlPrinters, isBrother, modelFromTxt, matchesInstalledPort } =\n await import(\"./mdns.js\");\n console.log(\"\");\n console.log(\"Browsing mDNS for _pdl-datastream._tcp (3s)...\");\n const found = (await discoverPdlPrinters()).filter(isBrother);\n if (found.length === 0) {\n console.log(\"No Brother printers responded via mDNS.\");\n return;\n }\n const installedPorts = all.map(p => p.portName);\n const networkOnly = found.filter(m => !installedPorts.some(pn => matchesInstalledPort(m, pn)));\n const matched = found.filter(m => installedPorts.some(pn => matchesInstalledPort(m, pn)));\n\n console.log(`mDNS responders (${found.length}; ${matched.length} matched, ${networkOnly.length} network-only):`);\n let n = 1;\n for (const m of [...matched, ...networkOnly]) {\n const isNetOnly = networkOnly.includes(m);\n const tag = isNetOnly ? \" [network only]\" : \" [installed]\";\n const model = modelFromTxt(m) ?? \"(unknown model)\";\n console.log(` #${n++} ${m.instanceName}${tag}`);\n console.log(` model: ${model}`);\n console.log(` hostname: ${m.hostname.replace(/\\.$/, \"\")}`);\n if (m.addresses.length > 0) {\n console.log(` address: ${m.addresses.join(\", \")}`);\n }\n console.log(` port: ${m.port}`);\n }\n }\n}\n\n/**\n * Pull a short identifier out of a Windows printer port: \"... on hostname\"\n * → hostname; raw IP/hostname kept as-is; USB ports kept as-is.\n */\nfunction shortenPort(portName: string): string {\n if (!portName) return \"(unknown)\";\n const onMatch = portName.match(/\\bon\\s+(\\S+)\\s*$/i);\n if (onMatch) return onMatch[1];\n const hostMatch = portName.match(/\\b(\\d{1,3}(?:\\.\\d{1,3}){3}|[\\w-]+\\.local|[\\w-]+\\.[\\w.-]+)\\b/);\n if (hostMatch) return hostMatch[1];\n return portName;\n}\n\n/**\n * Show details for a single resolved printer: name, port, driver, online\n * status, and the loaded media. For QL printers (auto-detected via the QL\n * TCP path) the media is read from the Windows driver's print ticket; for\n * PT printers detectTape queries the printer live (raw ESC i S status),\n * falling back to the driver ticket when the printer is unreachable.\n */\nasync function cmdPrinterInfo(printerName: string, qlHost: string | undefined): Promise<void> {\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n\n console.log(`Printer: ${printerName}`);\n if (info?.portName) {\n console.log(`Port: ${info.portName}`);\n }\n if (info?.driverName) {\n console.log(`Driver: ${info.driverName}`);\n }\n try {\n const s = await brotherPrinter.getStatus(printerName);\n const statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n console.log(`Status: ${statusStr}`);\n console.log(`Queue: ${s.queueLength} job(s)`);\n for (const j of s.jobs) {\n console.log(` #${j.id} \"${j.documentName}\" -- ${j.statusText}${j.stuck ? \" [STUCK]\" : \"\"}`);\n }\n if (s.queueWarning) console.log(`Warning: ${s.queueWarning}`);\n } catch (e: any) {\n console.log(`Status: error: ${e.message}`);\n }\n\n if (!qlHost) {\n const { resolvePtModel } = await import(\"./ptdirect.js\");\n const model = resolvePtModel(printerName);\n const direct = await resolvePtPort(printerName);\n if (model && direct) {\n console.log(`Direct: ${direct.port} via ${direct.source}`);\n console.log(` profile ${model.name}${model.verified ? \"\" : \" — geometry not from this model's own manual\"}`);\n } else {\n console.log(`Direct: unavailable (${!model ? \"no raster profile for this model\" : \"no serial port found\"}) — prints via the spooler`);\n }\n }\n\n if (qlHost) {\n console.log(`QL host: ${qlHost} (TCP/9100)`);\n const { detectQLMediaFromWindows } = await import(\"./ql.js\");\n const media = await detectQLMediaFromWindows(printerName);\n if (media) {\n console.log(`Media: ${media.name} (id ${media.id}, ${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : \", continuous\"})`);\n console.log(` printArea ${media.printAreaDots} dots${media.dieCutMaskedAreaDots ? `, dieCut ${media.dieCutMaskedAreaDots} dots` : \"\"}`);\n } else {\n console.log(`Media: (not detected from Windows driver — pass -ql-media <id>)`);\n }\n } else {\n const detected = await detectTape(printerName);\n const src = detected?.source === \"printer\" ? \"live from printer\" : \"driver default — printer unreachable\";\n console.log(`Loaded tape: ${detected ? `${detected.tape}mm (${src})` : \"(not detected)\"}`);\n }\n}\n\nasync function cmdStatus(opts: Record<string, string | boolean>): Promise<void> {\n const name = (opts.printer as string) || undefined;\n const s = await brotherPrinter.getStatus(name);\n console.log(`Printer: ${s.name}`);\n console.log(`Online: ${s.online ? \"yes\" : \"no\"}`);\n console.log(`Status: ${s.statusText}`);\n if (s.error) console.log(`Error: ${s.error}`);\n console.log(`Queue: ${s.queueLength} job(s)`);\n for (const j of s.jobs) {\n console.log(` #${j.id} \"${j.documentName}\" -- ${j.statusText}${j.stuck ? \" [STUCK]\" : \"\"}`);\n }\n if (s.queueWarning) console.log(`Warning: ${s.queueWarning}`);\n if (s.workOffline) console.log(`Work offline flag: true`);\n}\n\nasync function cmdConfig(opts: Record<string, string | boolean>): Promise<void> {\n const hasUpdate = !!(opts.tape || opts.printer);\n if (!hasUpdate) {\n const cfg = getConfig();\n console.log(`Configuration:`);\n console.log(` File: ${getConfigPath()}`);\n console.log(` Default tape: ${cfg.defaultTape ? cfg.defaultTape + \"mm\" : \"(not set)\"}`);\n console.log(` Default printer: ${cfg.defaultPrinter ?? \"(not set)\"}`);\n console.log(\"\\nValid tape sizes: 6, 9, 12, 18, 24\");\n return;\n }\n if (opts.tape) {\n const t = parseTape(opts.tape as string);\n setConfig({ defaultTape: t });\n console.log(`Default tape set to: ${t}mm`);\n }\n if (opts.printer) {\n setConfig({ defaultPrinter: opts.printer as string });\n console.log(`Default printer set to: ${opts.printer}`);\n }\n}\n\nasync function resolveTape(opts: Record<string, string | boolean>): Promise<TapeSize> {\n /* Explicit -tape skips the status round-trip entirely: the user said\n * what they want, and the live query costs ~1s of PowerShell. */\n if (opts.tape) return parseTape(opts.tape as string);\n const detected = await detectTape(opts.printer as string | undefined);\n if (detected) return detected.tape;\n return getConfig().defaultTape ?? 24;\n}\n\nasync function cmdPrint(\n opts: Record<string, string | boolean>,\n segments: Segment[],\n inputs: string[]\n): Promise<void> {\n const tape = await resolveTape(opts);\n\n // If there are 2+ positional inputs, treat each as a text segment.\n // (A single positional input is handled below via classifyInput so it can\n // auto-detect file paths.)\n if (inputs.length > 1 || (inputs.length >= 1 && segments.length > 0)) {\n for (const inp of inputs) {\n segments.push({ type: \"text\", value: inp });\n }\n inputs.length = 0;\n }\n\n const baseOpts = {\n printer: opts.printer as string | undefined,\n wait: !opts.nowait,\n waitTimeoutMs: opts.timeout ? Math.round(parseFloat(opts.timeout as string) * 1000) : undefined,\n waitIntervalMs: opts.interval ? Math.round(parseFloat(opts.interval as string) * 1000) : undefined,\n onWaiting: makeWaitCallback(),\n log: (msg: string) => outln(msg),\n };\n\n const qlHost = typeof opts.ql === \"string\" ? opts.ql : undefined;\n const qlMediaId = typeof opts.qlMedia === \"string\" ? parseInt(opts.qlMedia, 10) : undefined;\n\n // Multi-segment mode: explicit -text/-qr (>=1 segment), with positional joined in\n if (segments.length > 1 || (segments.length === 1 && inputs.length === 0)) {\n const segOpts: SegmentOptions = {\n ...baseOpts,\n tape,\n textHeight: opts.height as string | undefined,\n space: opts.space as string | undefined,\n };\n let segDirectPort: string;\n if (opts.output) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n fs.writeFileSync(opts.output as string, buffer);\n outln(`Saved to ${opts.output}`);\n } else if (qlHost) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);\n outln(`Printed ${segments.length} segment(s) on ${(opts.printer as string) || \"QL\"} @ ${qlHost}, ${tape}mm (direct TCP/9100)`);\n } else if (segDirectPort = await tryPrintDirect(\n await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined),\n opts, tape)) {\n reportPrinted(`${segments.length} segment(s) `, opts, tape, segDirectPort);\n } else {\n await printSegments(segments, segOpts);\n reportPrinted(`${segments.length} segment(s) `, opts, tape, null);\n }\n return;\n }\n\n // Single content op\n const printOpts: PrintOptions = {\n ...baseOpts,\n tape,\n aspect: opts.aspect as string | undefined,\n textHeight: opts.height as string | undefined,\n };\n\n if (opts.clip) {\n printOpts.clip = true;\n } else if (inputs.length === 1) {\n Object.assign(printOpts, classifyInput(inputs[0], opts));\n } else if (inputs.length === 0 && segments.length === 0) {\n if (opts.printer) {\n await cmdPrinterInfo(opts.printer as string, qlHost);\n return;\n }\n showHelp();\n return;\n }\n\n if (opts.output) {\n const buffer = await render(printOpts);\n fs.writeFileSync(opts.output as string, buffer);\n outln(`Saved to ${opts.output}`);\n return;\n }\n if (qlHost) {\n const buffer = await render(printOpts);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);\n outln(`Printed on ${(opts.printer as string) || \"QL\"} @ ${qlHost} (direct TCP/9100)`);\n return;\n }\n const directPort = await tryPrintDirect(await render(printOpts), opts, tape);\n if (directPort) {\n reportPrinted(\"\", opts, tape, directPort);\n return;\n }\n await print(printOpts);\n reportPrinted(\"\", opts, tape, null);\n}\n\n/**\n * Name the printer, the tape and the route a label actually took.\n *\n * \"Printed on 24mm tape\" is not something the user can check: it does not say\n * which of four Brother printers took it, and it reads identically whether the\n * bytes reached the printer or merely reached the Windows queue -- which is\n * where labels disappear. Naming the target and the route makes the line\n * verifiable, and still means something when read back in a log later.\n */\nfunction describeTarget(opts: Record<string, string | boolean>, tape: TapeSize, port: string): string {\n const printer = (opts.printer as string) || getDefaultPrinter();\n return port\n ? `${printer}, ${tape}mm tape (direct to ${port})`\n : `${printer}, ${tape}mm tape`;\n}\n\n/**\n * The verb has to be honest about what happened. A direct write reached the\n * printer, so \"Printed\" is a claim we can stand behind. Handing a job to the\n * Windows spooler is not printing -- the job can sit Retained in Error behind\n * another dead job and never reach the tape, which is exactly how labels have\n * been going missing. Say \"Queued\" for that, and name the printer either way.\n */\nfunction reportPrinted(what: string, opts: Record<string, string | boolean>, tape: TapeSize, port: string): void {\n const verb = port ? \"Printed\" : \"Queued\";\n const tail = port ? \"\" : \" -- the spooler has it; check the queue if nothing comes out\";\n outln(`${verb} ${what}on ${describeTarget(opts, tape, port)}${tail}`);\n}\n\n/**\n * Write the rendered label straight to the printer's port, skipping the\n * Windows spooler. Returns false when this printer has no direct path, so the\n * caller can fall back to the XPS/queue route.\n *\n * The direct route is the default because the queue is where labels go to die:\n * a job that fails is left Retained and blocks every later job until someone\n * clears it by hand, and `WorkOffline` discards jobs without a word. Writing\n * the bytes ourselves either succeeds or throws — there is no third state that\n * looks like success and prints nothing. `-queue` opts back in.\n */\nasync function tryPrintDirect(\n png: Buffer,\n opts: Record<string, string | boolean>,\n tape: TapeSize\n): Promise<string> {\n if (opts.queue) {\n /* A queue can be pointed at a discard port so the spooler stops\n * fighting us for the printer's serial port. Printing to it would\n * report success and put nothing on the tape, which is the one\n * outcome this whole module exists to prevent. */\n await refuseDiscardQueue(opts.printer as string);\n return null;\n }\n /* Fall back to the same default the spooler path uses, so omitting -p does\n * not silently drop the label into the queue. */\n const printerName = (opts.printer as string) || getDefaultPrinter();\n if (!printerName) return null;\n\n const { resolvePtModel, printPtDirect } = await import(\"./ptdirect.js\");\n if (!resolvePtModel(printerName)) return null;\n\n const port = (opts.port as string) || (await resolvePtPort(printerName))?.port;\n if (!port) return null;\n\n const jobsBefore = await listJobIds(printerName);\n try {\n await printPtDirect(png, {\n port,\n printerName,\n tape,\n autoCut: !opts.nocut,\n log: opts.verbose ? (m: string) => outln(m) : undefined,\n });\n } finally {\n /* Clear it even when the write failed -- a failed open still leaves the\n * phantom behind, and that is precisely when a wedged queue is most\n * confusing to look at. */\n await clearPhantomJobs(printerName, jobsBefore);\n }\n return port;\n}\n\n/** Ports that swallow whatever the spooler sends them. */\nconst DISCARD_PORTS = /^(nul|null|nul:|null:)$/i;\n\n/**\n * List the spooler's current job ids for a printer. Best-effort: a failure\n * here must never stop a label printing.\n */\nasync function listJobIds(printerName: string): Promise<Set<number>> {\n try {\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const out = await runScriptOrThrow(\n `@(Get-PrintJob -PrinterName '${printerName.replace(/'/g, \"''\")}' -ErrorAction SilentlyContinue).Id -join ','`\n );\n return new Set(out.trim().split(\",\").map(Number).filter(n => !isNaN(n)));\n } catch {\n return new Set();\n }\n}\n\n/**\n * Remove the empty job Windows invents when we open the printer's port.\n *\n * Writing directly to a serial port that a print queue is bound to makes the\n * spooler materialise a zero-byte job -- proven with a script that only opens\n * the port and writes an initialise sequence. That job then parks itself in\n * Error and drags the queue down with it, which is what made the queue look\n * broken after every successful direct print.\n *\n * The queue has to stay bound to the port: that binding is what establishes\n * and holds the Bluetooth RFCOMM link. Open the port with the queue pointed\n * elsewhere and the link will not come up at all. So the phantom cannot be\n * prevented, only cleared.\n *\n * Scoped by identity, not by size: only jobs that did not exist before we\n * opened the port are removed. Size is no use as a filter -- the spooler\n * records a copy of the bytes we put on the wire, so these jobs are not empty\n * (1205 and 4373 bytes were observed for two ordinary labels).\n *\n * The residual risk is a real job submitted by another program during the\n * second or so a direct print takes. That window is narrow and the alternative\n * -- leaving the queue permanently in Error after every label -- is worse.\n */\nasync function clearPhantomJobs(printerName: string, before: Set<number>): Promise<void> {\n try {\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const name = printerName.replace(/'/g, \"''\");\n const skip = [...before].join(\",\") || \"-1\";\n /* The spooler creates the job around the time the handle closes, so\n * give it a moment to appear before looking, and a moment to act on\n * the delete before returning -- a job caught mid-print lingers in\n * \"Deleting\" otherwise. */\n await runScriptOrThrow(`\n$ErrorActionPreference = 'SilentlyContinue'\n$before = @(${skip})\nStart-Sleep -Milliseconds 900\nGet-PrintJob -PrinterName '${name}' | Where-Object { $_.Id -notin $before } |\n ForEach-Object { Remove-PrintJob -PrinterName '${name}' -ID $_.Id }\nStart-Sleep -Milliseconds 600\n`);\n } catch { /* cosmetic cleanup; never fail a print over it */ }\n}\n\n/**\n * Refuse `-queue` when the printer's Windows queue points at a discard port.\n *\n * Repointing a queue away from the printer's real port is how we stop the\n * spooler from creating a phantom job every time we open that port. The cost\n * is that the queue no longer goes anywhere, so `-queue` must fail loudly\n * rather than cheerfully reporting a label that was never printed.\n */\nasync function refuseDiscardQueue(printerName: string): Promise<void> {\n const name = printerName || getDefaultPrinter();\n if (!name) return;\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const info = (await coreListPrinters(/brother|^pt-|^ql-/i)).find(p => p.name === name);\n const portName = (info?.portName ?? \"\").replace(/:$/, \"\");\n if (!DISCARD_PORTS.test(portName)) return;\n throw new UserError(\n `${name}'s Windows queue points at \"${info.portName}\", which discards everything sent to it — ` +\n `-queue would report success and print nothing. Drop -queue to print directly, ` +\n `or repoint the queue at the printer's port with: Set-Printer -Name \"${name}\" -PortName \"COMn:\"`\n );\n}\n\n/**\n * Find the serial port to write a P-touch label to.\n *\n * The printer queue's PortName is the obvious source but it goes stale:\n * Windows reassigns a Bluetooth printer's outgoing COM number when it\n * re-pairs (the PT-D610BT here was installed on COM8 and came back on COM10),\n * and the queue keeps pointing at the old one. Worse, the abandoned number is\n * often still a valid *incoming* Bluetooth port with nothing behind it, so the\n * stale queue looks openable and simply swallows the job.\n *\n * So: ask Bluetooth which port actually belongs to this printer, and fall back\n * to the queue's port only when no Bluetooth match exists (a genuinely wired\n * serial printer). Returns null when neither yields a COM port.\n */\nasync function resolvePtPort(printerName: string): Promise<{ port: string; source: string }> {\n const { listPrinters: coreListPrinters, runScriptOrThrow } =\n await import(\"@bobfrankston/label-core\");\n\n /* Bluetooth SPP ports carry the remote device's address in their instance\n * id; BTHPORT maps that address to the advertised device name, which\n * contains the model (e.g. \"PT-D610BT5465\"). Match on the model token. */\n const model = /PT-([A-Z0-9]+)/i.exec(printerName)?.[1];\n if (model) {\n const script = [\n \"$ErrorActionPreference = 'SilentlyContinue'\",\n \"Get-PnpDevice -Class Ports -PresentOnly | Where-Object { $_.FriendlyName -match 'Bluetooth' } | ForEach-Object {\",\n \" if ($_.InstanceId -match '&0&([0-9A-Fa-f]{12})_') {\",\n \" $addr = $matches[1].ToLower()\",\n \" $raw = (Get-ItemProperty \\\"HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\BTHPORT\\\\Parameters\\\\Devices\\\\$addr\\\").Name\",\n \" if ($raw -and $_.FriendlyName -match '\\\\((COM\\\\d+)\\\\)') {\",\n \" $com = $matches[1]\",\n \" $name = [System.Text.Encoding]::ASCII.GetString($raw).Trim([char]0)\",\n \" Write-Output \\\"$com=$name\\\"\",\n \" }\",\n \" }\",\n \"}\",\n ].join(\"\\n\");\n try {\n const out = (await runScriptOrThrow(script)).trim();\n for (const line of out.split(/\\r?\\n/)) {\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n const com = line.slice(0, eq).trim();\n const name = line.slice(eq + 1).trim();\n const squashed = name.replace(/[^A-Za-z0-9]/g, \"\").toUpperCase();\n if (com && squashed.includes(model.toUpperCase())) {\n return { port: com, source: `Bluetooth device ${name}` };\n }\n }\n } catch { /* fall through to the queue's port */ }\n }\n\n const info = (await coreListPrinters(/brother|^pt-/i)).find(p => p.name === printerName);\n const m = /^(COM\\d+):?$/i.exec(info?.portName ?? \"\");\n if (m) return { port: m[1], source: `printer queue port ${info.portName}` };\n return null;\n}\n\nfunction classifyInput(input: string, opts: Record<string, string | boolean>): Partial<CorePrintOptions> {\n if (opts.text) return { text: input };\n if (opts.html) return { htmlPath: path.resolve(input) };\n if (opts.image) return { imagePath: path.resolve(input) };\n\n const lower = input.toLowerCase();\n if (lower.endsWith(\".html\") || lower.endsWith(\".htm\")) return { htmlPath: path.resolve(input) };\n if (lower.endsWith(\".txt\")) return { textFile: path.resolve(input) };\n if (/\\.(png|jpg|jpeg|bmp|gif)$/i.test(lower)) return { imagePath: path.resolve(input) };\n if (fs.existsSync(input)) {\n const ext = path.extname(lower);\n if (ext === \".html\" || ext === \".htm\") return { htmlPath: path.resolve(input) };\n return { textFile: path.resolve(input) };\n }\n return { text: input };\n}\n\nfunction makeWaitCallback() {\n let lastReport = -Infinity;\n return (status: PrinterStatus, elapsedMs: number) => {\n const secs = Math.floor(elapsedMs / 1000);\n // Non-TTY (redirected output) can't rewrite in place; report sparsely.\n if (!process.stderr.isTTY) {\n if (secs - lastReport < 30) return;\n lastReport = secs;\n }\n statusLine.update(`[brother-print] waiting for ${status.name} (${status.statusText}${status.error ? \", \" + status.error : \"\"}); ${secs}s`);\n };\n}\n\nmain();\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Brother Label Printer CLI\n * Thin wrapper around api.ts using shared CLI primitives from label-core.\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport * as dns from \"dns/promises\";\nimport {\n preprocessSingleQuotes,\n parseArgs,\n} from \"@bobfrankston/label-core\";\nimport type {\n PrintOptions as CorePrintOptions,\n PrinterStatus,\n Segment,\n ValuedOptionSpec,\n BooleanFlagSpec,\n} from \"@bobfrankston/label-core\";\nimport {\n print,\n render,\n renderSegments,\n printSegments,\n getConfig,\n setConfig,\n getConfigPath,\n listPrinters,\n resolvePrinter,\n getDefaultPrinter,\n detectTape,\n brotherPrinter,\n} from \"./api.js\";\nimport type { TapeSize, PrintOptions, SegmentOptions } from \"./api.js\";\n\nconst VERSION = \"1.1.0\";\nconst VALID_TAPES: TapeSize[] = [6, 9, 12, 18, 24];\n\nfunction parseTape(value: string): TapeSize {\n const num = parseInt(value.replace(\"mm\", \"\"), 10) as TapeSize;\n if (!VALID_TAPES.includes(num)) {\n throw new UserError(`Invalid tape size: ${value}. Valid: ${VALID_TAPES.join(\", \")}`);\n }\n return num;\n}\n\nconst VALUED: ValuedOptionSpec[] = [\n { names: [\"-tape\"], key: \"tape\" },\n { names: [\"-p\", \"-printer\"], key: \"printer\" },\n { names: [\"-o\", \"-output\"], key: \"output\" },\n { names: [\"-a\", \"-aspect\"], key: \"aspect\" },\n { names: [\"-ht\", \"-height\"], key: \"height\" },\n { names: [\"-s\", \"-space\"], key: \"space\" },\n { names: [\"-timeout\"], key: \"timeout\" },\n { names: [\"-interval\"], key: \"interval\" },\n { names: [\"-ql\"], key: \"ql\" },\n { names: [\"-ql-media\"], key: \"qlMedia\" },\n { names: [\"-port\"], key: \"port\" },\n];\n\nconst BOOLEAN: BooleanFlagSpec[] = [\n { names: [\"-w\", \"-html\"], key: \"html\" },\n { names: [\"-i\", \"-image\"], key: \"image\" },\n { names: [\"-t\"], key: \"text\" },\n { names: [\"-c\", \"-clip\"], key: \"clip\" },\n { names: [\"-no-wait\", \"-nowait\"], key: \"nowait\" },\n { names: [\"-mdns\"], key: \"mdns\" },\n { names: [\"-vb\", \"-verbose\"], key: \"verbose\" },\n { names: [\"-queue\"], key: \"queue\" },\n { names: [\"-no-cut\", \"-nocut\"], key: \"nocut\" },\n { names: [\"-help\", \"-h\", \"-?\"], key: \"help\" },\n { names: [\"-version\", \"-v\"], key: \"version\" },\n];\n\nconst SUBCOMMANDS = [\"list\", \"status\", \"config\"];\n\nfunction showHelp(): void {\n console.log(`Brother Label Printer CLI v${VERSION}\n\nUsage:\n brother-print [options] <text> Print text\n brother-print [options] <file> Print html/txt/image file (auto-detected)\n brother-print -clip [options] Print clipboard contents (image or text)\n brother-print -text <v> -qr <v> ... Print ordered text/qr segments\n brother-print -list [-mdns] List Brother printers and status\n (-mdns also discovers network printers)\n brother-print -status [-p <name>] Show printer status\n brother-print -config Show / set defaults\n\nCommands take a leading dash so they can never be confused with label content:\na bare word is always something to print, a dashed word is always a command.\n\nTape size is auto-detected from the printer. Use -tape to override.\nSingle quotes can wrap arguments: '7\"' 'line1\\\\nline2'\n\nOptions:\n -tape <size> Tape size: 6, 9, 12, 18, 24 (mm) [auto-detected]\n -p, -printer <name> Printer queue name\n -o, -output <file> Save PNG to file instead of printing\n -a, -aspect <r> Aspect ratio width:height for HTML (e.g. 3.5:2)\n -ht, -height <size> Text height: 12mm, .5in, or 50% (of tape height)\n -s, -space <size> Space between segments: 12px, 1mm, .2in\n -t, -text Force input as literal text (-text <v> for segments)\n -qr <data> QR code segment\n -w, -html Force input as HTML file path\n -i, -image Force input as image file path\n -c, -clip Read content from clipboard (image preferred, then text)\n -no-wait Fail immediately if printer is offline (default: wait)\n -timeout <secs> Max wait time when printer is offline\n -interval <secs> Polling interval while waiting (default 2)\n -mdns With -list: also discover network printers via mDNS\n -ql <host> Print to a Brother QL series printer over TCP/9100\n (bypasses Windows queue; e.g. -ql ql720 or -ql 1.2.3.4)\n -ql-media <id> QL media id (e.g. 271=DK-11201 29×90mm, 259=DK-22205 62mm)\n Required when the printer doesn't respond to status\n -queue Print through the Windows spooler (XPS) instead of\n writing raster bytes straight to the printer's port\n -port <COMn> Serial/Bluetooth port for direct printing (overrides\n the port looked up from the printer queue)\n -no-cut Do not cut the tape after the label (direct path only)\n -vb, -verbose Print extra QL diagnostic messages (status, media source)\n -help Show this help\n -version Show version`);\n}\n\n/** Expected user-facing error: prints one line, no log file, no stack. */\nclass UserError extends Error {\n readonly isUserError = true;\n}\n\n/**\n * Single-line status display: on a TTY the line is rewritten in place\n * (CR + erase-line) instead of scrolling; callers must clear() before any\n * normal output so it doesn't collide with the pending status line.\n */\nconst statusLine = {\n active: false,\n update(msg: string): void {\n if (process.stderr.isTTY) {\n process.stderr.write(`\\r\\x1b[2K${msg}`);\n this.active = true;\n } else {\n console.error(msg);\n }\n },\n clear(): void {\n if (this.active) {\n process.stderr.write(`\\r\\x1b[2K`);\n this.active = false;\n }\n },\n};\n\n/** console.log that first clears any in-place status line. */\nfunction outln(msg: string): void {\n statusLine.clear();\n console.log(msg);\n}\n\nasync function main(): Promise<void> {\n const argv = preprocessSingleQuotes(process.argv.slice(2));\n let parsed;\n try {\n parsed = parseArgs(argv, VALUED, BOOLEAN, SUBCOMMANDS);\n } catch (e: any) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n\n if (parsed.unknown.length > 0) {\n console.error(`Unknown option(s): ${parsed.unknown.join(\", \")}`);\n console.error(`Run \"brother-print -help\" for usage.`);\n process.exit(1);\n }\n\n /* Bare `list` used to be the command syntax; it is now `-list`. Refusing\n * beats guessing: printing a label that says \"list\" and listing printers\n * are both plausible readings of the same word. */\n if (parsed.ambiguous.length > 0) {\n const w = parsed.ambiguous[0];\n console.error(`Ambiguous argument: \"${w}\" -- commands now take a dash.`);\n console.error(` brother-print -${w} run the ${w} command`);\n console.error(` brother-print -text ${w} print a label that says \"${w}\"`);\n process.exit(1);\n }\n\n if (parsed.opts.help) { showHelp(); return; }\n if (parsed.opts.version) { console.log(VERSION); return; }\n\n try {\n if (typeof parsed.opts.printer === \"string\") {\n parsed.opts.printer = await resolvePrinter(parsed.opts.printer);\n /* Auto-route QL series through the QL TCP path. The XPS / CustomMediaSize\n * pipeline is P-touch-only; sending it to a QL queue produces an XPS\n * error. If the user already passed -ql explicitly, leave it alone. */\n if (!parsed.opts.ql && isQLPrinter(parsed.opts.printer)) {\n const host = await resolveQLHost(parsed.opts.printer);\n if (host) parsed.opts.ql = host;\n }\n }\n switch (parsed.command) {\n case \"list\": await cmdList(parsed.opts); return;\n case \"status\": await cmdStatus(parsed.opts); return;\n case \"config\": await cmdConfig(parsed.opts); return;\n }\n await cmdPrint(parsed.opts, parsed.segments, parsed.inputs);\n } catch (e: any) {\n statusLine.clear();\n /* Timeout/offline are expected conditions, not crashes — one line,\n * no error-report file. */\n if (e?.isUserError || /^Timed out waiting for printer|is offline \\(/.test(e?.message ?? \"\")) {\n console.error(`Error: ${e.message}`);\n process.exit(1);\n }\n reportError(e);\n process.exit(1);\n }\n}\n\nfunction isQLPrinter(name: string): boolean {\n return /\\bQL-/i.test(name);\n}\n\n/**\n * Look up the TCP host for a QL printer's installed Windows queue. Pulls the\n * port name via label-core, then asks PowerShell for that port's\n * PrinterHostAddress. Returns undefined if the port isn't a TCP/IP port (e.g.\n * the QL-820NWB's BRWxxxx Bluetooth port has no host address).\n */\nasync function resolveQLHost(printerName: string): Promise<string | undefined> {\n const { listPrinters: coreListPrinters, runScriptOrThrow } =\n await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n if (!info?.portName) return undefined;\n const port = info.portName.replace(/'/g, \"''\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$p = Get-PrinterPort -Name '${port}'\nif ($p -and $p.PrinterHostAddress) { Write-Output $p.PrinterHostAddress }\n`;\n try {\n const out = (await runScriptOrThrow(script)).trim();\n if (!out) return undefined;\n return await resolvableHost(out);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Brother's installer often writes a bare NetBIOS-style hostname (BRWxxxx)\n * as the port's PrinterHostAddress, which won't resolve in environments where\n * the DNS suffix isn't auto-appended. Resolution tiers, in order:\n * 1. as-is via the OS resolver\n * 2. <host>.<suffix> for every DNS suffix the OS knows about (global search\n * list, per-connection suffix, computer domain) — covers the case where\n * the user pings as FQDN manually because Windows isn't auto-appending\n * 3. <host>.local via the OS resolver (Windows 10 1803+ native mDNS)\n * 4. multicast mDNS query directly (bypasses OS resolver — works even when\n * Windows .local resolution is disabled, common in AD/domain environments)\n */\nasync function resolvableHost(host: string): Promise<string> {\n if (/^\\d{1,3}(\\.\\d{1,3}){3}$/.test(host)) return host;\n try {\n await dns.lookup(host);\n return host;\n } catch {}\n if (!host.includes(\".\")) {\n const suffixes = await getDnsSuffixes();\n const { default_fqdn } = await import(\"@bobfrankston/miscinfo\");\n if (default_fqdn && !suffixes.includes(default_fqdn.toLowerCase())) {\n suffixes.push(default_fqdn.toLowerCase());\n }\n for (const suffix of suffixes) {\n const candidate = `${host}.${suffix}`;\n try {\n await dns.lookup(candidate);\n return candidate;\n } catch {}\n }\n }\n const mdnsName = host.includes(\".\") ? host : host + \".local\";\n try {\n await dns.lookup(mdnsName);\n return mdnsName;\n } catch {}\n try {\n const { discoverPdlPrinters } = await import(\"./mdns.js\");\n const printers = await discoverPdlPrinters(2500);\n const target = host.toLowerCase();\n const hit = printers.find(p => {\n const h = p.hostname.toLowerCase().replace(/\\.$/, \"\");\n return h === target || h === target + \".local\" || h.startsWith(target + \".\");\n });\n if (hit && hit.addresses.length > 0) return hit.addresses[0];\n } catch {}\n return host;\n}\n\nlet dnsSuffixCache: string[] | undefined;\n/**\n * Read every DNS suffix the system knows about: the global SuffixSearchList,\n * each adapter's ConnectionSpecificSuffix, and the computer's AD/workgroup\n * domain. Cached per-process. Returns lowercased, de-duplicated, in priority\n * order (search list first since that's what the user explicitly configured).\n */\nasync function getDnsSuffixes(): Promise<string[]> {\n if (dnsSuffixCache) return dnsSuffixCache;\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const script = `\n$ErrorActionPreference = 'SilentlyContinue'\n$out = @()\n$out += (Get-DnsClientGlobalSetting).SuffixSearchList\n$out += Get-DnsClient | Select-Object -ExpandProperty ConnectionSpecificSuffix\n$out += (Get-CimInstance Win32_ComputerSystem).Domain\n$out | Where-Object { $_ -and $_ -ne 'WORKGROUP' } | ForEach-Object { $_.ToLower() } | Select-Object -Unique\n`;\n try {\n const out = await runScriptOrThrow(script);\n dnsSuffixCache = out.split(/\\r?\\n/).map(s => s.trim()).filter(Boolean);\n } catch {\n dnsSuffixCache = [];\n }\n return dnsSuffixCache;\n}\n\n\n/**\n * Print an error with full context: message, cause chain (Node's fetch and\n * other newer APIs put the actual cause in error.cause), and stack on -v.\n */\n/**\n * Report an error: print a one-line summary to stderr and write the full\n * cause-chain (with stacks, codes, paths) to a temp file. Avoids dumping\n * walls of error text into the user's terminal. Set BROTHER_PRINT_DEBUG=1\n * to also include the full chain inline.\n */\nfunction reportError(e: any): void {\n const lines: string[] = [];\n lines.push(`# brother-print error report`);\n lines.push(`# timestamp: ${new Date().toISOString()}`);\n lines.push(`# argv: ${process.argv.join(\" \")}`);\n lines.push(`# cwd: ${process.cwd()}`);\n lines.push(`# pid: ${process.pid}`);\n lines.push(\"\");\n\n let cur = e;\n let depth = 0;\n while (cur) {\n const prefix = depth === 0 ? \"Error\" : \"Caused by\";\n const name = cur.name && cur.name !== \"Error\" ? ` ${cur.name}` : \"\";\n const code = cur.code ? ` [${cur.code}]` : \"\";\n const msg = cur.message ?? String(cur);\n lines.push(`${prefix}${name}${code}: ${msg}`);\n if (cur.path) lines.push(` path: ${cur.path}`);\n if (cur.input) lines.push(` input: ${cur.input}`);\n if (cur.stack) lines.push(String(cur.stack));\n lines.push(\"\");\n cur = cur.cause;\n depth++;\n if (depth > 8) { lines.push(\"... (cause chain truncated at 8 levels)\"); break; }\n }\n\n const dump = lines.join(\"\\n\");\n const tempPath = path.join(os.tmpdir(), `brother-print-error-${Date.now()}-${process.pid}.log`);\n let wroteFile = false;\n try { fs.writeFileSync(tempPath, dump); wroteFile = true; } catch { /* fallback */ }\n\n if (wroteFile) {\n console.error(`Error: ${tempPath}`);\n } else {\n console.error(dump);\n }\n if (process.env.BROTHER_PRINT_DEBUG) {\n console.error(\"--- full report (BROTHER_PRINT_DEBUG enabled) ---\");\n console.error(dump);\n }\n}\n\nasync function cmdList(opts: Record<string, string | boolean>): Promise<void> {\n const printers = await listPrinters();\n /* Brother's listPrinters returns just { name } (legacy shape). To get\n * portName/driverName for the diagnostic line, query label-core's\n * coreListPrinters too. */\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const byName = new Map(all.map(p => [p.name, p]));\n\n if (printers.length === 0) {\n console.log(\"No installed Brother printers.\");\n } else {\n const sorted = [...printers].sort((a, b) => a.name.localeCompare(b.name));\n console.log(`Brother printers (${sorted.length}):`);\n for (let i = 0; i < sorted.length; i++) {\n const p = sorted[i];\n const info = byName.get(p.name);\n let statusStr = \"?\";\n let queueWarning = \"\";\n try {\n const s = await brotherPrinter.getStatus(p.name);\n statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n queueWarning = s.queueWarning;\n } catch (e: any) {\n statusStr = `status error: ${e.message}`;\n }\n const port = info ? shortenPort(info.portName) : \"(unknown)\";\n const driver = info?.driverName ?? \"(unknown)\";\n console.log(` #${i + 1} ${p.name}`);\n if (info?.comment) console.log(` description: ${info.comment}`);\n if (info?.location) console.log(` location: ${info.location}`);\n console.log(` port: ${port}`);\n if (info?.portName && info.portName !== port) {\n console.log(` full port: ${info.portName}`);\n }\n console.log(` driver: ${driver}`);\n if (info?.shareName && info.shareName !== p.name) {\n console.log(` share name: ${info.shareName}`);\n }\n console.log(` status: ${statusStr}`);\n if (queueWarning) console.log(` warning: ${queueWarning}`);\n }\n }\n\n if (opts.mdns) {\n const { discoverPdlPrinters, isBrother, modelFromTxt, matchesInstalledPort } =\n await import(\"./mdns.js\");\n console.log(\"\");\n console.log(\"Browsing mDNS for _pdl-datastream._tcp (3s)...\");\n const found = (await discoverPdlPrinters()).filter(isBrother);\n if (found.length === 0) {\n console.log(\"No Brother printers responded via mDNS.\");\n return;\n }\n const installedPorts = all.map(p => p.portName);\n const networkOnly = found.filter(m => !installedPorts.some(pn => matchesInstalledPort(m, pn)));\n const matched = found.filter(m => installedPorts.some(pn => matchesInstalledPort(m, pn)));\n\n console.log(`mDNS responders (${found.length}; ${matched.length} matched, ${networkOnly.length} network-only):`);\n let n = 1;\n for (const m of [...matched, ...networkOnly]) {\n const isNetOnly = networkOnly.includes(m);\n const tag = isNetOnly ? \" [network only]\" : \" [installed]\";\n const model = modelFromTxt(m) ?? \"(unknown model)\";\n console.log(` #${n++} ${m.instanceName}${tag}`);\n console.log(` model: ${model}`);\n console.log(` hostname: ${m.hostname.replace(/\\.$/, \"\")}`);\n if (m.addresses.length > 0) {\n console.log(` address: ${m.addresses.join(\", \")}`);\n }\n console.log(` port: ${m.port}`);\n }\n }\n}\n\n/**\n * Pull a short identifier out of a Windows printer port: \"... on hostname\"\n * → hostname; raw IP/hostname kept as-is; USB ports kept as-is.\n */\nfunction shortenPort(portName: string): string {\n if (!portName) return \"(unknown)\";\n const onMatch = portName.match(/\\bon\\s+(\\S+)\\s*$/i);\n if (onMatch) return onMatch[1];\n const hostMatch = portName.match(/\\b(\\d{1,3}(?:\\.\\d{1,3}){3}|[\\w-]+\\.local|[\\w-]+\\.[\\w.-]+)\\b/);\n if (hostMatch) return hostMatch[1];\n return portName;\n}\n\n/**\n * Show details for a single resolved printer: name, port, driver, online\n * status, and the loaded media. For QL printers (auto-detected via the QL\n * TCP path) the media is read from the Windows driver's print ticket; for\n * PT printers detectTape queries the printer live (raw ESC i S status),\n * falling back to the driver ticket when the printer is unreachable.\n */\nasync function cmdPrinterInfo(printerName: string, qlHost: string | undefined): Promise<void> {\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const all = await coreListPrinters(/brother|^pt-|^ql-/i);\n const info = all.find(p => p.name === printerName);\n\n console.log(`Printer: ${printerName}`);\n if (info?.portName) {\n console.log(`Port: ${info.portName}`);\n }\n if (info?.driverName) {\n console.log(`Driver: ${info.driverName}`);\n }\n try {\n const s = await brotherPrinter.getStatus(printerName);\n const statusStr = s.online ? \"online\" : `offline (${s.statusText}${s.error ? \", \" + s.error : \"\"})`;\n console.log(`Status: ${statusStr}`);\n console.log(`Queue: ${s.queueLength} job(s)`);\n for (const j of s.jobs) {\n console.log(` #${j.id} \"${j.documentName}\" -- ${j.statusText}${j.stuck ? \" [STUCK]\" : \"\"}`);\n }\n if (s.queueWarning) console.log(`Warning: ${s.queueWarning}`);\n } catch (e: any) {\n console.log(`Status: error: ${e.message}`);\n }\n\n if (!qlHost) {\n const { resolvePtModel } = await import(\"./ptdirect.js\");\n const model = resolvePtModel(printerName);\n const direct = await resolvePtPort(printerName);\n if (model && direct) {\n console.log(`Direct: ${direct.port} via ${direct.source}`);\n console.log(` profile ${model.name}${model.verified ? \"\" : \" — geometry not from this model's own manual\"}`);\n } else {\n console.log(`Direct: unavailable (${!model ? \"no raster profile for this model\" : \"no serial port found\"}) — prints via the spooler`);\n }\n }\n\n if (qlHost) {\n console.log(`QL host: ${qlHost} (TCP/9100)`);\n const { detectQLMediaFromWindows } = await import(\"./ql.js\");\n const media = await detectQLMediaFromWindows(printerName);\n if (media) {\n console.log(`Media: ${media.name} (id ${media.id}, ${media.widthMm}mm${media.heightMm ? `×${media.heightMm}mm` : \", continuous\"})`);\n console.log(` printArea ${media.printAreaDots} dots${media.dieCutMaskedAreaDots ? `, dieCut ${media.dieCutMaskedAreaDots} dots` : \"\"}`);\n } else {\n console.log(`Media: (not detected from Windows driver — pass -ql-media <id>)`);\n }\n } else {\n const detected = await detectTape(printerName);\n const src = detected?.source === \"printer\" ? \"live from printer\" : \"driver default — printer unreachable\";\n console.log(`Loaded tape: ${detected ? `${detected.tape}mm (${src})` : \"(not detected)\"}`);\n }\n}\n\nasync function cmdStatus(opts: Record<string, string | boolean>): Promise<void> {\n const name = (opts.printer as string) || undefined;\n const s = await brotherPrinter.getStatus(name);\n console.log(`Printer: ${s.name}`);\n console.log(`Online: ${s.online ? \"yes\" : \"no\"}`);\n console.log(`Status: ${s.statusText}`);\n if (s.error) console.log(`Error: ${s.error}`);\n console.log(`Queue: ${s.queueLength} job(s)`);\n for (const j of s.jobs) {\n console.log(` #${j.id} \"${j.documentName}\" -- ${j.statusText}${j.stuck ? \" [STUCK]\" : \"\"}`);\n }\n if (s.queueWarning) console.log(`Warning: ${s.queueWarning}`);\n if (s.workOffline) console.log(`Work offline flag: true`);\n}\n\nasync function cmdConfig(opts: Record<string, string | boolean>): Promise<void> {\n const hasUpdate = !!(opts.tape || opts.printer);\n if (!hasUpdate) {\n const cfg = getConfig();\n console.log(`Configuration:`);\n console.log(` File: ${getConfigPath()}`);\n console.log(` Default tape: ${cfg.defaultTape ? cfg.defaultTape + \"mm\" : \"(not set)\"}`);\n console.log(` Default printer: ${cfg.defaultPrinter ?? \"(not set)\"}`);\n console.log(\"\\nValid tape sizes: 6, 9, 12, 18, 24\");\n return;\n }\n if (opts.tape) {\n const t = parseTape(opts.tape as string);\n setConfig({ defaultTape: t });\n console.log(`Default tape set to: ${t}mm`);\n }\n if (opts.printer) {\n setConfig({ defaultPrinter: opts.printer as string });\n console.log(`Default printer set to: ${opts.printer}`);\n }\n}\n\nasync function resolveTape(opts: Record<string, string | boolean>): Promise<TapeSize> {\n /* Explicit -tape skips the status round-trip entirely: the user said\n * what they want, and the live query costs ~1s of PowerShell. */\n if (opts.tape) return parseTape(opts.tape as string);\n const detected = await detectTape(opts.printer as string | undefined);\n if (detected) return detected.tape;\n return getConfig().defaultTape ?? 24;\n}\n\nasync function cmdPrint(\n opts: Record<string, string | boolean>,\n segments: Segment[],\n inputs: string[]\n): Promise<void> {\n const tape = await resolveTape(opts);\n\n // If there are 2+ positional inputs, treat each as a text segment.\n // (A single positional input is handled below via classifyInput so it can\n // auto-detect file paths.)\n if (inputs.length > 1 || (inputs.length >= 1 && segments.length > 0)) {\n for (const inp of inputs) {\n segments.push({ type: \"text\", value: inp });\n }\n inputs.length = 0;\n }\n\n const baseOpts = {\n printer: opts.printer as string | undefined,\n wait: !opts.nowait,\n waitTimeoutMs: opts.timeout ? Math.round(parseFloat(opts.timeout as string) * 1000) : undefined,\n waitIntervalMs: opts.interval ? Math.round(parseFloat(opts.interval as string) * 1000) : undefined,\n onWaiting: makeWaitCallback(),\n log: (msg: string) => outln(msg),\n };\n\n const qlHost = typeof opts.ql === \"string\" ? opts.ql : undefined;\n const qlMediaId = typeof opts.qlMedia === \"string\" ? parseInt(opts.qlMedia, 10) : undefined;\n\n // Multi-segment mode: explicit -text/-qr (>=1 segment), with positional joined in\n if (segments.length > 1 || (segments.length === 1 && inputs.length === 0)) {\n const segOpts: SegmentOptions = {\n ...baseOpts,\n tape,\n textHeight: opts.height as string | undefined,\n space: opts.space as string | undefined,\n };\n let segDirectPort: string;\n if (opts.output) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n fs.writeFileSync(opts.output as string, buffer);\n outln(`Saved to ${opts.output}`);\n } else if (qlHost) {\n const buffer = await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);\n outln(`Printed ${segments.length} segment(s) on ${(opts.printer as string) || \"QL\"} @ ${qlHost}, ${tape}mm (direct TCP/9100)`);\n } else if (segDirectPort = await tryPrintDirect(\n await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined),\n opts, tape)) {\n reportPrinted(`${segments.length} segment(s) `, opts, tape, segDirectPort, lastDirectConfirmed);\n } else {\n await printSegments(segments, segOpts);\n reportPrinted(`${segments.length} segment(s) `, opts, tape, null);\n }\n return;\n }\n\n // Single content op\n const printOpts: PrintOptions = {\n ...baseOpts,\n tape,\n aspect: opts.aspect as string | undefined,\n textHeight: opts.height as string | undefined,\n };\n\n if (opts.clip) {\n printOpts.clip = true;\n } else if (inputs.length === 1) {\n Object.assign(printOpts, classifyInput(inputs[0], opts));\n } else if (inputs.length === 0 && segments.length === 0) {\n if (opts.printer) {\n await cmdPrinterInfo(opts.printer as string, qlHost);\n return;\n }\n showHelp();\n return;\n }\n\n if (opts.output) {\n const buffer = await render(printOpts);\n fs.writeFileSync(opts.output as string, buffer);\n outln(`Saved to ${opts.output}`);\n return;\n }\n if (qlHost) {\n const buffer = await render(printOpts);\n const { printPngToQL } = await import(\"./ql.js\");\n await printPngToQL(qlHost, buffer, qlMediaId, opts.printer as string | undefined, !!opts.verbose);\n outln(`Printed on ${(opts.printer as string) || \"QL\"} @ ${qlHost} (direct TCP/9100)`);\n return;\n }\n const directPort = await tryPrintDirect(await render(printOpts), opts, tape);\n const directConfirmed = lastDirectConfirmed;\n if (directPort) {\n reportPrinted(\"\", opts, tape, directPort, directConfirmed);\n return;\n }\n await print(printOpts);\n reportPrinted(\"\", opts, tape, null);\n}\n\n/**\n * Name the printer, the tape and the route a label actually took.\n *\n * \"Printed on 24mm tape\" is not something the user can check: it does not say\n * which of four Brother printers took it, and it reads identically whether the\n * bytes reached the printer or merely reached the Windows queue -- which is\n * where labels disappear. Naming the target and the route makes the line\n * verifiable, and still means something when read back in a log later.\n */\nfunction describeTarget(opts: Record<string, string | boolean>, tape: TapeSize, port: string): string {\n const printer = (opts.printer as string) || getDefaultPrinter();\n return port\n ? `${printer}, ${tape}mm tape (direct to ${port})`\n : `${printer}, ${tape}mm tape`;\n}\n\n/**\n * The verb has to be honest about what happened. A direct write reached the\n * printer, so \"Printed\" is a claim we can stand behind. Handing a job to the\n * Windows spooler is not printing -- the job can sit Retained in Error behind\n * another dead job and never reach the tape, which is exactly how labels have\n * been going missing. Say \"Queued\" for that, and name the printer either way.\n */\nfunction reportPrinted(\n what: string,\n opts: Record<string, string | boolean>,\n tape: TapeSize,\n port: string,\n confirmed?: boolean\n): void {\n let verb: string;\n let tail: string;\n if (!port) {\n verb = \"Queued\";\n tail = \" -- the spooler has it; check the queue if nothing comes out\";\n } else if (confirmed) {\n verb = \"Printed\";\n tail = \"\";\n } else {\n /* The bytes were delivered, and that is all we know. This printer does\n * not report back, so it can still have refused the job -- wrong tape,\n * cover open, cutter jam -- with nothing to tell us. Claiming \"Printed\"\n * here is what made earlier failures look like successes. */\n verb = \"Sent\";\n tail = \" -- this printer does not confirm, so check the tape\";\n }\n outln(`${verb} ${what}on ${describeTarget(opts, tape, port)}${tail}`);\n}\n\n/**\n * Write the rendered label straight to the printer's port, skipping the\n * Windows spooler. Returns false when this printer has no direct path, so the\n * caller can fall back to the XPS/queue route.\n *\n * The direct route is the default because the queue is where labels go to die:\n * a job that fails is left Retained and blocks every later job until someone\n * clears it by hand, and `WorkOffline` discards jobs without a word. Writing\n * the bytes ourselves either succeeds or throws — there is no third state that\n * looks like success and prints nothing. `-queue` opts back in.\n */\nasync function tryPrintDirect(\n png: Buffer,\n opts: Record<string, string | boolean>,\n tape: TapeSize\n): Promise<string> {\n if (opts.queue) {\n /* A queue can be pointed at a discard port so the spooler stops\n * fighting us for the printer's serial port. Printing to it would\n * report success and put nothing on the tape, which is the one\n * outcome this whole module exists to prevent. */\n await refuseDiscardQueue(opts.printer as string);\n return null;\n }\n /* Fall back to the same default the spooler path uses, so omitting -p does\n * not silently drop the label into the queue. */\n const printerName = (opts.printer as string) || getDefaultPrinter();\n if (!printerName) return null;\n\n const { resolvePtModel, printPtDirect } = await import(\"./ptdirect.js\");\n const model = resolvePtModel(printerName);\n if (!model) return null;\n\n const port = (opts.port as string) || (await resolvePtPort(printerName))?.port;\n if (!port) return null;\n\n lastDirectConfirmed = model.confirmsPrint;\n const jobsBefore = await listJobIds(printerName);\n try {\n await printPtDirect(png, {\n port,\n printerName,\n tape,\n autoCut: !opts.nocut,\n log: opts.verbose ? (m: string) => outln(m) : undefined,\n });\n } finally {\n /* Clear it even when the write failed -- a failed open still leaves the\n * phantom behind, and that is precisely when a wedged queue is most\n * confusing to look at. */\n await clearPhantomJobs(printerName, jobsBefore);\n }\n return port;\n}\n\n/**\n * Whether the last direct print was confirmed by the printer. Set by\n * tryPrintDirect; read by the reporting call right after it. A module-level\n * value rather than a return field because the port string is already the\n * caller's success signal and threading a second one through both dispatch\n * sites obscures them.\n */\nlet lastDirectConfirmed = false;\n\n/** Ports that swallow whatever the spooler sends them. */\nconst DISCARD_PORTS = /^(nul|null|nul:|null:)$/i;\n\n/**\n * List the spooler's current job ids for a printer. Best-effort: a failure\n * here must never stop a label printing.\n */\nasync function listJobIds(printerName: string): Promise<Set<number>> {\n try {\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const out = await runScriptOrThrow(\n `@(Get-PrintJob -PrinterName '${printerName.replace(/'/g, \"''\")}' -ErrorAction SilentlyContinue).Id -join ','`\n );\n return new Set(out.trim().split(\",\").map(Number).filter(n => !isNaN(n)));\n } catch {\n return new Set();\n }\n}\n\n/**\n * Remove the empty job Windows invents when we open the printer's port.\n *\n * Writing directly to a serial port that a print queue is bound to makes the\n * spooler materialise a zero-byte job -- proven with a script that only opens\n * the port and writes an initialise sequence. That job then parks itself in\n * Error and drags the queue down with it, which is what made the queue look\n * broken after every successful direct print.\n *\n * The queue has to stay bound to the port: that binding is what establishes\n * and holds the Bluetooth RFCOMM link. Open the port with the queue pointed\n * elsewhere and the link will not come up at all. So the phantom cannot be\n * prevented, only cleared.\n *\n * Scoped by identity, not by size: only jobs that did not exist before we\n * opened the port are removed. Size is no use as a filter -- the spooler\n * records a copy of the bytes we put on the wire, so these jobs are not empty\n * (1205 and 4373 bytes were observed for two ordinary labels).\n *\n * The residual risk is a real job submitted by another program during the\n * second or so a direct print takes. That window is narrow and the alternative\n * -- leaving the queue permanently in Error after every label -- is worse.\n */\nasync function clearPhantomJobs(printerName: string, before: Set<number>): Promise<void> {\n try {\n const { runScriptOrThrow } = await import(\"@bobfrankston/label-core\");\n const name = printerName.replace(/'/g, \"''\");\n const skip = [...before].join(\",\") || \"-1\";\n /* The spooler creates the job around the time the handle closes, so\n * give it a moment to appear before looking, and a moment to act on\n * the delete before returning -- a job caught mid-print lingers in\n * \"Deleting\" otherwise. */\n await runScriptOrThrow(`\n$ErrorActionPreference = 'SilentlyContinue'\n$before = @(${skip})\nStart-Sleep -Milliseconds 900\nGet-PrintJob -PrinterName '${name}' | Where-Object { $_.Id -notin $before } |\n ForEach-Object { Remove-PrintJob -PrinterName '${name}' -ID $_.Id }\nStart-Sleep -Milliseconds 600\n`);\n } catch { /* cosmetic cleanup; never fail a print over it */ }\n}\n\n/**\n * Refuse `-queue` when the printer's Windows queue points at a discard port.\n *\n * Repointing a queue away from the printer's real port is how we stop the\n * spooler from creating a phantom job every time we open that port. The cost\n * is that the queue no longer goes anywhere, so `-queue` must fail loudly\n * rather than cheerfully reporting a label that was never printed.\n */\nasync function refuseDiscardQueue(printerName: string): Promise<void> {\n const name = printerName || getDefaultPrinter();\n if (!name) return;\n const { listPrinters: coreListPrinters } = await import(\"@bobfrankston/label-core\");\n const info = (await coreListPrinters(/brother|^pt-|^ql-/i)).find(p => p.name === name);\n const portName = (info?.portName ?? \"\").replace(/:$/, \"\");\n if (!DISCARD_PORTS.test(portName)) return;\n throw new UserError(\n `${name}'s Windows queue points at \"${info.portName}\", which discards everything sent to it — ` +\n `-queue would report success and print nothing. Drop -queue to print directly, ` +\n `or repoint the queue at the printer's port with: Set-Printer -Name \"${name}\" -PortName \"COMn:\"`\n );\n}\n\n/**\n * Find the serial port to write a P-touch label to.\n *\n * The printer queue's PortName is the obvious source but it goes stale:\n * Windows reassigns a Bluetooth printer's outgoing COM number when it\n * re-pairs (the PT-D610BT here was installed on COM8 and came back on COM10),\n * and the queue keeps pointing at the old one. Worse, the abandoned number is\n * often still a valid *incoming* Bluetooth port with nothing behind it, so the\n * stale queue looks openable and simply swallows the job.\n *\n * So: ask Bluetooth which port actually belongs to this printer, and fall back\n * to the queue's port only when no Bluetooth match exists (a genuinely wired\n * serial printer). Returns null when neither yields a COM port.\n */\nasync function resolvePtPort(printerName: string): Promise<{ port: string; source: string }> {\n const { listPrinters: coreListPrinters, runScriptOrThrow } =\n await import(\"@bobfrankston/label-core\");\n\n /* Bluetooth SPP ports carry the remote device's address in their instance\n * id; BTHPORT maps that address to the advertised device name, which\n * contains the model (e.g. \"PT-D610BT5465\"). Match on the model token. */\n const model = /PT-([A-Z0-9]+)/i.exec(printerName)?.[1];\n if (model) {\n const script = [\n \"$ErrorActionPreference = 'SilentlyContinue'\",\n \"Get-PnpDevice -Class Ports -PresentOnly | Where-Object { $_.FriendlyName -match 'Bluetooth' } | ForEach-Object {\",\n \" if ($_.InstanceId -match '&0&([0-9A-Fa-f]{12})_') {\",\n \" $addr = $matches[1].ToLower()\",\n \" $raw = (Get-ItemProperty \\\"HKLM:\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\BTHPORT\\\\Parameters\\\\Devices\\\\$addr\\\").Name\",\n \" if ($raw -and $_.FriendlyName -match '\\\\((COM\\\\d+)\\\\)') {\",\n \" $com = $matches[1]\",\n \" $name = [System.Text.Encoding]::ASCII.GetString($raw).Trim([char]0)\",\n \" Write-Output \\\"$com=$name\\\"\",\n \" }\",\n \" }\",\n \"}\",\n ].join(\"\\n\");\n try {\n const out = (await runScriptOrThrow(script)).trim();\n for (const line of out.split(/\\r?\\n/)) {\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n const com = line.slice(0, eq).trim();\n const name = line.slice(eq + 1).trim();\n const squashed = name.replace(/[^A-Za-z0-9]/g, \"\").toUpperCase();\n if (com && squashed.includes(model.toUpperCase())) {\n return { port: com, source: `Bluetooth device ${name}` };\n }\n }\n } catch { /* fall through to the queue's port */ }\n }\n\n const info = (await coreListPrinters(/brother|^pt-/i)).find(p => p.name === printerName);\n const m = /^(COM\\d+):?$/i.exec(info?.portName ?? \"\");\n if (m) return { port: m[1], source: `printer queue port ${info.portName}` };\n return null;\n}\n\nfunction classifyInput(input: string, opts: Record<string, string | boolean>): Partial<CorePrintOptions> {\n if (opts.text) return { text: input };\n if (opts.html) return { htmlPath: path.resolve(input) };\n if (opts.image) return { imagePath: path.resolve(input) };\n\n const lower = input.toLowerCase();\n if (lower.endsWith(\".html\") || lower.endsWith(\".htm\")) return { htmlPath: path.resolve(input) };\n if (lower.endsWith(\".txt\")) return { textFile: path.resolve(input) };\n if (/\\.(png|jpg|jpeg|bmp|gif)$/i.test(lower)) return { imagePath: path.resolve(input) };\n if (fs.existsSync(input)) {\n const ext = path.extname(lower);\n if (ext === \".html\" || ext === \".htm\") return { htmlPath: path.resolve(input) };\n return { textFile: path.resolve(input) };\n }\n return { text: input };\n}\n\nfunction makeWaitCallback() {\n let lastReport = -Infinity;\n return (status: PrinterStatus, elapsedMs: number) => {\n const secs = Math.floor(elapsedMs / 1000);\n // Non-TTY (redirected output) can't rewrite in place; report sparsely.\n if (!process.stderr.isTTY) {\n if (secs - lastReport < 30) return;\n lastReport = secs;\n }\n statusLine.update(`[brother-print] waiting for ${status.name} (${status.statusText}${status.error ? \", \" + status.error : \"\"}); ${secs}s`);\n };\n}\n\nmain();\n"]}
|
package/cli.ts
CHANGED
|
@@ -625,7 +625,7 @@ async function cmdPrint(
|
|
|
625
625
|
} else if (segDirectPort = await tryPrintDirect(
|
|
626
626
|
await renderSegments(segments, tape, opts.height as string | undefined, opts.space as string | undefined),
|
|
627
627
|
opts, tape)) {
|
|
628
|
-
reportPrinted(`${segments.length} segment(s) `, opts, tape, segDirectPort);
|
|
628
|
+
reportPrinted(`${segments.length} segment(s) `, opts, tape, segDirectPort, lastDirectConfirmed);
|
|
629
629
|
} else {
|
|
630
630
|
await printSegments(segments, segOpts);
|
|
631
631
|
reportPrinted(`${segments.length} segment(s) `, opts, tape, null);
|
|
@@ -668,8 +668,9 @@ async function cmdPrint(
|
|
|
668
668
|
return;
|
|
669
669
|
}
|
|
670
670
|
const directPort = await tryPrintDirect(await render(printOpts), opts, tape);
|
|
671
|
+
const directConfirmed = lastDirectConfirmed;
|
|
671
672
|
if (directPort) {
|
|
672
|
-
reportPrinted("", opts, tape, directPort);
|
|
673
|
+
reportPrinted("", opts, tape, directPort, directConfirmed);
|
|
673
674
|
return;
|
|
674
675
|
}
|
|
675
676
|
await print(printOpts);
|
|
@@ -699,9 +700,29 @@ function describeTarget(opts: Record<string, string | boolean>, tape: TapeSize,
|
|
|
699
700
|
* another dead job and never reach the tape, which is exactly how labels have
|
|
700
701
|
* been going missing. Say "Queued" for that, and name the printer either way.
|
|
701
702
|
*/
|
|
702
|
-
function reportPrinted(
|
|
703
|
-
|
|
704
|
-
|
|
703
|
+
function reportPrinted(
|
|
704
|
+
what: string,
|
|
705
|
+
opts: Record<string, string | boolean>,
|
|
706
|
+
tape: TapeSize,
|
|
707
|
+
port: string,
|
|
708
|
+
confirmed?: boolean
|
|
709
|
+
): void {
|
|
710
|
+
let verb: string;
|
|
711
|
+
let tail: string;
|
|
712
|
+
if (!port) {
|
|
713
|
+
verb = "Queued";
|
|
714
|
+
tail = " -- the spooler has it; check the queue if nothing comes out";
|
|
715
|
+
} else if (confirmed) {
|
|
716
|
+
verb = "Printed";
|
|
717
|
+
tail = "";
|
|
718
|
+
} else {
|
|
719
|
+
/* The bytes were delivered, and that is all we know. This printer does
|
|
720
|
+
* not report back, so it can still have refused the job -- wrong tape,
|
|
721
|
+
* cover open, cutter jam -- with nothing to tell us. Claiming "Printed"
|
|
722
|
+
* here is what made earlier failures look like successes. */
|
|
723
|
+
verb = "Sent";
|
|
724
|
+
tail = " -- this printer does not confirm, so check the tape";
|
|
725
|
+
}
|
|
705
726
|
outln(`${verb} ${what}on ${describeTarget(opts, tape, port)}${tail}`);
|
|
706
727
|
}
|
|
707
728
|
|
|
@@ -735,11 +756,13 @@ async function tryPrintDirect(
|
|
|
735
756
|
if (!printerName) return null;
|
|
736
757
|
|
|
737
758
|
const { resolvePtModel, printPtDirect } = await import("./ptdirect.js");
|
|
738
|
-
|
|
759
|
+
const model = resolvePtModel(printerName);
|
|
760
|
+
if (!model) return null;
|
|
739
761
|
|
|
740
762
|
const port = (opts.port as string) || (await resolvePtPort(printerName))?.port;
|
|
741
763
|
if (!port) return null;
|
|
742
764
|
|
|
765
|
+
lastDirectConfirmed = model.confirmsPrint;
|
|
743
766
|
const jobsBefore = await listJobIds(printerName);
|
|
744
767
|
try {
|
|
745
768
|
await printPtDirect(png, {
|
|
@@ -758,6 +781,15 @@ async function tryPrintDirect(
|
|
|
758
781
|
return port;
|
|
759
782
|
}
|
|
760
783
|
|
|
784
|
+
/**
|
|
785
|
+
* Whether the last direct print was confirmed by the printer. Set by
|
|
786
|
+
* tryPrintDirect; read by the reporting call right after it. A module-level
|
|
787
|
+
* value rather than a return field because the port string is already the
|
|
788
|
+
* caller's success signal and threading a second one through both dispatch
|
|
789
|
+
* sites obscures them.
|
|
790
|
+
*/
|
|
791
|
+
let lastDirectConfirmed = false;
|
|
792
|
+
|
|
761
793
|
/** Ports that swallow whatever the spooler sends them. */
|
|
762
794
|
const DISCARD_PORTS = /^(nul|null|nul:|null:)$/i;
|
|
763
795
|
|
package/package.json
CHANGED
package/ptdirect.d.ts
CHANGED
|
@@ -36,6 +36,16 @@ interface PtModel {
|
|
|
36
36
|
verified: boolean;
|
|
37
37
|
/** Where the numbers came from, for whoever reads this next. */
|
|
38
38
|
source: string;
|
|
39
|
+
/**
|
|
40
|
+
* Whether this printer will tell us a label actually came out.
|
|
41
|
+
*
|
|
42
|
+
* When false, a successful write means only that the bytes were delivered:
|
|
43
|
+
* the printer can still refuse the job (wrong tape, cover open, cutter jam)
|
|
44
|
+
* with no way for us to learn of it. Callers must not claim "Printed" in
|
|
45
|
+
* that case -- saying so when nothing emerged is the single most misleading
|
|
46
|
+
* thing this tool can do.
|
|
47
|
+
*/
|
|
48
|
+
confirmsPrint: boolean;
|
|
39
49
|
}
|
|
40
50
|
/** Look up the raster profile for a printer queue/driver name, or null. */
|
|
41
51
|
export declare function resolvePtModel(printerName: string): PtModel;
|
package/ptdirect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ptdirect.d.ts","sourceRoot":"","sources":["ptdirect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"ptdirect.d.ts","sourceRoot":"","sources":["ptdirect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAsEzC,kFAAkF;AAClF,UAAU,YAAY;IAClB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,OAAO;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;OAQG;IACH,aAAa,EAAE,OAAO,CAAC;CAC1B;AA8CD,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAG3D;AAID;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,OAAO,EACd,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,OAAO,CAAC,MAAM,EAAE,CAAC,CAiCnB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAyB9C;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CA2DxE;AAID,MAAM,WAAW,WAAW;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACvC,QAAQ,CAAC,WAAW,QAAQ;CAC/B;AA6BD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4CzF;AAID,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC;IAChE,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBrF"}
|
package/ptdirect.js
CHANGED
|
@@ -49,8 +49,11 @@ const ADVANCED = {
|
|
|
49
49
|
highResolution: 0x40,
|
|
50
50
|
};
|
|
51
51
|
const COMPRESSION_TIFF = Buffer.from([0x4d, 0x02]);
|
|
52
|
-
/**
|
|
53
|
-
|
|
52
|
+
/**
|
|
53
|
+
* ESC i z {n2}. Left unspecified because PI_KIND is not set, so the printer
|
|
54
|
+
* does not validate it -- matching Brother's driver, whose sample sends 00.
|
|
55
|
+
*/
|
|
56
|
+
const MEDIA_UNSPECIFIED = 0x00;
|
|
54
57
|
/** The printer's native raster resolution along both axes. */
|
|
55
58
|
const PT_DPI = 180;
|
|
56
59
|
/** Minimum margin the hardware accepts, from the feed-amount table: 2mm. */
|
|
@@ -86,6 +89,7 @@ const MODEL_P710BT = {
|
|
|
86
89
|
tapes: TAPES_128PIN,
|
|
87
90
|
verified: true,
|
|
88
91
|
source: "Brother Raster Command Reference PT-E550W/P750W/P710BT v1.02",
|
|
92
|
+
confirmsPrint: false,
|
|
89
93
|
};
|
|
90
94
|
const MODELS = [
|
|
91
95
|
{ match: /PT-(P710BT|P750W|E550W)/i, model: MODEL_P710BT },
|
|
@@ -99,6 +103,10 @@ const MODELS = [
|
|
|
99
103
|
name: "PT-D610BT (128-pin, unverified)",
|
|
100
104
|
verified: false,
|
|
101
105
|
source: "Assumed identical to the documented 128-pin models; Brother publishes no raster manual for the D610BT",
|
|
106
|
+
/* Proven by experiment: the D610BT ignores ESC i S over Bluetooth
|
|
107
|
+
* SPP, both on a bare connection and after switching to raster
|
|
108
|
+
* mode. Six-second waits, no reply either way. */
|
|
109
|
+
confirmsPrint: false,
|
|
102
110
|
},
|
|
103
111
|
},
|
|
104
112
|
];
|
|
@@ -193,10 +201,18 @@ export function buildPtJob(rasters, opts) {
|
|
|
193
201
|
const { tape, model } = opts;
|
|
194
202
|
const autoCut = opts.autoCut !== false;
|
|
195
203
|
const margin = Math.max(MIN_MARGIN_DOTS, opts.marginDots ?? MIN_MARGIN_DOTS);
|
|
204
|
+
/* Validity flags: RECOVER|WIDTH, exactly what Brother's own driver emits
|
|
205
|
+
* (0x84 in the manual's worked example). Deliberately NOT PI_KIND: the
|
|
206
|
+
* manual warns that whichever of KIND/WIDTH/LENGTH are flagged get checked
|
|
207
|
+
* against the loaded media and the job is refused on a mismatch. We cannot
|
|
208
|
+
* discover the tape TYPE -- this printer does not answer a status request
|
|
209
|
+
* over Bluetooth -- so declaring one only creates a check we may fail for
|
|
210
|
+
* reasons the user cannot see. Width alone is the one thing they can read
|
|
211
|
+
* off the cassette and pass with -tape. */
|
|
196
212
|
const printInfo = Buffer.from([
|
|
197
213
|
0x1b, 0x69, 0x7a,
|
|
198
|
-
PI.recover | PI.width
|
|
199
|
-
opts.mediaType ??
|
|
214
|
+
PI.recover | PI.width,
|
|
215
|
+
opts.mediaType ?? MEDIA_UNSPECIFIED,
|
|
200
216
|
tape,
|
|
201
217
|
0x00, // media length: always 0
|
|
202
218
|
rasters.length & 0xff,
|
package/ptdirect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ptdirect.js","sourceRoot":"","sources":["ptdirect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Direct (spooler-bypass) printing for Brother P-touch printers.\n *\n * The Windows print queue is the source of every failure mode this project has\n * chased: a queue pinned to a stale Bluetooth COM number reports \"online\" and\n * then errors every job; a failed job is left Retained and holds the queue in\n * Error until someone clears it by hand; `WorkOffline` swallows jobs silently.\n * None of that exists if the raster bytes go straight to the port, which is\n * what this module does -- the same shape `ql.ts` already uses over TCP/9100.\n *\n * Protocol source: Brother \"Software Developer's Manual -- Raster Command\n * Reference, PT-E550W/P750W/P710BT, Version 1.02\". The transcribed details and\n * the parts the manual leaves ambiguous are in `.llm/pt-raster-protocol.md`.\n */\n\nimport * as fs from \"fs\";\nimport { createCanvas, loadImage } from \"@napi-rs/canvas\";\nimport type { TapeSize } from \"./api.js\";\n\n/* ----- Commands (all values from the Raster Command Reference) ----- */\n\nconst CMD = {\n invalidate: Buffer.alloc(100, 0),\n initialize: Buffer.from([0x1b, 0x40]),\n statusRequest: Buffer.from([0x1b, 0x69, 0x53]),\n rasterMode: Buffer.from([0x1b, 0x69, 0x61, 0x01]),\n /* ESC i ! {n}: 0 = notify (the printer's default), 1 = do not notify.\n * We never read the status channel, so asking to be notified leaves the\n * printer emitting bytes into a link nobody drains -- which stalls it once\n * its buffer fills, intermittently and unpredictably. Until there is a\n * reader, tell it explicitly not to notify. */\n notifyOff: Buffer.from([0x1b, 0x69, 0x21, 0x01]),\n printLast: Buffer.from([0x1a]), /** Control-Z: print the last page, with feed */\n printPage: Buffer.from([0x0c]), /** FF: print a page that is not the last */\n zeroRaster: Buffer.from([0x5a]), /** Z: blank raster line (TIFF mode only) */\n} as const;\n\n/** ESC i z valid-flag bits. */\nconst PI = {\n kind: 0x02,\n width: 0x04,\n length: 0x08,\n recover: 0x80,\n} as const;\n\n/** ESC i M bits. */\nconst MODE = {\n autoCut: 0x40,\n mirror: 0x80,\n} as const;\n\n/** ESC i K bits. */\nconst ADVANCED = {\n noChainPrint: 0x08,\n noCutSpecialTape: 0x10,\n highResolution: 0x40,\n} as const;\n\nconst COMPRESSION_TIFF = Buffer.from([0x4d, 0x02]);\n\n/** Media type for ESC i z {n2}. */\nconst MEDIA_LAMINATED = 0x01;\n\n/** The printer's native raster resolution along both axes. */\nconst PT_DPI = 180;\n\n/** Minimum margin the hardware accepts, from the feed-amount table: 2mm. */\nconst MIN_MARGIN_DOTS = 14;\n\n/** Attempts to bring up a lazily-established Bluetooth SPP link before giving up. */\nconst OPEN_ATTEMPTS = 6;\nconst OPEN_RETRY_MS = 700;\n\n/**\n * How long to keep a serial handle open after the last write so the data can\n * actually reach the printer. Bluetooth SPP throughput here is around 10 KB/s;\n * the floor covers the printer starting its feed before the link drops.\n */\nfunction drainMs(bytes: number): number {\n return 1200 + Math.ceil(bytes / 10);\n}\n\n/* ----- Model geometry ----- */\n\n/** Unused pins at each end of the head, and printable pins, for one tape size. */\ninterface TapeGeometry {\n /** Pins ignored at each end. Left and right are equal for every TZe size. */\n marginPins: number;\n /** Printable pins across the tape. */\n printPins: number;\n}\n\ninterface PtModel {\n /** Human-readable profile name, used in diagnostics. */\n name: string;\n /** Total pins on the print head. */\n headPins: number;\n /** Bytes per raster line once expanded by the printer. */\n rasterBytes: number;\n tapes: Partial<Record<TapeSize, TapeGeometry>>;\n /**\n * False when the geometry is carried over from a documented sibling model\n * rather than taken from that model's own manual. Callers surface this so\n * a wrong-looking label has an obvious explanation.\n */\n verified: boolean;\n /** Where the numbers came from, for whoever reads this next. */\n source: string;\n}\n\n/**\n * 128-pin geometry, transcribed from the PT-E550W/P750W/P710BT manual\n * (section 2.3.5, \"Raster line\"). Margins are symmetric at every width, so\n * centring the image does not depend on which end of the head pin 0 sits at --\n * which matters, because the manual's own two diagrams disagree about that.\n */\nconst TAPES_128PIN: Partial<Record<TapeSize, TapeGeometry>> = {\n 6: { marginPins: 48, printPins: 32 },\n 9: { marginPins: 39, printPins: 50 },\n 12: { marginPins: 29, printPins: 70 },\n 18: { marginPins: 8, printPins: 112 },\n 24: { marginPins: 0, printPins: 128 },\n};\n\nconst MODEL_P710BT: PtModel = {\n name: \"PT-P710BT (128-pin)\",\n headPins: 128,\n rasterBytes: 16,\n tapes: TAPES_128PIN,\n verified: true,\n source: \"Brother Raster Command Reference PT-E550W/P750W/P710BT v1.02\",\n};\n\nconst MODELS: { match: RegExp; model: PtModel }[] = [\n { match: /PT-(P710BT|P750W|E550W)/i, model: MODEL_P710BT },\n {\n /* The PT-D610BT has no published raster manual. It is a 24mm 180dpi\n * TZe machine, so the 128-pin geometry is the only plausible fit, but\n * \"plausible\" is not \"documented\" -- hence verified:false. */\n match: /PT-D610BT/i,\n model: {\n ...MODEL_P710BT,\n name: \"PT-D610BT (128-pin, unverified)\",\n verified: false,\n source: \"Assumed identical to the documented 128-pin models; Brother publishes no raster manual for the D610BT\",\n },\n },\n];\n\n/** Look up the raster profile for a printer queue/driver name, or null. */\nexport function resolvePtModel(printerName: string): PtModel {\n for (const m of MODELS) if (m.match.test(printerName)) return m.model;\n return null;\n}\n\n/* ----- Raster construction ----- */\n\n/**\n * Resample the rendered PNG to the printer's native grid and slice it into\n * raster lines.\n *\n * The render pipeline produces a landscape image at 300 dpi whose height is\n * the tape's printable width. The printer wants one raster line per column at\n * 180 dpi, each line covering the full head. Both axes are therefore rescaled,\n * and each column becomes `rasterBytes` bytes with the image bits centred in\n * the head by `marginPins`.\n */\nexport async function imageToRasters(\n png: Buffer,\n tape: TapeSize,\n model: PtModel,\n opts: { flip?: boolean; threshold?: number } = {}\n): Promise<Buffer[]> {\n const geom = model.tapes[tape];\n if (!geom) {\n throw new Error(`${model.name} has no geometry for ${tape}mm tape (known: ${Object.keys(model.tapes).join(\", \")}mm)`);\n }\n\n const img = await loadImage(png);\n const rasterCount = Math.max(1, Math.round((img.width / 300) * PT_DPI));\n const canvas = createCanvas(rasterCount, geom.printPins);\n const ctx = canvas.getContext(\"2d\");\n /* White ground: anything the label does not cover must not burn tape. */\n ctx.fillStyle = \"#ffffff\";\n ctx.fillRect(0, 0, rasterCount, geom.printPins);\n ctx.drawImage(img, 0, 0, rasterCount, geom.printPins);\n const { data } = ctx.getImageData(0, 0, rasterCount, geom.printPins);\n\n const threshold = opts.threshold ?? 128;\n const lines: Buffer[] = [];\n for (let x = 0; x < rasterCount; x++) {\n const line = Buffer.alloc(model.rasterBytes, 0);\n for (let y = 0; y < geom.printPins; y++) {\n const i = (y * rasterCount + x) * 4;\n const alpha = data[i + 3];\n /* Luminance; a transparent pixel is background, not black. */\n const lum = alpha === 0 ? 255 : (data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114);\n if (lum >= threshold) continue; // white -> pin off\n const row = opts.flip ? geom.printPins - 1 - y : y;\n const bit = geom.marginPins + row; // centre within the head\n line[bit >> 3] |= 0x80 >> (bit & 7); // MSB first\n }\n lines.push(line);\n }\n return lines;\n}\n\n/**\n * TIFF (PackBits) encoder, byte-oriented, as specified for the `M 02`\n * compression mode: a run of identical bytes becomes `-(len-1)` then the byte;\n * a literal run becomes `len-1` then the bytes.\n */\nexport function packBits(input: Buffer): Buffer {\n const out: number[] = [];\n let i = 0;\n while (i < input.length) {\n let runEnd = i;\n while (runEnd + 1 < input.length && input[runEnd + 1] === input[i] && runEnd - i < 126) runEnd++;\n const runLength = runEnd - i + 1;\n if (runLength >= 2) {\n out.push(256 - (runLength - 1), input[i]); // negative count\n i = runEnd + 1;\n continue;\n }\n /* Literal run: extend until a repeat of 2+ starts or we hit the cap. */\n let litEnd = i;\n while (\n litEnd + 1 < input.length &&\n litEnd - i < 126 &&\n !(litEnd + 2 < input.length && input[litEnd + 1] === input[litEnd + 2])\n ) litEnd++;\n const litLength = litEnd - i + 1;\n out.push(litLength - 1);\n for (let k = 0; k < litLength; k++) out.push(input[i + k]);\n i = litEnd + 1;\n }\n return Buffer.from(out);\n}\n\nexport interface PtJobOptions {\n tape: TapeSize;\n model: PtModel;\n /** Cut the tape after the label. Default true. */\n autoCut?: boolean;\n /** Leading/trailing margin in dots. Clamped to the hardware minimum. */\n marginDots?: number;\n /** Mirror the image (for iron-on / clear tape applied from behind). */\n mirror?: boolean;\n /** ESC i z {n2} media type. Default laminated. */\n mediaType?: number;\n}\n\n/**\n * Assemble the complete byte stream for a one-page job. Pure function -- it\n * touches no transport, so it can be diffed against a driver capture.\n */\nexport function buildPtJob(rasters: Buffer[], opts: PtJobOptions): Buffer {\n const { tape, model } = opts;\n const autoCut = opts.autoCut !== false;\n const margin = Math.max(MIN_MARGIN_DOTS, opts.marginDots ?? MIN_MARGIN_DOTS);\n\n const printInfo = Buffer.from([\n 0x1b, 0x69, 0x7a,\n PI.recover | PI.width | PI.kind,\n opts.mediaType ?? MEDIA_LAMINATED,\n tape,\n 0x00, // media length: always 0\n rasters.length & 0xff,\n (rasters.length >> 8) & 0xff,\n (rasters.length >> 16) & 0xff,\n (rasters.length >> 24) & 0xff,\n 0x00, // first page\n 0x00,\n ]);\n\n const variousMode = Buffer.from([\n 0x1b, 0x69, 0x4d,\n (autoCut ? MODE.autoCut : 0) | (opts.mirror ? MODE.mirror : 0),\n ]);\n /* No chain printing: feed and cut after the last label, so the label can\n * actually be torn off rather than staying attached to the next one. */\n const advancedMode = Buffer.from([0x1b, 0x69, 0x4b, ADVANCED.noChainPrint]);\n const marginCmd = Buffer.from([0x1b, 0x69, 0x64, margin & 0xff, (margin >> 8) & 0xff]);\n\n const parts: Buffer[] = [\n CMD.invalidate,\n CMD.initialize,\n CMD.rasterMode,\n CMD.notifyOff,\n printInfo,\n variousMode,\n advancedMode,\n marginCmd,\n COMPRESSION_TIFF,\n ];\n\n const blank = Buffer.alloc(model.rasterBytes, 0);\n for (const line of rasters) {\n if (line.equals(blank)) {\n parts.push(CMD.zeroRaster);\n continue;\n }\n const packed = packBits(line);\n parts.push(Buffer.from([0x47, packed.length & 0xff, (packed.length >> 8) & 0xff]), packed);\n }\n parts.push(CMD.printLast);\n return Buffer.concat(parts);\n}\n\n/* ----- Transport ----- */\n\nexport interface PtTransport {\n write(data: Buffer): Promise<void>;\n close(): Promise<void>;\n}\n\n/**\n * A transport failure the user can act on -- almost always \"the printer is not\n * switched on\". Carries `isUserError` so the CLI prints one sentence instead of\n * writing a crash report, which is the wrong shape entirely for a sleeping\n * printer.\n */\nexport class PtTransportError extends Error {\n readonly isUserError = true;\n}\n\n/**\n * Turn an fs open failure into something worth reading.\n *\n * Opening a Bluetooth SPP port is what establishes the RFCOMM link, so the\n * failure modes are about the printer, not about the file system. Windows\n * reports an unreachable Bluetooth peer as ERROR_SEM_TIMEOUT (121), which Node\n * surfaces as EPERM -- a code that tells the user nothing at all.\n */\nfunction describeOpenFailure(e: any, port: string, printerName?: string): string {\n const who = printerName ? `${printerName} (${port})` : port;\n switch (e?.code) {\n /* Windows reports an unreachable Bluetooth peer as ERROR_SEM_TIMEOUT\n * (121) or ERROR_DEVICE_NOT_CONNECTED (1167). libuv maps neither to a\n * useful errno: they arrive as EPERM and UNKNOWN respectively. */\n case \"UNKNOWN\":\n case \"EPERM\":\n case \"ETIMEDOUT\":\n return `${who} is not answering after ${OPEN_ATTEMPTS} attempts. For a Bluetooth printer this means the RFCOMM link could not be established — check the printer is on and its Bluetooth indicator is solid rather than flashing, then try again.`;\n case \"EBUSY\":\n return `${who} is already open by another program. Close whatever is holding the port (a stalled print job, a terminal, P-touch Editor) and try again.`;\n case \"ENOENT\":\n return `${who} does not exist. A Bluetooth printer's COM number changes when it re-pairs; run \"brother-print -p <printer>\" to see the port currently assigned to it.`;\n default:\n return `Cannot open ${who}: ${e?.message ?? String(e)}`;\n }\n}\n\n/**\n * Serial / Bluetooth-SPP transport.\n *\n * Windows needs the `\\\\.\\COMn` device form -- the bare `COMn` name only\n * resolves for COM1-COM9, which is exactly the trap a drifting Bluetooth port\n * number walks into. Note that .NET's SerialPort class refuses these Bluetooth\n * ports outright (it calls GetCommState, which the BthModem driver rejects)\n * while a plain open of the device path works; that is why this writes through\n * `fs` rather than through any serial-port library.\n */\nexport async function openSerial(port: string, printerName?: string): Promise<PtTransport> {\n const devPath = process.platform === \"win32\"\n ? (port.startsWith(\"\\\\\\\\\") ? port : \"\\\\\\\\.\\\\\" + port.replace(/:$/, \"\"))\n : port;\n\n /* Opening a Bluetooth SPP port is what establishes the RFCOMM link, and\n * that link is brought up lazily: the first open after an idle period\n * regularly loses the race and comes back ERROR_SEM_TIMEOUT (121) or\n * ERROR_DEVICE_NOT_CONNECTED (1167), both of which Node reports as EPERM.\n * A second attempt a moment later succeeds. This is the documented \"open\n * the port\" step failing to complete, not an error to report -- so retry\n * before giving up, and only then say the printer is not answering. */\n let fd: number;\n let lastError: any;\n for (let attempt = 0; attempt < OPEN_ATTEMPTS; attempt++) {\n try {\n fd = fs.openSync(devPath, \"r+\");\n lastError = null;\n break;\n } catch (e: any) {\n lastError = e;\n if (e?.code === \"EBUSY\") break; // held by someone else; retrying will not help\n await new Promise(r => setTimeout(r, OPEN_RETRY_MS));\n }\n }\n if (lastError) throw new PtTransportError(describeOpenFailure(lastError, port, printerName));\n let written = 0;\n return {\n async write(data: Buffer) {\n let off = 0;\n while (off < data.length) off += fs.writeSync(fd, data, off, data.length - off);\n written += data.length;\n },\n async close() {\n /* writeSync returns once the driver has taken the bytes, not once\n * the printer has them. Closing a Bluetooth SPP handle straight\n * afterwards can discard whatever is still sitting in the RFCOMM\n * buffer, which truncates the job and prints nothing -- and Node\n * offers no way to flush a serial handle. Hold the port open long\n * enough for the data to drain, scaled to how much was sent. */\n await new Promise(r => setTimeout(r, drainMs(written)));\n try { fs.closeSync(fd); } catch { /* already gone; nothing to salvage */ }\n },\n };\n}\n\n/* ----- Top level ----- */\n\nexport interface PtDirectOptions extends Omit<PtJobOptions, \"model\"> {\n /** Serial port to write to, e.g. \"COM10\". */\n port: string;\n /** Printer queue/driver name, used only to pick the raster profile. */\n printerName: string;\n /** Mirror the image vertically. Set once the orientation is confirmed. */\n flip?: boolean;\n log?: (msg: string) => void;\n}\n\n/**\n * Render a PNG straight to a P-touch printer over a serial/Bluetooth port.\n * Resolves once the bytes are on the wire; the printer confirms completion in\n * its status stream, which this does not yet read (see the module notes).\n */\nexport async function printPtDirect(png: Buffer, opts: PtDirectOptions): Promise<void> {\n const model = resolvePtModel(opts.printerName);\n if (!model) {\n throw new Error(`No direct-print profile for \"${opts.printerName}\". Use -queue to print through the Windows spooler instead.`);\n }\n if (!model.verified && opts.log) {\n opts.log(`[brother-label] using unverified raster geometry for ${opts.printerName} (${model.source})`);\n }\n const rasters = await imageToRasters(png, opts.tape, model, { flip: opts.flip });\n const job = buildPtJob(rasters, { ...opts, model });\n if (opts.log) {\n opts.log(`[brother-label] ${opts.printerName}: ${rasters.length} raster lines, ${job.length} bytes -> ${opts.port}`);\n }\n const transport = await openSerial(opts.port, opts.printerName);\n try {\n await transport.write(job);\n } finally {\n await transport.close();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ptdirect.js","sourceRoot":"","sources":["ptdirect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Direct (spooler-bypass) printing for Brother P-touch printers.\n *\n * The Windows print queue is the source of every failure mode this project has\n * chased: a queue pinned to a stale Bluetooth COM number reports \"online\" and\n * then errors every job; a failed job is left Retained and holds the queue in\n * Error until someone clears it by hand; `WorkOffline` swallows jobs silently.\n * None of that exists if the raster bytes go straight to the port, which is\n * what this module does -- the same shape `ql.ts` already uses over TCP/9100.\n *\n * Protocol source: Brother \"Software Developer's Manual -- Raster Command\n * Reference, PT-E550W/P750W/P710BT, Version 1.02\". The transcribed details and\n * the parts the manual leaves ambiguous are in `.llm/pt-raster-protocol.md`.\n */\n\nimport * as fs from \"fs\";\nimport { createCanvas, loadImage } from \"@napi-rs/canvas\";\nimport type { TapeSize } from \"./api.js\";\n\n/* ----- Commands (all values from the Raster Command Reference) ----- */\n\nconst CMD = {\n invalidate: Buffer.alloc(100, 0),\n initialize: Buffer.from([0x1b, 0x40]),\n statusRequest: Buffer.from([0x1b, 0x69, 0x53]),\n rasterMode: Buffer.from([0x1b, 0x69, 0x61, 0x01]),\n /* ESC i ! {n}: 0 = notify (the printer's default), 1 = do not notify.\n * We never read the status channel, so asking to be notified leaves the\n * printer emitting bytes into a link nobody drains -- which stalls it once\n * its buffer fills, intermittently and unpredictably. Until there is a\n * reader, tell it explicitly not to notify. */\n notifyOff: Buffer.from([0x1b, 0x69, 0x21, 0x01]),\n printLast: Buffer.from([0x1a]), /** Control-Z: print the last page, with feed */\n printPage: Buffer.from([0x0c]), /** FF: print a page that is not the last */\n zeroRaster: Buffer.from([0x5a]), /** Z: blank raster line (TIFF mode only) */\n} as const;\n\n/** ESC i z valid-flag bits. */\nconst PI = {\n kind: 0x02,\n width: 0x04,\n length: 0x08,\n recover: 0x80,\n} as const;\n\n/** ESC i M bits. */\nconst MODE = {\n autoCut: 0x40,\n mirror: 0x80,\n} as const;\n\n/** ESC i K bits. */\nconst ADVANCED = {\n noChainPrint: 0x08,\n noCutSpecialTape: 0x10,\n highResolution: 0x40,\n} as const;\n\nconst COMPRESSION_TIFF = Buffer.from([0x4d, 0x02]);\n\n/**\n * ESC i z {n2}. Left unspecified because PI_KIND is not set, so the printer\n * does not validate it -- matching Brother's driver, whose sample sends 00.\n */\nconst MEDIA_UNSPECIFIED = 0x00;\n\n/** The printer's native raster resolution along both axes. */\nconst PT_DPI = 180;\n\n/** Minimum margin the hardware accepts, from the feed-amount table: 2mm. */\nconst MIN_MARGIN_DOTS = 14;\n\n/** Attempts to bring up a lazily-established Bluetooth SPP link before giving up. */\nconst OPEN_ATTEMPTS = 6;\nconst OPEN_RETRY_MS = 700;\n\n/**\n * How long to keep a serial handle open after the last write so the data can\n * actually reach the printer. Bluetooth SPP throughput here is around 10 KB/s;\n * the floor covers the printer starting its feed before the link drops.\n */\nfunction drainMs(bytes: number): number {\n return 1200 + Math.ceil(bytes / 10);\n}\n\n/* ----- Model geometry ----- */\n\n/** Unused pins at each end of the head, and printable pins, for one tape size. */\ninterface TapeGeometry {\n /** Pins ignored at each end. Left and right are equal for every TZe size. */\n marginPins: number;\n /** Printable pins across the tape. */\n printPins: number;\n}\n\ninterface PtModel {\n /** Human-readable profile name, used in diagnostics. */\n name: string;\n /** Total pins on the print head. */\n headPins: number;\n /** Bytes per raster line once expanded by the printer. */\n rasterBytes: number;\n tapes: Partial<Record<TapeSize, TapeGeometry>>;\n /**\n * False when the geometry is carried over from a documented sibling model\n * rather than taken from that model's own manual. Callers surface this so\n * a wrong-looking label has an obvious explanation.\n */\n verified: boolean;\n /** Where the numbers came from, for whoever reads this next. */\n source: string;\n /**\n * Whether this printer will tell us a label actually came out.\n *\n * When false, a successful write means only that the bytes were delivered:\n * the printer can still refuse the job (wrong tape, cover open, cutter jam)\n * with no way for us to learn of it. Callers must not claim \"Printed\" in\n * that case -- saying so when nothing emerged is the single most misleading\n * thing this tool can do.\n */\n confirmsPrint: boolean;\n}\n\n/**\n * 128-pin geometry, transcribed from the PT-E550W/P750W/P710BT manual\n * (section 2.3.5, \"Raster line\"). Margins are symmetric at every width, so\n * centring the image does not depend on which end of the head pin 0 sits at --\n * which matters, because the manual's own two diagrams disagree about that.\n */\nconst TAPES_128PIN: Partial<Record<TapeSize, TapeGeometry>> = {\n 6: { marginPins: 48, printPins: 32 },\n 9: { marginPins: 39, printPins: 50 },\n 12: { marginPins: 29, printPins: 70 },\n 18: { marginPins: 8, printPins: 112 },\n 24: { marginPins: 0, printPins: 128 },\n};\n\nconst MODEL_P710BT: PtModel = {\n name: \"PT-P710BT (128-pin)\",\n headPins: 128,\n rasterBytes: 16,\n tapes: TAPES_128PIN,\n verified: true,\n source: \"Brother Raster Command Reference PT-E550W/P750W/P710BT v1.02\",\n confirmsPrint: false,\n};\n\nconst MODELS: { match: RegExp; model: PtModel }[] = [\n { match: /PT-(P710BT|P750W|E550W)/i, model: MODEL_P710BT },\n {\n /* The PT-D610BT has no published raster manual. It is a 24mm 180dpi\n * TZe machine, so the 128-pin geometry is the only plausible fit, but\n * \"plausible\" is not \"documented\" -- hence verified:false. */\n match: /PT-D610BT/i,\n model: {\n ...MODEL_P710BT,\n name: \"PT-D610BT (128-pin, unverified)\",\n verified: false,\n source: \"Assumed identical to the documented 128-pin models; Brother publishes no raster manual for the D610BT\",\n /* Proven by experiment: the D610BT ignores ESC i S over Bluetooth\n * SPP, both on a bare connection and after switching to raster\n * mode. Six-second waits, no reply either way. */\n confirmsPrint: false,\n },\n },\n];\n\n/** Look up the raster profile for a printer queue/driver name, or null. */\nexport function resolvePtModel(printerName: string): PtModel {\n for (const m of MODELS) if (m.match.test(printerName)) return m.model;\n return null;\n}\n\n/* ----- Raster construction ----- */\n\n/**\n * Resample the rendered PNG to the printer's native grid and slice it into\n * raster lines.\n *\n * The render pipeline produces a landscape image at 300 dpi whose height is\n * the tape's printable width. The printer wants one raster line per column at\n * 180 dpi, each line covering the full head. Both axes are therefore rescaled,\n * and each column becomes `rasterBytes` bytes with the image bits centred in\n * the head by `marginPins`.\n */\nexport async function imageToRasters(\n png: Buffer,\n tape: TapeSize,\n model: PtModel,\n opts: { flip?: boolean; threshold?: number } = {}\n): Promise<Buffer[]> {\n const geom = model.tapes[tape];\n if (!geom) {\n throw new Error(`${model.name} has no geometry for ${tape}mm tape (known: ${Object.keys(model.tapes).join(\", \")}mm)`);\n }\n\n const img = await loadImage(png);\n const rasterCount = Math.max(1, Math.round((img.width / 300) * PT_DPI));\n const canvas = createCanvas(rasterCount, geom.printPins);\n const ctx = canvas.getContext(\"2d\");\n /* White ground: anything the label does not cover must not burn tape. */\n ctx.fillStyle = \"#ffffff\";\n ctx.fillRect(0, 0, rasterCount, geom.printPins);\n ctx.drawImage(img, 0, 0, rasterCount, geom.printPins);\n const { data } = ctx.getImageData(0, 0, rasterCount, geom.printPins);\n\n const threshold = opts.threshold ?? 128;\n const lines: Buffer[] = [];\n for (let x = 0; x < rasterCount; x++) {\n const line = Buffer.alloc(model.rasterBytes, 0);\n for (let y = 0; y < geom.printPins; y++) {\n const i = (y * rasterCount + x) * 4;\n const alpha = data[i + 3];\n /* Luminance; a transparent pixel is background, not black. */\n const lum = alpha === 0 ? 255 : (data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114);\n if (lum >= threshold) continue; // white -> pin off\n const row = opts.flip ? geom.printPins - 1 - y : y;\n const bit = geom.marginPins + row; // centre within the head\n line[bit >> 3] |= 0x80 >> (bit & 7); // MSB first\n }\n lines.push(line);\n }\n return lines;\n}\n\n/**\n * TIFF (PackBits) encoder, byte-oriented, as specified for the `M 02`\n * compression mode: a run of identical bytes becomes `-(len-1)` then the byte;\n * a literal run becomes `len-1` then the bytes.\n */\nexport function packBits(input: Buffer): Buffer {\n const out: number[] = [];\n let i = 0;\n while (i < input.length) {\n let runEnd = i;\n while (runEnd + 1 < input.length && input[runEnd + 1] === input[i] && runEnd - i < 126) runEnd++;\n const runLength = runEnd - i + 1;\n if (runLength >= 2) {\n out.push(256 - (runLength - 1), input[i]); // negative count\n i = runEnd + 1;\n continue;\n }\n /* Literal run: extend until a repeat of 2+ starts or we hit the cap. */\n let litEnd = i;\n while (\n litEnd + 1 < input.length &&\n litEnd - i < 126 &&\n !(litEnd + 2 < input.length && input[litEnd + 1] === input[litEnd + 2])\n ) litEnd++;\n const litLength = litEnd - i + 1;\n out.push(litLength - 1);\n for (let k = 0; k < litLength; k++) out.push(input[i + k]);\n i = litEnd + 1;\n }\n return Buffer.from(out);\n}\n\nexport interface PtJobOptions {\n tape: TapeSize;\n model: PtModel;\n /** Cut the tape after the label. Default true. */\n autoCut?: boolean;\n /** Leading/trailing margin in dots. Clamped to the hardware minimum. */\n marginDots?: number;\n /** Mirror the image (for iron-on / clear tape applied from behind). */\n mirror?: boolean;\n /** ESC i z {n2} media type. Default laminated. */\n mediaType?: number;\n}\n\n/**\n * Assemble the complete byte stream for a one-page job. Pure function -- it\n * touches no transport, so it can be diffed against a driver capture.\n */\nexport function buildPtJob(rasters: Buffer[], opts: PtJobOptions): Buffer {\n const { tape, model } = opts;\n const autoCut = opts.autoCut !== false;\n const margin = Math.max(MIN_MARGIN_DOTS, opts.marginDots ?? MIN_MARGIN_DOTS);\n\n /* Validity flags: RECOVER|WIDTH, exactly what Brother's own driver emits\n * (0x84 in the manual's worked example). Deliberately NOT PI_KIND: the\n * manual warns that whichever of KIND/WIDTH/LENGTH are flagged get checked\n * against the loaded media and the job is refused on a mismatch. We cannot\n * discover the tape TYPE -- this printer does not answer a status request\n * over Bluetooth -- so declaring one only creates a check we may fail for\n * reasons the user cannot see. Width alone is the one thing they can read\n * off the cassette and pass with -tape. */\n const printInfo = Buffer.from([\n 0x1b, 0x69, 0x7a,\n PI.recover | PI.width,\n opts.mediaType ?? MEDIA_UNSPECIFIED,\n tape,\n 0x00, // media length: always 0\n rasters.length & 0xff,\n (rasters.length >> 8) & 0xff,\n (rasters.length >> 16) & 0xff,\n (rasters.length >> 24) & 0xff,\n 0x00, // first page\n 0x00,\n ]);\n\n const variousMode = Buffer.from([\n 0x1b, 0x69, 0x4d,\n (autoCut ? MODE.autoCut : 0) | (opts.mirror ? MODE.mirror : 0),\n ]);\n /* No chain printing: feed and cut after the last label, so the label can\n * actually be torn off rather than staying attached to the next one. */\n const advancedMode = Buffer.from([0x1b, 0x69, 0x4b, ADVANCED.noChainPrint]);\n const marginCmd = Buffer.from([0x1b, 0x69, 0x64, margin & 0xff, (margin >> 8) & 0xff]);\n\n const parts: Buffer[] = [\n CMD.invalidate,\n CMD.initialize,\n CMD.rasterMode,\n CMD.notifyOff,\n printInfo,\n variousMode,\n advancedMode,\n marginCmd,\n COMPRESSION_TIFF,\n ];\n\n const blank = Buffer.alloc(model.rasterBytes, 0);\n for (const line of rasters) {\n if (line.equals(blank)) {\n parts.push(CMD.zeroRaster);\n continue;\n }\n const packed = packBits(line);\n parts.push(Buffer.from([0x47, packed.length & 0xff, (packed.length >> 8) & 0xff]), packed);\n }\n parts.push(CMD.printLast);\n return Buffer.concat(parts);\n}\n\n/* ----- Transport ----- */\n\nexport interface PtTransport {\n write(data: Buffer): Promise<void>;\n close(): Promise<void>;\n}\n\n/**\n * A transport failure the user can act on -- almost always \"the printer is not\n * switched on\". Carries `isUserError` so the CLI prints one sentence instead of\n * writing a crash report, which is the wrong shape entirely for a sleeping\n * printer.\n */\nexport class PtTransportError extends Error {\n readonly isUserError = true;\n}\n\n/**\n * Turn an fs open failure into something worth reading.\n *\n * Opening a Bluetooth SPP port is what establishes the RFCOMM link, so the\n * failure modes are about the printer, not about the file system. Windows\n * reports an unreachable Bluetooth peer as ERROR_SEM_TIMEOUT (121), which Node\n * surfaces as EPERM -- a code that tells the user nothing at all.\n */\nfunction describeOpenFailure(e: any, port: string, printerName?: string): string {\n const who = printerName ? `${printerName} (${port})` : port;\n switch (e?.code) {\n /* Windows reports an unreachable Bluetooth peer as ERROR_SEM_TIMEOUT\n * (121) or ERROR_DEVICE_NOT_CONNECTED (1167). libuv maps neither to a\n * useful errno: they arrive as EPERM and UNKNOWN respectively. */\n case \"UNKNOWN\":\n case \"EPERM\":\n case \"ETIMEDOUT\":\n return `${who} is not answering after ${OPEN_ATTEMPTS} attempts. For a Bluetooth printer this means the RFCOMM link could not be established — check the printer is on and its Bluetooth indicator is solid rather than flashing, then try again.`;\n case \"EBUSY\":\n return `${who} is already open by another program. Close whatever is holding the port (a stalled print job, a terminal, P-touch Editor) and try again.`;\n case \"ENOENT\":\n return `${who} does not exist. A Bluetooth printer's COM number changes when it re-pairs; run \"brother-print -p <printer>\" to see the port currently assigned to it.`;\n default:\n return `Cannot open ${who}: ${e?.message ?? String(e)}`;\n }\n}\n\n/**\n * Serial / Bluetooth-SPP transport.\n *\n * Windows needs the `\\\\.\\COMn` device form -- the bare `COMn` name only\n * resolves for COM1-COM9, which is exactly the trap a drifting Bluetooth port\n * number walks into. Note that .NET's SerialPort class refuses these Bluetooth\n * ports outright (it calls GetCommState, which the BthModem driver rejects)\n * while a plain open of the device path works; that is why this writes through\n * `fs` rather than through any serial-port library.\n */\nexport async function openSerial(port: string, printerName?: string): Promise<PtTransport> {\n const devPath = process.platform === \"win32\"\n ? (port.startsWith(\"\\\\\\\\\") ? port : \"\\\\\\\\.\\\\\" + port.replace(/:$/, \"\"))\n : port;\n\n /* Opening a Bluetooth SPP port is what establishes the RFCOMM link, and\n * that link is brought up lazily: the first open after an idle period\n * regularly loses the race and comes back ERROR_SEM_TIMEOUT (121) or\n * ERROR_DEVICE_NOT_CONNECTED (1167), both of which Node reports as EPERM.\n * A second attempt a moment later succeeds. This is the documented \"open\n * the port\" step failing to complete, not an error to report -- so retry\n * before giving up, and only then say the printer is not answering. */\n let fd: number;\n let lastError: any;\n for (let attempt = 0; attempt < OPEN_ATTEMPTS; attempt++) {\n try {\n fd = fs.openSync(devPath, \"r+\");\n lastError = null;\n break;\n } catch (e: any) {\n lastError = e;\n if (e?.code === \"EBUSY\") break; // held by someone else; retrying will not help\n await new Promise(r => setTimeout(r, OPEN_RETRY_MS));\n }\n }\n if (lastError) throw new PtTransportError(describeOpenFailure(lastError, port, printerName));\n let written = 0;\n return {\n async write(data: Buffer) {\n let off = 0;\n while (off < data.length) off += fs.writeSync(fd, data, off, data.length - off);\n written += data.length;\n },\n async close() {\n /* writeSync returns once the driver has taken the bytes, not once\n * the printer has them. Closing a Bluetooth SPP handle straight\n * afterwards can discard whatever is still sitting in the RFCOMM\n * buffer, which truncates the job and prints nothing -- and Node\n * offers no way to flush a serial handle. Hold the port open long\n * enough for the data to drain, scaled to how much was sent. */\n await new Promise(r => setTimeout(r, drainMs(written)));\n try { fs.closeSync(fd); } catch { /* already gone; nothing to salvage */ }\n },\n };\n}\n\n/* ----- Top level ----- */\n\nexport interface PtDirectOptions extends Omit<PtJobOptions, \"model\"> {\n /** Serial port to write to, e.g. \"COM10\". */\n port: string;\n /** Printer queue/driver name, used only to pick the raster profile. */\n printerName: string;\n /** Mirror the image vertically. Set once the orientation is confirmed. */\n flip?: boolean;\n log?: (msg: string) => void;\n}\n\n/**\n * Render a PNG straight to a P-touch printer over a serial/Bluetooth port.\n * Resolves once the bytes are on the wire; the printer confirms completion in\n * its status stream, which this does not yet read (see the module notes).\n */\nexport async function printPtDirect(png: Buffer, opts: PtDirectOptions): Promise<void> {\n const model = resolvePtModel(opts.printerName);\n if (!model) {\n throw new Error(`No direct-print profile for \"${opts.printerName}\". Use -queue to print through the Windows spooler instead.`);\n }\n if (!model.verified && opts.log) {\n opts.log(`[brother-label] using unverified raster geometry for ${opts.printerName} (${model.source})`);\n }\n const rasters = await imageToRasters(png, opts.tape, model, { flip: opts.flip });\n const job = buildPtJob(rasters, { ...opts, model });\n if (opts.log) {\n opts.log(`[brother-label] ${opts.printerName}: ${rasters.length} raster lines, ${job.length} bytes -> ${opts.port}`);\n }\n const transport = await openSerial(opts.port, opts.printerName);\n try {\n await transport.write(job);\n } finally {\n await transport.close();\n }\n}\n"]}
|
package/ptdirect.ts
CHANGED
|
@@ -58,8 +58,11 @@ const ADVANCED = {
|
|
|
58
58
|
|
|
59
59
|
const COMPRESSION_TIFF = Buffer.from([0x4d, 0x02]);
|
|
60
60
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
61
|
+
/**
|
|
62
|
+
* ESC i z {n2}. Left unspecified because PI_KIND is not set, so the printer
|
|
63
|
+
* does not validate it -- matching Brother's driver, whose sample sends 00.
|
|
64
|
+
*/
|
|
65
|
+
const MEDIA_UNSPECIFIED = 0x00;
|
|
63
66
|
|
|
64
67
|
/** The printer's native raster resolution along both axes. */
|
|
65
68
|
const PT_DPI = 180;
|
|
@@ -106,6 +109,16 @@ interface PtModel {
|
|
|
106
109
|
verified: boolean;
|
|
107
110
|
/** Where the numbers came from, for whoever reads this next. */
|
|
108
111
|
source: string;
|
|
112
|
+
/**
|
|
113
|
+
* Whether this printer will tell us a label actually came out.
|
|
114
|
+
*
|
|
115
|
+
* When false, a successful write means only that the bytes were delivered:
|
|
116
|
+
* the printer can still refuse the job (wrong tape, cover open, cutter jam)
|
|
117
|
+
* with no way for us to learn of it. Callers must not claim "Printed" in
|
|
118
|
+
* that case -- saying so when nothing emerged is the single most misleading
|
|
119
|
+
* thing this tool can do.
|
|
120
|
+
*/
|
|
121
|
+
confirmsPrint: boolean;
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
/**
|
|
@@ -129,6 +142,7 @@ const MODEL_P710BT: PtModel = {
|
|
|
129
142
|
tapes: TAPES_128PIN,
|
|
130
143
|
verified: true,
|
|
131
144
|
source: "Brother Raster Command Reference PT-E550W/P750W/P710BT v1.02",
|
|
145
|
+
confirmsPrint: false,
|
|
132
146
|
};
|
|
133
147
|
|
|
134
148
|
const MODELS: { match: RegExp; model: PtModel }[] = [
|
|
@@ -143,6 +157,10 @@ const MODELS: { match: RegExp; model: PtModel }[] = [
|
|
|
143
157
|
name: "PT-D610BT (128-pin, unverified)",
|
|
144
158
|
verified: false,
|
|
145
159
|
source: "Assumed identical to the documented 128-pin models; Brother publishes no raster manual for the D610BT",
|
|
160
|
+
/* Proven by experiment: the D610BT ignores ESC i S over Bluetooth
|
|
161
|
+
* SPP, both on a bare connection and after switching to raster
|
|
162
|
+
* mode. Six-second waits, no reply either way. */
|
|
163
|
+
confirmsPrint: false,
|
|
146
164
|
},
|
|
147
165
|
},
|
|
148
166
|
];
|
|
@@ -259,10 +277,18 @@ export function buildPtJob(rasters: Buffer[], opts: PtJobOptions): Buffer {
|
|
|
259
277
|
const autoCut = opts.autoCut !== false;
|
|
260
278
|
const margin = Math.max(MIN_MARGIN_DOTS, opts.marginDots ?? MIN_MARGIN_DOTS);
|
|
261
279
|
|
|
280
|
+
/* Validity flags: RECOVER|WIDTH, exactly what Brother's own driver emits
|
|
281
|
+
* (0x84 in the manual's worked example). Deliberately NOT PI_KIND: the
|
|
282
|
+
* manual warns that whichever of KIND/WIDTH/LENGTH are flagged get checked
|
|
283
|
+
* against the loaded media and the job is refused on a mismatch. We cannot
|
|
284
|
+
* discover the tape TYPE -- this printer does not answer a status request
|
|
285
|
+
* over Bluetooth -- so declaring one only creates a check we may fail for
|
|
286
|
+
* reasons the user cannot see. Width alone is the one thing they can read
|
|
287
|
+
* off the cassette and pass with -tape. */
|
|
262
288
|
const printInfo = Buffer.from([
|
|
263
289
|
0x1b, 0x69, 0x7a,
|
|
264
|
-
PI.recover | PI.width
|
|
265
|
-
opts.mediaType ??
|
|
290
|
+
PI.recover | PI.width,
|
|
291
|
+
opts.mediaType ?? MEDIA_UNSPECIFIED,
|
|
266
292
|
tape,
|
|
267
293
|
0x00, // media length: always 0
|
|
268
294
|
rasters.length & 0xff,
|