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

@qxdev/qrid

v1.1.0

Published

Decode and encode MergeID electronic invoice QR codes

Readme

qrid — Node.js

Decode and encode MergeID electronic invoice QR codes.

TypeScript port of qrid/codec (PHP). Both libraries share the same payload format, function names, and parameter signatures, so QR codes generated by either implementation scan correctly in the other.

Typical usage flow

sequenceDiagram
    actor Staff as Billing Staff
    participant ERP as Billing / ERP System
    participant Lib as qrid
    participant QR as Invoice QR Code
    participant App as MergeID App

    Staff->>ERP: create invoice
    ERP->>Lib: encodeQRId(id, company, email, address, activityCode)
    Lib-->>ERP: SVG QR code
    ERP->>QR: print / embed on invoice

    Note over App,QR: later, at point of scan
    App->>QR: scan with camera
    QR-->>App: base64 payload string
    App->>Lib: decodeQRId(encoded)
    Lib-->>App: { v, id, company, email, address, activity_code }
    App-->>Staff: display verified invoice identity

Installation

# Decode only (no extra dependencies)
npm install @qxdev/qrid
# or: yarn add @qxdev/qrid

# Decode + encode SVG
npm install @qxdev/qrid qrcode
# or: yarn add @qxdev/qrid qrcode

Usage

Decode

import { decodeQRId } from '@qxdev/qrid';

// `encoded` is the raw string value scanned from a MergeID QR code
const payload = decodeQRId(encoded);

console.log(payload.v);             // Payload schema version (number, currently 1)
console.log(payload.id);            // Tax or company ID              (e.g. "3101679980")
console.log(payload.company);       // Company legal name
console.log(payload.email);         // Billing e-mail address
console.log(payload.address);       // Physical address
console.log(payload.activity_code); // Installation / activity code (e.g. "ACT-001"), or "" if blank

decodeQRId trims surrounding whitespace from the input before decoding, so strings copied with accidental padding are handled transparently.

Exceptions thrown:

| Exception | Cause | | --- | --- | | TypeError | Input is not valid base64 | | SyntaxError | Decoded bytes are not valid JSON |

import { decodeQRId } from '@qxdev/qrid';

try {
  const payload = decodeQRId(raw);
} catch (err) {
  if (err instanceof TypeError) {
    // QR data was not base64
  } else if (err instanceof SyntaxError) {
    // QR data decoded but was not the expected JSON structure
  }
}

Encode (requires qrcode)

import { encodeQRId } from '@qxdev/qrid';

const svg = await encodeQRId(
  '3101679980',
  'Acme Corp S.A.',
  '[email protected]',
  '123 Main St, San José, Costa Rica',
  'ACT-001',
);

// Serve as an image
res.setHeader('Content-Type', 'image/svg+xml');
res.send(svg);

activityCode is optional and defaults to '' (blank). A blank activity code signals a consuming system to generate an electronic ticket instead of using an activity code.

Note: encodeQRId is async because the underlying qrcode library uses async rendering. The PHP equivalent (Codec::encodeQRId) is synchronous — this is the only API difference between the two implementations.

Exceptions thrown:

| Exception | Cause | | --- | --- | | Error | qrcode peer dependency is not installed |

Payload format

The QR code data is a UTF-8 JSON object encoded as standard base64 (no line-breaks):

{
  "v": 1,
  "id": "3101679980",
  "company": "Acme Corp S.A.",
  "email": "[email protected]",
  "address": "123 Main St, San José, Costa Rica",
  "activity_code": "ACT-001"
}

| Field | Type | Description | | --- | --- | --- | | v | number | Payload schema version. Currently always 1. | | id | string | Tax / company registration ID. | | company | string | Legal company name (UTF-8, including accented characters). | | email | string | Primary billing or contact e-mail address. | | address | string | Physical address of the company. | | activity_code | string | Installation or activity code that links the QR to an internal record. May be blank (""), which signals a consuming system to generate an electronic ticket instead. |

Requirements

| Dependency | Version | Required for | | --- | --- | --- | | Node.js | >= 18 | Always | | qrcode | ^1.5 | encodeQRId() only |

Running tests

npm install
npm test

Build from source

npm run build   # emits dist/ (JS + .d.ts)

Publishing

Releases to npm are fully automated — there is no manual npm publish step:

  1. Bump version in package.json and merge to main.
  2. release.yml runs on every push to main. It reads the version from package.json; if no vX.Y.Z tag already exists for it, it runs the test suite and creates that tag plus a GitHub Release.
  3. In that same run, release.yml calls publish.yml as a reusable workflow, which re-runs the tests across supported Node versions and publishes to npm using Trusted Publishing (OIDC) with provenance — no stored NPM_TOKEN.

publish.yml is invoked directly as a job (workflow_call) rather than relying on the release: published event, because releases created with the Actions-internal GITHUB_TOKEN don't trigger other workflows via events (GitHub's anti-recursion guard). publish.yml also accepts release: published (for a release cut by hand) and workflow_dispatch (manual re-run) as a fallback.

Pushes to main that don't change the version are a no-op for release.yml (the tag already exists), so unrelated commits (docs, CI tweaks) don't trigger a release.

One-time npm setup (documented here for reference): on the package's Settings pageTrusted Publisher → add a GitHub Actions publisher with owner Quality-XP-Development-SESSA, repository qrid-node, workflow filename publish.yml, and environment name npm; the same npm environment must exist under the repo's GitHub Settings → Environments. Requires npm CLI >= 11.5.1; the publish job uses Node 24 (bundles npm 11.16+) rather than upgrading npm in place, since a global npm install -g npm@latest on GitHub-hosted runners is known to leave a broken install (missing the optional sigstore dependency needed for --provenance).

License

MIT