label-printer 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  > :warning: `label-printer` is still under heavy development and is subject to frequent API changes
4
4
 
5
+
5
6
  This package provides a TypeScript/JavaScript API to:
6
7
 
7
8
  - **Build labels** in a printer-language-independent way
@@ -20,16 +21,16 @@ npm install label-printer
20
21
 
21
22
  The library exposes three main areas:
22
23
 
23
- - **Commands**: `import { Command } from "label-printer"`
24
- - **Labels**: `import { Label } from "label-printer"`
25
- - **Printers**: `import { PrinterService } from "label-printer"`
24
+ - **Commands**: `import { commands } from "label-printer"`
25
+ - **Labels**: `import { labels } from "label-printer"`
26
+ - **Printers**: `import { printers } from "label-printer"`
26
27
 
27
28
  ## Runtime support (Browser vs Node)
28
29
 
29
30
  ### Browser
30
31
 
31
32
  - Uses **WebUSB** to communicate with USB label printers.
32
- - Typical entry point: `PrinterService.requestPrinter()`.
33
+ - Typical entry point: `printers.PrinterService.requestPrinter()`.
33
34
 
34
35
  ### Node.js
35
36
 
@@ -45,31 +46,31 @@ The library exposes three main areas:
45
46
  ### Discover printers
46
47
 
47
48
  ```ts
48
- import { PrinterService } from "label-printer"
49
+ import { printers } from "label-printer"
49
50
 
50
- const printers = await PrinterService.getPrinters()
51
- if(printers.length === 0) {
51
+ const printersList = await printers.PrinterService.getPrinters()
52
+ if(printersList.length === 0) {
52
53
  throw new Error("No printers found")
53
54
  }
54
55
 
55
- const printer = printers[0]
56
+ const printer = printersList[0]
56
57
  ```
57
58
 
58
59
  ### Request a printer (browser-focused)
59
60
 
60
61
  ```ts
61
- import { PrinterService } from "label-printer"
62
+ import { printers } from "label-printer"
62
63
 
63
- const printer = await PrinterService.requestPrinter()
64
+ const printer = await printers.PrinterService.requestPrinter()
64
65
  if(!printer) throw new Error("No printer selected")
65
66
  ```
66
67
 
67
68
  ### Print or display a label
68
69
 
69
70
  ```ts
70
- import { Label } from "label-printer"
71
+ import { labels } from "label-printer"
71
72
 
72
- const label = new Label(50, 25)
73
+ const label = new labels.Label(50, 25)
73
74
  // 1 label, 3mm gap
74
75
  await printer.print(label, 1, 3)
75
76
  // or
@@ -78,15 +79,48 @@ await printer.display(label)
78
79
  await printer.close()
79
80
  ```
80
81
 
81
- ### Direct network usage (Node.js)
82
+ ### Connect directly (bypass discovery)
83
+
84
+ If you already know how to reach your printer (network address or USB identifiers), you can create a printer instance directly.
85
+
86
+ Auto-detect language (recommended default):
87
+
88
+ ```ts
89
+ import { printers } from "label-printer"
90
+
91
+ const printer = await printers.PrinterService.connect({ network: { host: "192.168.100.31" } })
92
+ if(!printer) throw new Error("Printer not found or not supported")
93
+ ```
94
+
95
+ Explicit TSPL (when you know it's a TSPL printer):
96
+
97
+ ```ts
98
+ import { printers } from "label-printer"
99
+
100
+ const printer = await printers.PrinterService.connectTSPL({ network: { host: "192.168.100.31", port: 9100 } })
101
+ if(!printer) throw new Error("Not a TSPL printer")
102
+ ```
103
+
104
+ USB (Node.js - filter without a prompt):
105
+
106
+ ```ts
107
+ import { printers } from "label-printer"
108
+
109
+ const printer = await printers.PrinterService.connect({
110
+ usb: { vendorId: 0x1234, productId: 0x5678, serialNumber: "ABC" }
111
+ })
112
+ if(!printer) throw new Error("Printer not found or not supported")
113
+ ```
82
114
 
83
- If you already know the printer IP and want to bypass discovery:
115
+ USB (Browser - shows a picker; you can optionally filter by `vendorId` / `productId`):
84
116
 
85
117
  ```ts
86
- import TSPLPrinter from "label-printer/dist/printers/TSPLPrinter"
87
- import NetworkDevice from "label-printer/dist/helpers/NetworkDevice"
118
+ import { printers } from "label-printer"
88
119
 
89
- const printer = new TSPLPrinter(new NetworkDevice("192.168.100.31", 9100))
120
+ const printer = await printers.PrinterService.connect({
121
+ usb: { vendorId: 0x1234, productId: 0x5678 }
122
+ })
123
+ if(!printer) throw new Error("No printer selected")
90
124
  ```
91
125
 
92
126
  ## Device abstraction
@@ -94,16 +128,16 @@ const printer = new TSPLPrinter(new NetworkDevice("192.168.100.31", 9100))
94
128
  Commands write to a transport-agnostic `Device` interface. This enables the same printer and label APIs to work over different transports.
95
129
 
96
130
  - **USB** device implementation is internal to `USBUtils`.
97
- - **NetworkDevice** is a TCP implementation used in Node.js.
131
+ - **Network** support uses a TCP implementation in Node.js.
98
132
 
99
133
  ## Label layer
100
134
 
101
135
  The label layer provides a language-independent way to construct labels, which can then be rendered to commands for the chosen printer language.
102
136
 
103
137
  ```ts
104
- import { Label } from "label-printer"
138
+ import { labels } from "label-printer"
105
139
 
106
- const label = new Label(50, 25)
140
+ const label = new labels.Label(50, 25)
107
141
  // label.add(...fields)
108
142
  ```
109
143
 
@@ -113,19 +147,18 @@ const label = new Label(50, 25)
113
147
 
114
148
  ## Fields
115
149
 
116
- Fields live under `label-printer/dist/labels/fields` in the built output.
150
+ Fields are available from the package root export.
117
151
 
118
152
  ### Text
119
153
 
120
154
  Create a text field at (`x`, `y`) in **dots**.
121
155
 
122
156
  ```ts
123
- import { Label } from "label-printer/dist/labels"
124
- import { Text } from "label-printer/dist/labels/fields"
157
+ import { labels } from "label-printer"
125
158
 
126
- const label = new Label(50, 25)
159
+ const label = new labels.Label(50, 25)
127
160
 
128
- const text = new Text("Hello", 20, 20, true)
161
+ const text = new labels.Text("Hello", 20, 20, true)
129
162
  text.setSingleLine(200)
130
163
 
131
164
  label.add(text)
@@ -148,9 +181,9 @@ Formatted text (when `formatted = true`) supports basic tags:
148
181
  Draw a line between two points (values in **dots**).
149
182
 
150
183
  ```ts
151
- import { Line } from "label-printer/dist/labels/fields"
184
+ import { labels } from "label-printer"
152
185
 
153
- label.add(new Line({ x: 10, y: 10 }, { x: 300, y: 10 }, 3))
186
+ label.add(new labels.Line({ x: 10, y: 10 }, { x: 300, y: 10 }, 3))
154
187
  ```
155
188
 
156
189
  ### Image
@@ -159,9 +192,9 @@ Draw a black/white bitmap image. You can either provide a bitmap-like object dir
159
192
  or use the async helper to load/convert an image.
160
193
 
161
194
  ```ts
162
- import { Image } from "label-printer/dist/labels/fields"
195
+ import { labels } from "label-printer"
163
196
 
164
- const img = await Image.create("./logo.png", 10, 60, 200)
197
+ const img = await labels.Image.create("./logo.png", 10, 60, 200)
165
198
  label.add(img)
166
199
  ```
167
200
 
@@ -170,9 +203,9 @@ label.add(img)
170
203
  Draw a barcode (TSPL-backed). Values are in **dots**.
171
204
 
172
205
  ```ts
173
- import { BarCode } from "label-printer/dist/labels/fields"
206
+ import { labels } from "label-printer"
174
207
 
175
- const barcode = new BarCode("123456789", 20, 120, "CODE128", 80)
208
+ const barcode = new labels.BarCode("123456789", 20, 120, "CODE128", 80)
176
209
  barcode.setHumanReadable("bottom")
177
210
  barcode.setRotation(0)
178
211
 
@@ -184,9 +217,9 @@ label.add(barcode)
184
217
  Draw a QR code.
185
218
 
186
219
  ```ts
187
- import { QRCode } from "label-printer/dist/labels/fields"
220
+ import { labels } from "label-printer"
188
221
 
189
- label.add(new QRCode("https://example.com", 20, 220, 6))
222
+ label.add(new labels.QRCode("https://example.com", 20, 220, 6))
190
223
  ```
191
224
 
192
225
  ### Table
@@ -194,12 +227,11 @@ label.add(new QRCode("https://example.com", 20, 220, 6))
194
227
  The `Table` field draws a grid and places text into each cell. It uses the existing `Text` field for cell contents and the existing `Line` field for the grid lines.
195
228
 
196
229
  ```ts
197
- import { Label } from "label-printer/dist/labels"
198
- import { Table } from "label-printer/dist/labels/fields"
230
+ import { labels } from "label-printer"
199
231
 
200
- const label = new Label(50, 25)
232
+ const label = new labels.Label(50, 25)
201
233
 
202
- const table = new Table(10, 10, [
234
+ const table = new labels.Table(10, 10, [
203
235
  ["A1", "A2"],
204
236
  ["B1", "B2"],
205
237
  ], {
@@ -281,9 +313,9 @@ Registering fonts enables better text measurement (for wrapping) and ensures the
281
313
  as part of the generated print/display command sequence.
282
314
 
283
315
  ```ts
284
- import { Label } from "label-printer/dist/labels"
316
+ import { labels } from "label-printer"
285
317
 
286
- const label = new Label(50, 25)
318
+ const label = new labels.Label(50, 25)
287
319
 
288
320
  await label.registerFont({
289
321
  name: "MyFont",
package/dist/index.d.mts CHANGED
@@ -846,6 +846,88 @@ declare abstract class Printer {
846
846
  static try(_device: Device): Promise<boolean>;
847
847
  }
848
848
 
849
+ declare class NetworkDevice implements Device {
850
+ private socket?;
851
+ private readonly host;
852
+ private readonly port;
853
+ private readonly connectTimeoutMs;
854
+ private readonly readTimeoutMs;
855
+ /**
856
+ * Create a TCP-based device.
857
+ *
858
+ * This is intended for raw printing ports (typically 9100). It is Node-only.
859
+ *
860
+ * @param host Hostname or IP
861
+ * @param port TCP port (defaults to 9100)
862
+ * @param connectTimeoutMs Connection timeout
863
+ * @param readTimeoutMs Read timeout used by `readData`/`readString`
864
+ */
865
+ constructor(host: string, port?: number, connectTimeoutMs?: number, readTimeoutMs?: number);
866
+ get opened(): boolean;
867
+ openAndConfigure(): Promise<void>;
868
+ close(): Promise<void>;
869
+ writeData(data: Uint8Array | ArrayBuffer): Promise<void>;
870
+ writeString(text: string): Promise<void>;
871
+ readData(length: number): Promise<DataView | undefined>;
872
+ readString(length: number): Promise<string | undefined>;
873
+ }
874
+
875
+ declare class TSPLPrinter extends Printer {
876
+ get language(): PrinterLanguage;
877
+ feedLabel(): Promise<void>;
878
+ getModelname(): Promise<string>;
879
+ getStatus(): Promise<PrinterStatus>;
880
+ private static statusFor;
881
+ static try(device: Device): Promise<boolean>;
882
+ /**
883
+ * Discover TSPL-capable printers on the local network.
884
+ *
885
+ * Strategy:
886
+ * - Use Bonjour/mDNS to discover "printer-ish" services to obtain a set of candidate hosts.
887
+ * - For each unique host, probe TCP/9100 by sending the TSPL identify command (~!I).
888
+ * - Only return devices that respond to the TSPL probe.
889
+ * - If Bonjour yields no candidates (e.g. mDNS is blocked), fall back to a conservative
890
+ * subnet scan on local private /24 networks (still verified by the same TSPL probe).
891
+ */
892
+ static discoverDevices(): Promise<NetworkDevice[]>;
893
+ /**
894
+ * Fallback discovery mechanism used when Bonjour/mDNS returns no printer candidates.
895
+ *
896
+ * It derives local private IPv4 /24 prefixes from the current machine's network interfaces
897
+ * and probes TCP/9100 using the TSPL identify command.
898
+ *
899
+ * The scan is intentionally conservative:
900
+ * - Limited number of prefixes
901
+ * - Concurrency limits
902
+ * - Total time cap
903
+ * - Early-exit when at least one printer is found
904
+ */
905
+ private static discoverHostsBySubnetScan;
906
+ /**
907
+ * Returns a Node-style `require` function.
908
+ *
909
+ * This is used to keep runtime dependencies Node-only while still allowing the library to
910
+ * be imported/bundled in browser contexts.
911
+ *
912
+ * Tests may inject a custom require implementation via `globalThis.__label_printer_require`.
913
+ */
914
+ private static getNodeRequire;
915
+ }
916
+
917
+ type PrinterServiceUsbConnectOptions = {
918
+ vendorId?: number;
919
+ productId?: number;
920
+ serialNumber?: string;
921
+ };
922
+ type PrinterServiceNetworkConnectOptions = {
923
+ host: string;
924
+ port?: number;
925
+ };
926
+ type PrinterServiceConnectOptions = {
927
+ usb: PrinterServiceUsbConnectOptions;
928
+ } | {
929
+ network: PrinterServiceNetworkConnectOptions;
930
+ };
849
931
  declare class PrinterService {
850
932
  /**
851
933
  * Try each type of printer and return the one that mathces the usb device
@@ -875,6 +957,34 @@ declare class PrinterService {
875
957
  * printer, it may return undefined. In node, use `getPrinters` instead
876
958
  */
877
959
  static requestPrinter(): Promise<Printer | undefined>;
960
+ /**
961
+ * Create a printer instance using explicit connection information.
962
+ *
963
+ * This is useful when you already know your printer's network address or USB identifiers
964
+ * and want to bypass discovery.
965
+ *
966
+ * Behavior differs by environment:
967
+ * - **Node.js**
968
+ * - `network`: connects via TCP (defaults to port `9100`)
969
+ * - `usb`: selects the first connected USB device matching the provided filters
970
+ * - **Browser**
971
+ * - `usb`: shows the WebUSB picker (optionally filtered by `vendorId`/`productId`)
972
+ * - `network`: not supported (will throw if network transport is used in the browser)
973
+ */
974
+ static connect(options: PrinterServiceConnectOptions): Promise<Printer | undefined>;
975
+ /**
976
+ * Create a TSPL printer instance using explicit connection information.
977
+ *
978
+ * This still verifies the target by probing it with the TSPL identify command (`~!I`).
979
+ * If the probe fails, `undefined` is returned.
980
+ */
981
+ static connectTSPL(options: PrinterServiceConnectOptions): Promise<TSPLPrinter | undefined>;
982
+ /**
983
+ * Convert user-facing connect options into a concrete device.
984
+ *
985
+ * This keeps transport details (USB vs TCP socket) out of the public API.
986
+ */
987
+ private static deviceForConnectOptions;
878
988
  }
879
989
 
880
990
  type index_Printer = Printer;
package/dist/index.d.ts CHANGED
@@ -846,6 +846,88 @@ declare abstract class Printer {
846
846
  static try(_device: Device): Promise<boolean>;
847
847
  }
848
848
 
849
+ declare class NetworkDevice implements Device {
850
+ private socket?;
851
+ private readonly host;
852
+ private readonly port;
853
+ private readonly connectTimeoutMs;
854
+ private readonly readTimeoutMs;
855
+ /**
856
+ * Create a TCP-based device.
857
+ *
858
+ * This is intended for raw printing ports (typically 9100). It is Node-only.
859
+ *
860
+ * @param host Hostname or IP
861
+ * @param port TCP port (defaults to 9100)
862
+ * @param connectTimeoutMs Connection timeout
863
+ * @param readTimeoutMs Read timeout used by `readData`/`readString`
864
+ */
865
+ constructor(host: string, port?: number, connectTimeoutMs?: number, readTimeoutMs?: number);
866
+ get opened(): boolean;
867
+ openAndConfigure(): Promise<void>;
868
+ close(): Promise<void>;
869
+ writeData(data: Uint8Array | ArrayBuffer): Promise<void>;
870
+ writeString(text: string): Promise<void>;
871
+ readData(length: number): Promise<DataView | undefined>;
872
+ readString(length: number): Promise<string | undefined>;
873
+ }
874
+
875
+ declare class TSPLPrinter extends Printer {
876
+ get language(): PrinterLanguage;
877
+ feedLabel(): Promise<void>;
878
+ getModelname(): Promise<string>;
879
+ getStatus(): Promise<PrinterStatus>;
880
+ private static statusFor;
881
+ static try(device: Device): Promise<boolean>;
882
+ /**
883
+ * Discover TSPL-capable printers on the local network.
884
+ *
885
+ * Strategy:
886
+ * - Use Bonjour/mDNS to discover "printer-ish" services to obtain a set of candidate hosts.
887
+ * - For each unique host, probe TCP/9100 by sending the TSPL identify command (~!I).
888
+ * - Only return devices that respond to the TSPL probe.
889
+ * - If Bonjour yields no candidates (e.g. mDNS is blocked), fall back to a conservative
890
+ * subnet scan on local private /24 networks (still verified by the same TSPL probe).
891
+ */
892
+ static discoverDevices(): Promise<NetworkDevice[]>;
893
+ /**
894
+ * Fallback discovery mechanism used when Bonjour/mDNS returns no printer candidates.
895
+ *
896
+ * It derives local private IPv4 /24 prefixes from the current machine's network interfaces
897
+ * and probes TCP/9100 using the TSPL identify command.
898
+ *
899
+ * The scan is intentionally conservative:
900
+ * - Limited number of prefixes
901
+ * - Concurrency limits
902
+ * - Total time cap
903
+ * - Early-exit when at least one printer is found
904
+ */
905
+ private static discoverHostsBySubnetScan;
906
+ /**
907
+ * Returns a Node-style `require` function.
908
+ *
909
+ * This is used to keep runtime dependencies Node-only while still allowing the library to
910
+ * be imported/bundled in browser contexts.
911
+ *
912
+ * Tests may inject a custom require implementation via `globalThis.__label_printer_require`.
913
+ */
914
+ private static getNodeRequire;
915
+ }
916
+
917
+ type PrinterServiceUsbConnectOptions = {
918
+ vendorId?: number;
919
+ productId?: number;
920
+ serialNumber?: string;
921
+ };
922
+ type PrinterServiceNetworkConnectOptions = {
923
+ host: string;
924
+ port?: number;
925
+ };
926
+ type PrinterServiceConnectOptions = {
927
+ usb: PrinterServiceUsbConnectOptions;
928
+ } | {
929
+ network: PrinterServiceNetworkConnectOptions;
930
+ };
849
931
  declare class PrinterService {
850
932
  /**
851
933
  * Try each type of printer and return the one that mathces the usb device
@@ -875,6 +957,34 @@ declare class PrinterService {
875
957
  * printer, it may return undefined. In node, use `getPrinters` instead
876
958
  */
877
959
  static requestPrinter(): Promise<Printer | undefined>;
960
+ /**
961
+ * Create a printer instance using explicit connection information.
962
+ *
963
+ * This is useful when you already know your printer's network address or USB identifiers
964
+ * and want to bypass discovery.
965
+ *
966
+ * Behavior differs by environment:
967
+ * - **Node.js**
968
+ * - `network`: connects via TCP (defaults to port `9100`)
969
+ * - `usb`: selects the first connected USB device matching the provided filters
970
+ * - **Browser**
971
+ * - `usb`: shows the WebUSB picker (optionally filtered by `vendorId`/`productId`)
972
+ * - `network`: not supported (will throw if network transport is used in the browser)
973
+ */
974
+ static connect(options: PrinterServiceConnectOptions): Promise<Printer | undefined>;
975
+ /**
976
+ * Create a TSPL printer instance using explicit connection information.
977
+ *
978
+ * This still verifies the target by probing it with the TSPL identify command (`~!I`).
979
+ * If the probe fails, `undefined` is returned.
980
+ */
981
+ static connectTSPL(options: PrinterServiceConnectOptions): Promise<TSPLPrinter | undefined>;
982
+ /**
983
+ * Convert user-facing connect options into a concrete device.
984
+ *
985
+ * This keeps transport details (USB vs TCP socket) out of the public API.
986
+ */
987
+ private static deviceForConnectOptions;
878
988
  }
879
989
 
880
990
  type index_Printer = Printer;
package/dist/index.js CHANGED
@@ -1436,10 +1436,28 @@ var requestDevice = () => __async(null, null, function* () {
1436
1436
  return void 0;
1437
1437
  }
1438
1438
  });
1439
+ var requestDeviceWithFilters = (..._0) => __async(null, [..._0], function* (filters = []) {
1440
+ const agent = yield getUSB();
1441
+ const device = yield agent.requestDevice({ filters });
1442
+ if (device) {
1443
+ return new UsbDevice(device);
1444
+ } else {
1445
+ return void 0;
1446
+ }
1447
+ });
1439
1448
  var UsbDevice = class {
1440
1449
  get opened() {
1441
1450
  return this.device.opened;
1442
1451
  }
1452
+ get vendorId() {
1453
+ return this.device.vendorId;
1454
+ }
1455
+ get productId() {
1456
+ return this.device.productId;
1457
+ }
1458
+ get serialNumber() {
1459
+ return this.device.serialNumber;
1460
+ }
1443
1461
  /**
1444
1462
  * All available endpoints
1445
1463
  */
@@ -2021,6 +2039,69 @@ var PrinterService = class _PrinterService {
2021
2039
  }
2022
2040
  });
2023
2041
  }
2042
+ /**
2043
+ * Create a printer instance using explicit connection information.
2044
+ *
2045
+ * This is useful when you already know your printer's network address or USB identifiers
2046
+ * and want to bypass discovery.
2047
+ *
2048
+ * Behavior differs by environment:
2049
+ * - **Node.js**
2050
+ * - `network`: connects via TCP (defaults to port `9100`)
2051
+ * - `usb`: selects the first connected USB device matching the provided filters
2052
+ * - **Browser**
2053
+ * - `usb`: shows the WebUSB picker (optionally filtered by `vendorId`/`productId`)
2054
+ * - `network`: not supported (will throw if network transport is used in the browser)
2055
+ */
2056
+ static connect(options) {
2057
+ return __async(this, null, function* () {
2058
+ const device = yield _PrinterService.deviceForConnectOptions(options);
2059
+ if (!device) return void 0;
2060
+ return _PrinterService.printerForDevice(device);
2061
+ });
2062
+ }
2063
+ /**
2064
+ * Create a TSPL printer instance using explicit connection information.
2065
+ *
2066
+ * This still verifies the target by probing it with the TSPL identify command (`~!I`).
2067
+ * If the probe fails, `undefined` is returned.
2068
+ */
2069
+ static connectTSPL(options) {
2070
+ return __async(this, null, function* () {
2071
+ const device = yield _PrinterService.deviceForConnectOptions(options);
2072
+ if (!device) return void 0;
2073
+ const ok = yield TSPLPrinter.try(device);
2074
+ if (!ok) return void 0;
2075
+ return new TSPLPrinter(device);
2076
+ });
2077
+ }
2078
+ /**
2079
+ * Convert user-facing connect options into a concrete device.
2080
+ *
2081
+ * This keeps transport details (USB vs TCP socket) out of the public API.
2082
+ */
2083
+ static deviceForConnectOptions(options) {
2084
+ return __async(this, null, function* () {
2085
+ var _a;
2086
+ if ("network" in options) {
2087
+ const port = (_a = options.network.port) != null ? _a : 9100;
2088
+ return new NetworkDevice(options.network.host, port);
2089
+ }
2090
+ const filters = [{ vendorId: options.usb.vendorId, productId: options.usb.productId }].filter((f) => f.vendorId != null || f.productId != null);
2091
+ if (typeof window !== "undefined") {
2092
+ if (filters.length > 0) return requestDeviceWithFilters(filters);
2093
+ return requestDevice();
2094
+ }
2095
+ const devices = yield getDevices();
2096
+ const matching = devices.filter((d) => {
2097
+ if (options.usb.vendorId != null && d.vendorId !== options.usb.vendorId) return false;
2098
+ if (options.usb.productId != null && d.productId !== options.usb.productId) return false;
2099
+ if (options.usb.serialNumber != null && d.serialNumber !== options.usb.serialNumber) return false;
2100
+ return true;
2101
+ });
2102
+ return matching[0];
2103
+ });
2104
+ }
2024
2105
  };
2025
2106
 
2026
2107
  // src/labels/index.ts