quadqr-js 0.7.0 → 1.0.0

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
@@ -11,11 +11,15 @@
11
11
  <p align="center">
12
12
  <a href="https://akanshsirohi.github.io/QuadQR/demo/"><strong>Try the Live Demo</strong></a>
13
13
  ·
14
+ <a href="https://akanshsirohi.github.io/QuadQR/docs-site/"><strong>Documentation Site</strong></a>
15
+ ·
16
+ <a href="https://www.npmjs.com/package/quadqr-js">npm</a>
17
+ ·
14
18
  <a href="#current-benchmark">Benchmark</a>
15
19
  ·
16
20
  <a href="#getting-started">Use the Library</a>
17
21
  ·
18
- <a href="docs/README.md">Documentation</a>
22
+ <a href="docs/README.md">Markdown Docs</a>
19
23
  ·
20
24
  <a href="FORMAT.md">Format Specification</a>
21
25
  </p>
@@ -550,21 +554,21 @@ QuadQR can be used as an npm package, directly from a CDN, or from this reposito
550
554
 
551
555
  ### Requirements
552
556
 
553
- - **Node.js 20.19+** for the published Node/CommonJS entry points
554
- - npm for package workflows
557
+ - **Node.js 20.19+** for Node.js and CommonJS usage
558
+ - npm or another Node package manager for installation
555
559
  - A modern browser for the demo and documentation site
556
560
  - HTTPS or localhost for browser camera access
557
561
 
558
562
  ### Install from npm
559
563
 
560
564
  ```bash
561
- npm install quadqr
565
+ npm install quadqr-js
562
566
  ```
563
567
 
564
568
  Core usage:
565
569
 
566
570
  ```js
567
- import { encodeText, decodeMatrix } from "quadqr";
571
+ import { encodeText, decodeMatrix } from "quadqr-js";
568
572
 
569
573
  const code = encodeText("Hello from QuadQR", { ecc: "M" });
570
574
  const result = decodeMatrix(code.matrix);
@@ -574,8 +578,8 @@ console.log(result.text);
574
578
  Node PNG usage:
575
579
 
576
580
  ```js
577
- import { encodeText } from "quadqr";
578
- import { savePNG, scanFile } from "quadqr/node";
581
+ import { encodeText } from "quadqr-js";
582
+ import { savePNG, scanFile } from "quadqr-js/node";
579
583
 
580
584
  const code = encodeText("Generated on Node.js");
581
585
  await savePNG(code, "quadqr.png", { moduleSize: 12, quietZone: 4 });
@@ -586,10 +590,10 @@ console.log(result.text);
586
590
 
587
591
  ### CDN / script tag
588
592
 
589
- After the package is published to npm, the classic global build can be loaded from npm-backed CDNs:
593
+ The `quadqr-js` package can be loaded directly from npm-backed CDNs:
590
594
 
591
595
  ```html
592
- <script src="https://cdn.jsdelivr.net/npm/quadqr@0.7.0/dist/quadqr.min.js"></script>
596
+ <script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.0.0/dist/quadqr.min.js"></script>
593
597
  <script>
594
598
  const code = QuadQR.encodeText("Hello from a script tag");
595
599
  </script>
@@ -597,7 +601,25 @@ After the package is published to npm, the classic global build can be loaded fr
597
601
 
598
602
  The same file is available through unpkg. Pin an exact version in production.
599
603
 
600
- ### Clone for development
604
+ ### CLI
605
+
606
+ Encode and decode directly from the terminal:
607
+
608
+ ```bash
609
+ npx quadqr-js encode "Hello QuadQR" -o hello.png
610
+ npx quadqr-js decode hello.png
611
+ ```
612
+
613
+ Password-protected payloads use the same commands:
614
+
615
+ ```bash
616
+ npx quadqr-js encode "Private data" --password "my-password" -o secure.png
617
+ npx quadqr-js decode secure.png --password "my-password"
618
+ ```
619
+
620
+ See [`docs/CLI.md`](docs/CLI.md) for all CLI options, including raw 256-bit key mode.
621
+
622
+ ### Run from source
601
623
 
602
624
  ```bash
603
625
  git clone https://github.com/akanshsirohi/QuadQR.git
@@ -611,7 +633,7 @@ Run the complete test suite:
611
633
  npm test
612
634
  ```
613
635
 
614
- Build the npm/CDN distribution and prebuilt WASM asset:
636
+ Build the distributable browser, Node.js, CDN, and WASM files:
615
637
 
616
638
  ```bash
617
639
  npm run build
@@ -635,18 +657,12 @@ Run benchmarks:
635
657
  npm run benchmark
636
658
  ```
637
659
 
638
- Inspect exactly what npm will publish:
639
-
640
- ```bash
641
- npm run pack:check
642
- ```
643
-
644
660
  ### Optional WASM acceleration
645
661
 
646
662
  The package ships a prebuilt WASM helper but never requires it.
647
663
 
648
664
  ```js
649
- import { initWasm } from "quadqr";
665
+ import { initWasm } from "quadqr-js";
650
666
 
651
667
  await initWasm();
652
668
  ```
@@ -659,11 +675,11 @@ If WASM cannot load, the normal JavaScript codec remains available.
659
675
 
660
676
  | Import | Purpose |
661
677
  |---|---|
662
- | `quadqr` | Runtime-neutral core, secure payloads, rendering, scanning, optional WASM |
663
- | `quadqr/browser` | Browser ESM entry |
664
- | `quadqr/node` | Node core plus PNG/file/buffer helpers |
665
- | `quadqr/benchmark` | Capacity and codec benchmark helpers |
666
- | `quadqr/quadqr.min.js` | Classic browser global/CDN bundle |
678
+ | `quadqr-js` | Runtime-neutral core, secure payloads, rendering, scanning, optional WASM |
679
+ | `quadqr-js/browser` | Browser ESM entry |
680
+ | `quadqr-js/node` | Node core plus PNG/file/buffer helpers |
681
+ | `quadqr-js/benchmark` | Capacity and codec benchmark helpers |
682
+ | `quadqr-js/quadqr.min.js` | Classic browser global/CDN bundle |
667
683
 
668
684
  The Node PNG path is dependency-free. For JPEG, WebP, or AVIF input, the Node adapter can use `sharp` when the consuming application already has it installed.
669
685
 
@@ -686,7 +702,7 @@ wasm-src/
686
702
  quadqr_core.c Small portable WASM accelerator source
687
703
 
688
704
  wasm/
689
- quadqr-core.wasm Maintainer build output for direct source usage
705
+ quadqr-core.wasm Source-tree WASM build output
690
706
 
691
707
  dist/
692
708
  index.js ESM package entry
@@ -714,11 +730,11 @@ docs/
714
730
  BROWSER_CDN.md
715
731
  NODE.md
716
732
  SECURITY.md
733
+ CLI.md
717
734
  WASM.md
718
- PUBLISHING.md
719
735
 
720
736
  types/ TypeScript declarations for JavaScript consumers
721
- bin/ `npx quadqr` CLI
737
+ bin/ `quadqr` CLI (`npx quadqr-js`)
722
738
  scripts/ Build, benchmark, and local server scripts
723
739
  tests/ Codec and package distribution tests
724
740
  FORMAT.md Wire-format specification
package/bin/quadqr.js CHANGED
@@ -1,102 +1,102 @@
1
- #!/usr/bin/env node
2
-
3
- import { readFile } from "node:fs/promises";
4
- import process from "node:process";
5
- import {
6
- bytesToHex,
7
- decryptDecoded,
8
- encodeSecureText,
9
- encodeText,
10
- generateRaw256Key
11
- } from "../dist/index.js";
12
- import { savePNG, scanFile } from "../dist/node.js";
13
-
14
- function help() {
15
- console.log(`QuadQR CLI\n\nUsage:\n quadqr encode <text> [-o file.png] [--ecc M] [--version auto|1..40]\n quadqr encode <text> --password <password> [-o file.png]\n quadqr encode <text> --key <64-hex-key> [-o file.png]\n quadqr decode <file.png> [--password <password> | --key <64-hex-key>]\n quadqr keygen\n\nOptions:\n -o, --output <file> Output PNG path (default: quadqr.png)\n --ecc <L|M|Q|H> ECC profile (default: M)\n --version <auto|1..40> Symbol version (default: auto)\n --password <text> Encrypt/decrypt with password mode\n --key <hex> Encrypt/decrypt with raw 256-bit key mode\n --module-size <px> PNG module size (default: 12)\n --quiet-zone <modules> PNG quiet zone (default: 4)\n -h, --help Show help\n`);
16
- }
17
-
18
- function parse(argv) {
19
- const args = [];
20
- const flags = {};
21
- for (let i = 0; i < argv.length; i++) {
22
- const token = argv[i];
23
- if (token === "-h" || token === "--help") flags.help = true;
24
- else if (token === "-o" || token === "--output") flags.output = argv[++i];
25
- else if (token === "--ecc") flags.ecc = argv[++i];
26
- else if (token === "--version") flags.version = argv[++i];
27
- else if (token === "--password") flags.password = argv[++i];
28
- else if (token === "--key") flags.key = argv[++i];
29
- else if (token === "--module-size") flags.moduleSize = Number(argv[++i]);
30
- else if (token === "--quiet-zone") flags.quietZone = Number(argv[++i]);
31
- else args.push(token);
32
- }
33
- return { args, flags };
34
- }
35
-
36
- async function main() {
37
- const { args, flags } = parse(process.argv.slice(2));
38
- if (flags.help || !args.length) {
39
- help();
40
- return;
41
- }
42
-
43
- const command = args.shift();
44
- if (command === "keygen") {
45
- console.log(bytesToHex(generateRaw256Key()));
46
- return;
47
- }
48
-
49
- if (command === "encode") {
50
- const text = args.join(" ");
51
- if (!text) throw new Error("encode requires text.");
52
- if (flags.password && flags.key) throw new Error("Choose password mode or raw-key mode, not both.");
53
-
54
- const options = {
55
- ecc: flags.ecc || "M",
56
- ...(flags.version && flags.version !== "auto" ? { version: Number(flags.version) } : {})
57
- };
58
- const code = flags.password
59
- ? await encodeSecureText(text, { ...options, security: { mode: "password", password: flags.password } })
60
- : flags.key
61
- ? await encodeSecureText(text, { ...options, security: { mode: "raw-key", key: flags.key } })
62
- : encodeText(text, options);
63
-
64
- const output = flags.output || "quadqr.png";
65
- const saved = await savePNG(code, output, {
66
- moduleSize: flags.moduleSize || 12,
67
- quietZone: Number.isFinite(flags.quietZone) ? flags.quietZone : 4
68
- });
69
- console.log(`Saved ${output} (${saved.bytes} bytes, v${code.version}, ${code.size}x${code.size}, ECC ${code.eccLevel}).`);
70
- return;
71
- }
72
-
73
- if (command === "decode") {
74
- const filename = args[0];
75
- if (!filename) throw new Error("decode requires an image filename.");
76
- let result = await scanFile(filename);
77
- if (result.secure) {
78
- if (!flags.password && !flags.key) {
79
- console.log(JSON.stringify({
80
- secure: true,
81
- mode: result.security?.mode,
82
- algorithm: result.security?.algorithm,
83
- keyId: result.security?.keyId || result.security?.keyIdHex || null,
84
- requiresDecryption: true
85
- }, null, 2));
86
- process.exitCode = 2;
87
- return;
88
- }
89
- result = await decryptDecoded(result, flags.password ? { password: flags.password } : { key: flags.key });
90
- }
91
- if (result.text != null) console.log(result.text);
92
- else process.stdout.write(Buffer.from(result.payload));
93
- return;
94
- }
95
-
96
- throw new Error(`Unknown command: ${command}`);
97
- }
98
-
99
- main().catch((error) => {
100
- console.error(`QuadQR: ${error.message}`);
101
- process.exitCode = 1;
102
- });
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from "node:fs/promises";
4
+ import process from "node:process";
5
+ import {
6
+ bytesToHex,
7
+ decryptDecoded,
8
+ encodeSecureText,
9
+ encodeText,
10
+ generateRaw256Key
11
+ } from "../dist/index.js";
12
+ import { savePNG, scanFile } from "../dist/node.js";
13
+
14
+ function help() {
15
+ console.log(`QuadQR CLI\n\nUsage:\n quadqr encode <text> [-o file.png] [--ecc M] [--version auto|1..40]\n quadqr encode <text> --password <password> [-o file.png]\n quadqr encode <text> --key <64-hex-key> [-o file.png]\n quadqr decode <file.png> [--password <password> | --key <64-hex-key>]\n quadqr keygen\n\nOptions:\n -o, --output <file> Output PNG path (default: quadqr.png)\n --ecc <L|M|Q|H> ECC profile (default: M)\n --version <auto|1..40> Symbol version (default: auto)\n --password <text> Encrypt/decrypt with password mode\n --key <hex> Encrypt/decrypt with raw 256-bit key mode\n --module-size <px> PNG module size (default: 12)\n --quiet-zone <modules> PNG quiet zone (default: 4)\n -h, --help Show help\n`);
16
+ }
17
+
18
+ function parse(argv) {
19
+ const args = [];
20
+ const flags = {};
21
+ for (let i = 0; i < argv.length; i++) {
22
+ const token = argv[i];
23
+ if (token === "-h" || token === "--help") flags.help = true;
24
+ else if (token === "-o" || token === "--output") flags.output = argv[++i];
25
+ else if (token === "--ecc") flags.ecc = argv[++i];
26
+ else if (token === "--version") flags.version = argv[++i];
27
+ else if (token === "--password") flags.password = argv[++i];
28
+ else if (token === "--key") flags.key = argv[++i];
29
+ else if (token === "--module-size") flags.moduleSize = Number(argv[++i]);
30
+ else if (token === "--quiet-zone") flags.quietZone = Number(argv[++i]);
31
+ else args.push(token);
32
+ }
33
+ return { args, flags };
34
+ }
35
+
36
+ async function main() {
37
+ const { args, flags } = parse(process.argv.slice(2));
38
+ if (flags.help || !args.length) {
39
+ help();
40
+ return;
41
+ }
42
+
43
+ const command = args.shift();
44
+ if (command === "keygen") {
45
+ console.log(bytesToHex(generateRaw256Key()));
46
+ return;
47
+ }
48
+
49
+ if (command === "encode") {
50
+ const text = args.join(" ");
51
+ if (!text) throw new Error("encode requires text.");
52
+ if (flags.password && flags.key) throw new Error("Choose password mode or raw-key mode, not both.");
53
+
54
+ const options = {
55
+ ecc: flags.ecc || "M",
56
+ ...(flags.version && flags.version !== "auto" ? { version: Number(flags.version) } : {})
57
+ };
58
+ const code = flags.password
59
+ ? await encodeSecureText(text, { ...options, security: { mode: "password", password: flags.password } })
60
+ : flags.key
61
+ ? await encodeSecureText(text, { ...options, security: { mode: "raw-key", key: flags.key } })
62
+ : encodeText(text, options);
63
+
64
+ const output = flags.output || "quadqr.png";
65
+ const saved = await savePNG(code, output, {
66
+ moduleSize: flags.moduleSize || 12,
67
+ quietZone: Number.isFinite(flags.quietZone) ? flags.quietZone : 4
68
+ });
69
+ console.log(`Saved ${output} (${saved.bytes} bytes, v${code.version}, ${code.size}x${code.size}, ECC ${code.eccLevel}).`);
70
+ return;
71
+ }
72
+
73
+ if (command === "decode") {
74
+ const filename = args[0];
75
+ if (!filename) throw new Error("decode requires an image filename.");
76
+ let result = await scanFile(filename);
77
+ if (result.secure) {
78
+ if (!flags.password && !flags.key) {
79
+ console.log(JSON.stringify({
80
+ secure: true,
81
+ mode: result.security?.mode,
82
+ algorithm: result.security?.algorithm,
83
+ keyId: result.security?.keyId || result.security?.keyIdHex || null,
84
+ requiresDecryption: true
85
+ }, null, 2));
86
+ process.exitCode = 2;
87
+ return;
88
+ }
89
+ result = await decryptDecoded(result, flags.password ? { password: flags.password } : { key: flags.key });
90
+ }
91
+ if (result.text != null) console.log(result.text);
92
+ else process.stdout.write(Buffer.from(result.payload));
93
+ return;
94
+ }
95
+
96
+ throw new Error(`Unknown command: ${command}`);
97
+ }
98
+
99
+ main().catch((error) => {
100
+ console.error(`QuadQR: ${error.message}`);
101
+ process.exitCode = 1;
102
+ });