npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pos-hardware-bridge

v1.1.0

Published

Print sharp receipts from any POS: build receipts line by line and send them to POS Hardware Bridge (thermal, kitchen and A4 printers).

Readme

pos-hardware-bridge

Print sharp receipts from any POS (web, Node or Electron) on any ESC/POS or Star thermal printer, kitchen printer or A4 printer, through POS Hardware Bridge.

📖 Documentation: https://thisarahetz.github.io/pos-hardware-bridge/

npm install pos-hardware-bridge
import { BridgeClient, ReceiptBuilder } from 'pos-hardware-bridge';

const bridge = new BridgeClient({ token: '<access token from the bridge settings>' });

const bill = new ReceiptBuilder()
  .center('MY SHOP', { bold: true, size: 'large' })
  .center('12 Main St · 077 123 4567')
  .banner('PAID')
  .rule()
  .row('Bill', '#1042')
  .row('Date', '25 Oct 2026 10:41')
  .rule()
  .item({ name: 'Latte', qty: 2, unitPrice: '4.50', total: '9.00', notes: ['Oat milk'] })
  .item({ name: 'Croissant', qty: 1, total: '3.50' })
  .rule('double')
  .total('TOTAL', 'LKR 1,250.00')
  .qr('https://myshop.example/r/1042', 'Scan for your e-receipt')
  .center('Thank you!', { bold: true });

await bridge.printReceipt(bill);                        // default receipt printer
await bridge.printReceipt(bill, { printer: 'kitchen' }); // a named printer

No dependencies, typed, works with import and require.


Receipts, line by line

A receipt is a list of lines (blocks): text, row, rule, qr and feed. The bridge prints each line with the printer's own commands and fonts — nothing is turned into HTML, a PDF or a picture — and wraps text to the paper: 32 characters on 58mm, 42 or 48 on 80mm. The same receipt prints correctly on every paper width.

| ReceiptBuilder method | Prints | | --- | --- | | text(text, options?) | Text, wrapped at word boundaries. options: align, bold, underline, invert, size, indent | | center(text, options?) | Centred text | | row(left, right, options?) | Label left, value right; a long label wraps beside the value | | item({ name, total, qty?, unitPrice?, notes? }) | 2 x Latte ..... 9.00, then unit price and notes indented | | total(label, value, { emphasis? }) | A bold, double-height total (emphasis: false for a plain row) | | banner(text) | White-on-black label, e.g. PAID, REPRINT | | rule(style?) | ----- (single) or ===== (double) across the paper | | qr(data, label?, size?) | A centred QR code (small/medium/large) | | feed(lines?) | Blank lines | | build() | { blocks } — send it with printReceipt(), or in a print job |

Sizes: normal, wide (double width), tall (double height), large (both).

Preview exactly what prints

layoutReceipt(blocks, columns) is the bridge's own layout code. It returns the lines exactly as the printer prints them, so your on-screen preview always matches the paper:

import { layoutReceipt } from 'pos-hardware-bridge';

const columns = (await bridge.printers()).find((p) => p.id === 'receipt')?.paperWidthChars ?? 42;
for (const line of layoutReceipt(bill.build().blocks, columns)) {
  if (line.kind === 'line') console.log(line.text);   // also: align, bold, size, invert
  if (line.kind === 'qr') console.log(`[QR ${line.data}]`);
}

Rules every POS should follow

  1. Format money yourself and pass strings ('LKR 1,250.00'): the bridge prints what you send.
  2. Don't assume a paper width or pad with spaces — use row() and rule().
  3. Keep the right-hand values short (amounts, dates, references).
  4. Leave the cut to the bridge. Each printer's settings decide full cut, partial cut or tear-off feed.
  5. Pick printers by API name (receipt, kitchen, invoice), not by device. The shop maps names to printers in the bridge.

ReceiptLine receipts (alternative)

The SDK can also write ReceiptLine documents (an open "Markdown for receipts"), which add barcodes and logos. Build them with receipt() and print with bridge.print():

import { receipt } from 'pos-hardware-bridge';

await bridge.print(
  receipt().title('My Shop').row('Bill', '#1042').barcode('1042', { type: 'code128' }),
);

Building ReceiptLine receipts

| Method | Prints | | --- | --- | | title(text, size?) | Big bold centred heading (default large = 2×) | | center(text, style?) | Centred text | | text(text, style?) | A line of text; style: align, bold, underline, invert, size, indent | | banner(text) | White-on-black label, e.g. TAX INVOICE, REPRINT | | row(label, value, style?) | Label left, value right | | item({ name, total, qty?, unitPrice?, notes? }) | An item line, with unit price and notes underneath | | total(label, value, { emphasis? }) | A totals row; emphasis = bold, double height | | divider() | A full-width rule | | qr(data, { cell?, level?, caption? }) | A QR code | | barcode(data, { type?, width?, height?, hri? }) | A barcode (code128 by default) | | image(pngBase64) | A logo | | feed(lines?) | Blank lines | | cut() | Cut here | | raw(receiptLine) | Your own ReceiptLine | | toString() | The ReceiptLine document |

Sizes: normal, wide (double width), tall (double height), large (2×), xlarge (3×). Special characters such as | { } - = ~ _ " ^ are escaped for you, so Coca-Cola | 50% prints as written.

Talking to the bridge

const bridge = new BridgeClient({
  url: 'http://127.0.0.1:9191', // default
  token: '…',                   // bridge settings → Connect your POS
});

| Method | Does | | --- | --- | | printReceipt(receipt, { printer?, copies?, cut?, title? }) | Print a line-by-line receipt (ReceiptBuilder or { blocks }) | | print(receipt, { printer?, copies?, cut?, title? }) | Print a ReceiptLine receipt | | preview(receipt, { printer? }) | { svg, cpl }: a ReceiptLine receipt as it will print, for your screen | | printDocument({ html } \| { pdfBase64 }, { printer?, paperSize?, landscape?, copies? }) | Print an A4/A5 invoice | | printPage(html, { printer?, format?, copies?, title? }) | Print a ready-made HTML bill | | printTestPage(printer?) | The bridge's test page | | printers() | Printers with API names and live status | | status() | Plain-language status to show the cashier | | health() | Is the bridge running? Its id and name (no token needed) | | me() | { kind: 'local' } on the printer computer, or the paired device's name | | BridgeClient.pair({ url, code, name }) | Pair a phone/tablet/till; returns { url, token } to save | | onStatus(listener) | Live updates (printer unplugged, back online); returns stop() |

Errors are BridgeErrors whose message is written for the cashier ("The receipt printer is not connected. Check the cable…"). status holds the HTTP code, details lists bad fields, and unreachable is true when the bridge isn't running.

try {
  await bridge.printReceipt(bill);
} catch (err) {
  showToast(err.message);              // already cashier-friendly
  if (err.unreachable) offerBrowserPrint();
}

A printer menu, with no printer settings in your POS

The bridge owns the printer setup. Your POS just lists what's there:

const printers = await bridge.printers();
// [{ id: 'receipt', name: 'Front counter', kind: 'receipt', isDefault: true, ready: true, status: {…} }, …]
const choices = printers.map((p) => ({ value: p.id, label: `${p.name}${p.ready ? '' : ` (${p.status?.title})`}` }));
await bridge.printReceipt(bill, { printer: chosenId }); // or leave printer out for the default

Printing from tablets and phones

On the shop Wi-Fi: the shop opens Connect your POS → Phones & tablets in the bridge, turns it on and clicks Get a code. Your app pairs once and saves the result:

const { url, token } = await BridgeClient.pair({
  url: 'http://192.168.1.20:9191', // shown in the bridge's Phones & tablets tab
  code: '482 913',                 // shown after "Get a code"
  name: 'Kitchen tablet',
});
const bridge = new BridgeClient({ url, token });

Paired devices can print and read status, not take card payments. Native apps can find bridges with mDNS (_pos-bridge._tcp). A web page served over https:// can't call a local http:// address, so for a hosted web POS use:

Through your backend (any network): implement the print station protocol on your server, pair the bridge with it once, and queue the same receipt (bill.build()) as a print job's receipt.

Full API reference: openapi.yaml, or http://127.0.0.1:9191/docs on any computer running the bridge.

License

MIT. ReceiptLine is © Open Foodservice System Consortium / OpenReceipt Project, Apache-2.0.