{"_id":"@elchika-inc/moonqr","_rev":"3-8cb0d5f79ffdc3e825ca4c6a1002d172","name":"@elchika-inc/moonqr","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@elchika-inc/moonqr","version":"0.1.0","keywords":["qrcode","qr","decoder","encoder","moonbit","browser"],"license":"Apache-2.0","_id":"@elchika-inc/moonqr@0.1.0","maintainers":[{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"}],"homepage":"https://github.com/elchika-inc/moonqr/tree/main/packages/moonqr#readme","bugs":{"url":"https://github.com/elchika-inc/moonqr/issues"},"dist":{"shasum":"9a049edfda564020e5bf458a7a2b7ab229e03b55","tarball":"https://registry.npmjs.org/@elchika-inc/moonqr/-/moonqr-0.1.0.tgz","fileCount":25,"integrity":"sha512-Kt57SIe9tQq31TVfwSREqipI5t0boygbqwv9SkXyzETv3ZcNNK6mCP3ln72Q1EhJHaBRLQR5BBbZOyxt5Oydpw==","signatures":[{"sig":"MEYCIQDja4hOgXEXxhNrcxy2cTxaRhMPNok3qSmsa4JRUco0WgIhALAB9h4Km8g57PBVUjm78VWoCxtVjSlSSaMekoEiPrLB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":505242},"main":"./dist/index.cjs","type":"module","_from":"file:elchika-inc-moonqr-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.18.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./dom":{"types":"./dist/dom.d.ts","import":"./dist/dom.js","require":"./dist/dom.cjs"},"./decode":{"types":"./dist/decode.d.ts","import":"./dist/decode.js","require":"./dist/decode.cjs"},"./encode":{"types":"./dist/encode.d.ts","import":"./dist/encode.js","require":"./dist/encode.cjs"},"./package.json":"./package.json"},"scripts":{"test":"node --test test/*.test.mjs","build":"tsup","test:unit":"vitest run","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"},"_resolved":"/private/var/folders/hy/dnckvhxs2rjczcpybl990ldw0000gn/T/85bdfa6952c0cc27e28e87b1084b49d9/elchika-inc-moonqr-0.1.0.tgz","_integrity":"sha512-Kt57SIe9tQq31TVfwSREqipI5t0boygbqwv9SkXyzETv3ZcNNK6mCP3ln72Q1EhJHaBRLQR5BBbZOyxt5Oydpw==","repository":{"url":"git+https://github.com/elchika-inc/moonqr.git","type":"git","directory":"packages/moonqr"},"_npmVersion":"11.16.0","description":"QR code encoder and decoder written in MoonBit. No dependencies, works in the browser.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.1","qrcode":"^1.5.4","vitest":"^4.1.10","typescript":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/moonqr_0.1.0_1785227346212_0.9070522545070479","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@elchika-inc/moonqr","version":"0.2.0","keywords":["qrcode","qr","decoder","encoder","moonbit","browser"],"license":"Apache-2.0","_id":"@elchika-inc/moonqr@0.2.0","maintainers":[{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"}],"homepage":"https://github.com/elchika-inc/moonqr/tree/main/packages/moonqr#readme","bugs":{"url":"https://github.com/elchika-inc/moonqr/issues"},"dist":{"shasum":"b68b372a7c8c394ccb1aa026cf9ec7bf761b8134","tarball":"https://registry.npmjs.org/@elchika-inc/moonqr/-/moonqr-0.2.0.tgz","fileCount":25,"integrity":"sha512-rNTal3kTla/2SEa4L6H+MEbcUcIZ1QGEE96l6gNEYyexQma5HYVUrS0GeVvpkCf/tkVrAly7MqiXVTW7ps73sA==","signatures":[{"sig":"MEUCIGmYh/cUSuksHzhuJWlTUnEGpHTvkjQxWOB+3I90chaQAiEA8gmDvkDpziP3z+JWsLesnPUxsF+rDn7U5/iw+4MbFkM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":514968},"main":"./dist/index.cjs","type":"module","_from":"file:elchika-inc-moonqr-0.2.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.18.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./dom":{"types":"./dist/dom.d.ts","import":"./dist/dom.js","require":"./dist/dom.cjs"},"./decode":{"types":"./dist/decode.d.ts","import":"./dist/decode.js","require":"./dist/decode.cjs"},"./encode":{"types":"./dist/encode.d.ts","import":"./dist/encode.js","require":"./dist/encode.cjs"},"./package.json":"./package.json"},"scripts":{"test":"node --test test/*.test.mjs","build":"tsup","test:unit":"vitest run","typecheck":"tsc --noEmit -p tsconfig.json"},"_npmUser":{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"},"_resolved":"/private/var/folders/hy/dnckvhxs2rjczcpybl990ldw0000gn/T/fe92dc28eea1ee3313c9884327f52ec5/elchika-inc-moonqr-0.2.0.tgz","_integrity":"sha512-rNTal3kTla/2SEa4L6H+MEbcUcIZ1QGEE96l6gNEYyexQma5HYVUrS0GeVvpkCf/tkVrAly7MqiXVTW7ps73sA==","repository":{"url":"git+https://github.com/elchika-inc/moonqr.git","type":"git","directory":"packages/moonqr"},"_npmVersion":"11.16.0","description":"QR code encoder and decoder written in MoonBit. No dependencies, works in the browser.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.1","qrcode":"^1.5.4","vitest":"^4.1.10","typescript":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/moonqr_0.2.0_1785310219122_0.4627401603750201","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"_id":"@elchika-inc/moonqr@0.2.1","bugs":{"url":"https://github.com/elchika-inc/moonqr/issues"},"dist":{"shasum":"bb74cfa8559cf52fa5099485983e9646496c2094","tarball":"https://registry.npmjs.org/@elchika-inc/moonqr/-/moonqr-0.2.1.tgz","fileCount":25,"integrity":"sha512-nRQj9Lb3R3qtuKma4QxbwgHa+PXSA6g8IBgssXAHMUwcvHY0q0DSGjL2kPCu+2AOqekM8dZqwrWAChOyxoUSaA==","signatures":[{"sig":"MEUCIQCEZe6RwOIk9tOlPiNOH+bZ0xR8tVQDbhLvd576aunQlgIgF0kKyvmgXlMF+pT3HOrMFlX9SC9Hg54mN+WPwcyl3mU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIG4G0mlKhIWH1v9ksDvzoTEXWtLCShSIcormqoyvw+K2AiEA4lNv1C2wkuyDUH7lday7+OQQ8SHABuuTYiXrxCA/TfY="}],"unpackedSize":517016},"main":"./dist/index.cjs","name":"@elchika-inc/moonqr","type":"module","_from":"file:elchika-inc-moonqr-0.2.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.18.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./dom":{"types":"./dist/dom.d.ts","import":"./dist/dom.js","require":"./dist/dom.cjs"},"./decode":{"types":"./dist/decode.d.ts","import":"./dist/decode.js","require":"./dist/decode.cjs"},"./encode":{"types":"./dist/encode.d.ts","import":"./dist/encode.js","require":"./dist/encode.cjs"},"./package.json":"./package.json"},"license":"Apache-2.0","scripts":{"test":"node --test test/*.test.mjs","build":"tsup","test:unit":"vitest run","typecheck":"tsc --noEmit -p tsconfig.json"},"version":"0.2.1","_npmUser":{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"},"homepage":"https://github.com/elchika-inc/moonqr/tree/main/packages/moonqr#readme","keywords":["qrcode","qr","decoder","encoder","moonbit","browser"],"_resolved":"/private/var/folders/hy/dnckvhxs2rjczcpybl990ldw0000gn/T/364d1f34ee5f7079eb6887cf53ba7aca/elchika-inc-moonqr-0.2.1.tgz","_integrity":"sha512-nRQj9Lb3R3qtuKma4QxbwgHa+PXSA6g8IBgssXAHMUwcvHY0q0DSGjL2kPCu+2AOqekM8dZqwrWAChOyxoUSaA==","repository":{"url":"git+https://github.com/elchika-inc/moonqr.git","type":"git","directory":"packages/moonqr"},"_npmVersion":"11.19.0","description":"QR code encoder and decoder written in MoonBit. No dependencies, works in the browser.","directories":{},"maintainers":[{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^25.0.1","qrcode":"^1.5.4","vitest":"^4.1.10","typescript":"^6.0.3"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/moonqr_0.2.1_1789877364118_0.5914688402100883"}}},"time":{"created":"2026-07-28T08:29:05.997Z","modified":"2026-09-20T04:09:24.393Z","0.1.0":"2026-07-28T08:29:06.351Z","0.2.0":"2026-07-29T07:30:19.290Z","0.2.1":"2026-09-20T04:09:24.206Z"},"bugs":{"url":"https://github.com/elchika-inc/moonqr/issues"},"license":"Apache-2.0","homepage":"https://github.com/elchika-inc/moonqr/tree/main/packages/moonqr#readme","keywords":["qrcode","qr","decoder","encoder","moonbit","browser"],"repository":{"url":"git+https://github.com/elchika-inc/moonqr.git","type":"git","directory":"packages/moonqr"},"description":"QR code encoder and decoder written in MoonBit. No dependencies, works in the browser.","maintainers":[{"name":"naoto24kawa","email":"naoto24kawa@gmail.com"}],"readme":"# @elchika-inc/moonqr\n\nQR code encoder and decoder written in [MoonBit](https://www.moonbitlang.com/), compiled to\nplain JavaScript. No runtime dependencies. Works in Node.js and the browser.\n\n- `encode` / `toSvgString` — text → QR matrix → SVG string.\n- `decode` — RGBA pixels → QR text (locates, extracts, corrects, and decodes; handles rotation,\n  inverted color schemes, and photographed QR codes).\n- `toCanvas` — draw a QR matrix onto an `HTMLCanvasElement`.\n\nFor a live camera scanner built on top of this package, see\n[`@elchika-inc/moonqr-scanner`](../scanner).\n\n## Install\n\n```sh\nnpm install @elchika-inc/moonqr\n```\n\n## Usage\n\n### Encode\n\n```ts\nimport { encode, toSvgString } from \"@elchika-inc/moonqr/encode\";\n\nconst matrix = encode(\"HELLO\", { ecLevel: \"M\" });\nif (matrix) {\n  console.log(matrix.size); // 21 (v1)\n  console.log(matrix.get(0, 0)); // true (top-left finder pattern)\n  const svg = toSvgString(matrix, { margin: 4, cell: 8 });\n}\n```\n\n`encode()` returns `null` on capacity overflow, empty input, or invalid options (`version`\noutside 1..40, `ecLevel` not one of `L`/`M`/`Q`/`H`) — it never throws.\n\n### Decode\n\n```ts\nimport { decode } from \"@elchika-inc/moonqr/decode\";\n\n// data is an RGBA pixel buffer (same shape as ImageData.data — Uint8Array or\n// Uint8ClampedArray both work)\nconst result = decode(imageData.data, imageData.width, imageData.height);\nif (result) {\n  console.log(result.text, result.version, result.ecLevel, result.corners);\n}\n```\n\n`decode()` returns `null` when no QR code is found or the input is invalid (e.g.\n`data.length !== width * height * 4`) — it never throws. `options.invert` (default `true`)\ncontrols whether an inverted-color QR code is also attempted automatically.\n\n### Render to canvas\n\n```ts\nimport { toCanvas } from \"@elchika-inc/moonqr/dom\";\nimport { encode } from \"@elchika-inc/moonqr/encode\";\n\nconst matrix = encode(\"HELLO\")!;\ntoCanvas(matrix, document.querySelector(\"canvas\")!, { margin: 4, cell: 8 });\n```\n\n## Subpath exports (bundle size)\n\n**If bundle size matters, import from a subpath instead of the package root\n(`@elchika-inc/moonqr`).**\n\n| Entry | Purpose | Size (CJS, minified) |\n|---|---|---|\n| `@elchika-inc/moonqr/encode` | Encode only | raw 25.0 KB / gzip 7.5 KB |\n| `@elchika-inc/moonqr/decode` | Decode only | raw 129.6 KB / gzip 49.8 KB |\n| `@elchika-inc/moonqr/dom` | `toCanvas` (canvas rendering) | raw 0.3 KB / gzip 0.2 KB (no MoonBit dependency) |\n| `@elchika-inc/moonqr` | Everything (convenience) | raw 154.7 KB / gzip 57.7 KB |\n\n(Sizes are reproduced by `node scripts/report-bundle-sizes.mjs` at the repo root after\n`pnpm -r build`, and checked in CI on every push.)\n\nThe decoder is large (its Shift-JIS table alone accounts for a good chunk of it) and\n**encode-only consumers should not have to bundle it.** The `encode` subpath is a physically\nseparate output file from `decode`, so it's excluded regardless of how good (or not) your\ndownstream bundler's tree-shaking is.\n\nThe root entry also declares `sideEffects: false`, so a modern bundler *can* drop the unused\nhalf even when importing from the root — but that's not guaranteed. If size is a requirement,\nuse the subpaths.\n\n## API reference\n\n### `encode(text: string, options?: EncodeOptions): QrMatrix | null`\n\n```ts\ninterface EncodeOptions {\n  ecLevel?: \"L\" | \"M\" | \"Q\" | \"H\"; // error correction level, default \"M\"\n  version?: number;                // 1..40; omit to auto-pick the smallest that fits\n}\n\ninterface QrMatrix {\n  readonly size: number;\n  get(x: number, y: number): boolean; // true = dark module. Out-of-range -> false, never throws.\n}\n```\n\n### `toSvgString(matrix: QrMatrix, options?: SvgOptions): string`\n\n```ts\ninterface SvgOptions {\n  margin?: number; // quiet-zone width in modules, default 4\n  cell?: number;    // module side length in px, default 4\n}\n```\n\nPure function, no DOM dependency — works in Node.js too.\n\n### `decode(data: Uint8Array | Uint8ClampedArray, width: number, height: number, options?: DecodeOptions): DecodeResult | null`\n\n```ts\ninterface DecodeOptions {\n  invert?: boolean; // also try inverted color scheme, default true\n}\n\ninterface DecodeResult {\n  text: string;\n  bytes: Uint8Array;                              // raw payload bytes (for binary payloads)\n  version: number;\n  ecLevel: \"L\" | \"M\" | \"Q\" | \"H\";\n  corners: [Point, Point, Point, Point];          // source-image px, TL/TR/BR/BL order\n}\n\ninterface Point { x: number; y: number; }\n```\n\n### `toCanvas(matrix: QrMatrix, canvas: HTMLCanvasElement, options?: SvgOptions): void`\n\n(`@elchika-inc/moonqr/dom` only.) Sets `canvas.width`/`canvas.height` to fit the matrix and\ndraws it (white background, black modules), using the same margin/cell conventions as\n`toSvgString`.\n\n## Correctness\n\n- Decoder: **214/214** exact-text match against jsQR's own end-to-end test corpus (jsQR itself\n  also gets 214/214 on the cases that have ground truth).\n- Encoder: bit-for-bit module match against the [`qrcode`](https://www.npmjs.com/package/qrcode)\n  npm package across all 160 version (1–40) × error-correction-level (L/M/Q/H) combinations.\n\nSee [`bench/RESULT.md`](../../bench/RESULT.md) in the repo root for full methodology and\nperformance numbers (decoding is faster than jsQR on both hit and miss frames).\n\n## Browser support\n\nShips as plain ESM + CJS with no bundler-specific syntax; targets ES2020. Works in any\nevergreen browser and in Node.js 18.18+. `toCanvas` (the `/dom` subpath) requires\n`HTMLCanvasElement` and its 2D context, i.e. a browser or a canvas-polyfilled environment — the\n`encode` and `decode` subpaths themselves have no DOM dependency and run fine in Node.js\n(see the parity/sweep tests under `test/` for examples of decoding from a Node-side pixel\nbuffer).\n\n## License and attribution\n\nApache License 2.0 — see [LICENSE](../../LICENSE) (also bundled in this package's published\ntarball).\n\nPortions of the decoder are ported from [jsQR](https://github.com/cozmo/jsQR) (Apache-2.0), and\nthe Reed–Solomon block / alignment-pattern position tables are derived from\n[qrcode-generator](https://github.com/kazuhikoarase/qrcode-generator) (MIT). See\n[NOTICE](../../NOTICE) and [THIRD_PARTY_LICENSES](../../THIRD_PARTY_LICENSES) (also bundled) for\nthe full attribution and upstream license texts.\n","readmeFilename":"README.md"}