quadqr-js 0.7.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/FORMAT.md +390 -0
- package/LICENSE +661 -0
- package/README.md +1042 -0
- package/bin/quadqr.js +102 -0
- package/dist/benchmark.cjs +1 -0
- package/dist/benchmark.js +1 -0
- package/dist/browser.js +2 -0
- package/dist/esm/benchmark.js +193 -0
- package/dist/esm/geometry.js +149 -0
- package/dist/esm/node.js +275 -0
- package/dist/esm/quadqr.js +1962 -0
- package/dist/esm/reed-solomon.js +371 -0
- package/dist/esm/security.js +402 -0
- package/dist/esm/vision.js +752 -0
- package/dist/esm/wasm.js +98 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +2 -0
- package/dist/node.cjs +1 -0
- package/dist/node.js +1 -0
- package/dist/quadqr.js +3651 -0
- package/dist/quadqr.min.js +3593 -0
- package/dist/wasm/quadqr-core.wasm +0 -0
- package/docs/API.md +188 -0
- package/docs/BROWSER_CDN.md +83 -0
- package/docs/GETTING_STARTED.md +91 -0
- package/docs/NODE.md +92 -0
- package/docs/PUBLISHING.md +115 -0
- package/docs/README.md +26 -0
- package/docs/SECURITY.md +78 -0
- package/docs/WASM.md +36 -0
- package/package.json +95 -0
- package/types/benchmark.d.ts +7 -0
- package/types/index.d.ts +139 -0
- package/types/node.d.ts +9 -0
|
Binary file
|
package/docs/API.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## Core encoding
|
|
4
|
+
|
|
5
|
+
### `encodeText(text, options?)`
|
|
6
|
+
|
|
7
|
+
Synchronously encodes UTF-8 text.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
const code = encodeText("Hello", {
|
|
11
|
+
ecc: "M",
|
|
12
|
+
version: 5
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If `version` is omitted, QuadQR selects the smallest version that fits.
|
|
17
|
+
|
|
18
|
+
### `encodeBytes(bytes, options?)`
|
|
19
|
+
|
|
20
|
+
Encodes arbitrary bytes.
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
const code = encodeBytes(new Uint8Array([1, 2, 3, 4]), { ecc: "Q" });
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Secure encoding
|
|
27
|
+
|
|
28
|
+
### `encodeSecureText(text, options)`
|
|
29
|
+
|
|
30
|
+
Encrypts text using Secure Payload v1 and then encodes the encrypted envelope.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const code = await encodeSecureText("secret", {
|
|
34
|
+
security: {
|
|
35
|
+
mode: "password",
|
|
36
|
+
password: "example-password"
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `encodeSecureBytes(bytes, options)`
|
|
42
|
+
|
|
43
|
+
The byte-oriented equivalent of `encodeSecureText`.
|
|
44
|
+
|
|
45
|
+
### `decryptDecoded(result, credentials)`
|
|
46
|
+
|
|
47
|
+
Decrypts a result returned by `decodeMatrix`, `scanImageData`, browser scanning, or Node scanning.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const result = await decryptDecoded(locked, { password: "example-password" });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For raw-key mode:
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
const result = await decryptDecoded(locked, { key: raw256BitKey });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Decoding
|
|
60
|
+
|
|
61
|
+
### `decodeMatrix(matrix, options?)`
|
|
62
|
+
|
|
63
|
+
Decodes an already sampled QuadQR matrix.
|
|
64
|
+
|
|
65
|
+
Secure results are intentionally returned in a locked state:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
{
|
|
69
|
+
secure: true,
|
|
70
|
+
requiresDecryption: true,
|
|
71
|
+
payload: Uint8Array(...),
|
|
72
|
+
security: {
|
|
73
|
+
mode: "password",
|
|
74
|
+
algorithm: "AES-256-GCM"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Rendering
|
|
80
|
+
|
|
81
|
+
### `renderToCanvas(codeOrMatrix, canvas, options?)`
|
|
82
|
+
|
|
83
|
+
Browser canvas renderer.
|
|
84
|
+
|
|
85
|
+
### `renderToImageData(codeOrMatrix, options?)`
|
|
86
|
+
|
|
87
|
+
Runtime-neutral RGBA renderer. The result has:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
{
|
|
91
|
+
width,
|
|
92
|
+
height,
|
|
93
|
+
data: Uint8ClampedArray
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Useful render options include:
|
|
98
|
+
|
|
99
|
+
- `moduleSize`
|
|
100
|
+
- `quietZone`
|
|
101
|
+
- `style`: `classic`, `depth`, `soft`, or `inset`
|
|
102
|
+
- custom palette values
|
|
103
|
+
|
|
104
|
+
## Image scanning
|
|
105
|
+
|
|
106
|
+
### `scanImageData(imageData, options?)`
|
|
107
|
+
|
|
108
|
+
Runtime-neutral scanner that accepts RGBA pixels.
|
|
109
|
+
|
|
110
|
+
### Browser `scanFile(file, options?)`
|
|
111
|
+
|
|
112
|
+
Available from `quadqr` and `quadqr/browser`. Accepts a browser `File`/`Blob`.
|
|
113
|
+
|
|
114
|
+
### `scanVideoFrame(video, options?)`
|
|
115
|
+
|
|
116
|
+
Scans the current browser video frame.
|
|
117
|
+
|
|
118
|
+
### `startCameraScanner(video, options?)`
|
|
119
|
+
|
|
120
|
+
Starts a live camera scanner and repeatedly scans frames.
|
|
121
|
+
|
|
122
|
+
## Node helpers
|
|
123
|
+
|
|
124
|
+
Import from `quadqr/node`.
|
|
125
|
+
|
|
126
|
+
### `toPNG(codeOrMatrix, options?)`
|
|
127
|
+
|
|
128
|
+
Returns a Node `Buffer` containing a PNG.
|
|
129
|
+
|
|
130
|
+
### `savePNG(codeOrMatrix, filename, options?)`
|
|
131
|
+
|
|
132
|
+
Writes a PNG file.
|
|
133
|
+
|
|
134
|
+
### `scanBuffer(buffer, options?)`
|
|
135
|
+
|
|
136
|
+
PNG works with no dependencies. Other image formats can use the consuming application's optional `sharp` installation.
|
|
137
|
+
|
|
138
|
+
### Node `scanFile(filename, options?)`
|
|
139
|
+
|
|
140
|
+
Reads an image path and scans it.
|
|
141
|
+
|
|
142
|
+
### `decodePNG(buffer)` / `encodePNG(imageData)`
|
|
143
|
+
|
|
144
|
+
Dependency-free PNG pixel helpers.
|
|
145
|
+
|
|
146
|
+
## Security utilities
|
|
147
|
+
|
|
148
|
+
- `generateRaw256Key()`
|
|
149
|
+
- `normalizeRaw256Key(key)`
|
|
150
|
+
- `bytesToHex(bytes)`
|
|
151
|
+
- `SECURITY_MODES`
|
|
152
|
+
- `SECURITY_ALGORITHMS`
|
|
153
|
+
- `DEFAULT_PBKDF2_ITERATIONS`
|
|
154
|
+
|
|
155
|
+
## WASM
|
|
156
|
+
|
|
157
|
+
### `initWasm(options?)`
|
|
158
|
+
|
|
159
|
+
Loads the bundled optional WASM accelerator and installs it into the normal codec path.
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
import { initWasm } from "quadqr";
|
|
163
|
+
|
|
164
|
+
await initWasm();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `getWasmState()`
|
|
168
|
+
|
|
169
|
+
Returns the active WASM state or `null`.
|
|
170
|
+
|
|
171
|
+
### `disableWasm()`
|
|
172
|
+
|
|
173
|
+
Returns the codec to its JavaScript fallback.
|
|
174
|
+
|
|
175
|
+
## Version/capacity utilities
|
|
176
|
+
|
|
177
|
+
### `getVersionInfo(version, options?)`
|
|
178
|
+
|
|
179
|
+
Returns matrix and payload information for a specific QuadQR version/ECC profile.
|
|
180
|
+
|
|
181
|
+
## Benchmark entry
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
import {
|
|
185
|
+
buildCapacityComparison,
|
|
186
|
+
benchmarkCodec
|
|
187
|
+
} from "quadqr/benchmark";
|
|
188
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Browser and CDN Usage
|
|
2
|
+
|
|
3
|
+
## ESM with a bundler
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import {
|
|
7
|
+
encodeText,
|
|
8
|
+
renderToCanvas,
|
|
9
|
+
scanFile,
|
|
10
|
+
startCameraScanner
|
|
11
|
+
} from "quadqr/browser";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The normal `quadqr` entry also works in modern browser bundlers.
|
|
15
|
+
|
|
16
|
+
## Classic script tag
|
|
17
|
+
|
|
18
|
+
QuadQR publishes a global build that exposes `window.QuadQR` / `globalThis.QuadQR`.
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr@0.7.0/dist/quadqr.min.js"></script>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then:
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<script>
|
|
28
|
+
const code = QuadQR.encodeText("Hello CDN");
|
|
29
|
+
QuadQR.renderToCanvas(code, document.querySelector("#qr"), {
|
|
30
|
+
moduleSize: 12,
|
|
31
|
+
quietZone: 4
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## unpkg
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<script src="https://unpkg.com/quadqr@0.7.0/dist/quadqr.min.js"></script>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## CDN ESM
|
|
43
|
+
|
|
44
|
+
You may also load the ESM file directly:
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<script type="module">
|
|
48
|
+
import { encodeText } from "https://cdn.jsdelivr.net/npm/quadqr@0.7.0/dist/browser.js";
|
|
49
|
+
console.log(encodeText("ES module CDN"));
|
|
50
|
+
</script>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## WASM from a CDN
|
|
54
|
+
|
|
55
|
+
The classic global build remembers its own script URL. Calling:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
await QuadQR.initWasm();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
loads the sibling asset:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
dist/wasm/quadqr-core.wasm
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
from the same package/version URL.
|
|
68
|
+
|
|
69
|
+
You can override the location:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
await QuadQR.initWasm({
|
|
73
|
+
url: "https://example.com/assets/quadqr-core.wasm"
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Camera requirements
|
|
78
|
+
|
|
79
|
+
Live camera access requires a secure browser context, normally HTTPS or localhost.
|
|
80
|
+
|
|
81
|
+
## Production recommendation
|
|
82
|
+
|
|
83
|
+
Pin a concrete version such as `@0.7.0` instead of using an unversioned CDN URL. This prevents a future package release from changing a production site unexpectedly.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
## Install from npm
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install quadqr
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
QuadQR 0.7.x targets Node.js 20.19 or newer for the full ESM/CommonJS distribution behavior.
|
|
10
|
+
|
|
11
|
+
## Generate and decode a matrix
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { encodeText, decodeMatrix } from "quadqr";
|
|
15
|
+
|
|
16
|
+
const code = encodeText("Hello from QuadQR", { ecc: "M" });
|
|
17
|
+
const decoded = decodeMatrix(code.matrix);
|
|
18
|
+
|
|
19
|
+
console.log(decoded.text);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Secure password mode
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { encodeSecureText, decodeMatrix, decryptDecoded } from "quadqr";
|
|
26
|
+
|
|
27
|
+
const code = await encodeSecureText("Private payload", {
|
|
28
|
+
ecc: "M",
|
|
29
|
+
security: {
|
|
30
|
+
mode: "password",
|
|
31
|
+
password: "correct horse battery staple"
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const locked = decodeMatrix(code.matrix);
|
|
36
|
+
const unlocked = await decryptDecoded(locked, {
|
|
37
|
+
password: "correct horse battery staple"
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(unlocked.text);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Raw 256-bit key mode
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import {
|
|
47
|
+
bytesToHex,
|
|
48
|
+
encodeSecureText,
|
|
49
|
+
generateRaw256Key
|
|
50
|
+
} from "quadqr";
|
|
51
|
+
|
|
52
|
+
const key = generateRaw256Key();
|
|
53
|
+
console.log(bytesToHex(key));
|
|
54
|
+
|
|
55
|
+
const code = await encodeSecureText("Machine-readable secret", {
|
|
56
|
+
security: {
|
|
57
|
+
mode: "raw-key",
|
|
58
|
+
key
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Never place the raw encryption key inside the same QuadQR payload.
|
|
64
|
+
|
|
65
|
+
## Node.js PNG
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import { encodeText } from "quadqr";
|
|
69
|
+
import { savePNG, scanFile } from "quadqr/node";
|
|
70
|
+
|
|
71
|
+
const code = encodeText("Generated on Node.js");
|
|
72
|
+
await savePNG(code, "quadqr.png", {
|
|
73
|
+
moduleSize: 12,
|
|
74
|
+
quietZone: 4
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const result = await scanFile("quadqr.png");
|
|
78
|
+
console.log(result.text);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Browser script tag
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr@0.7.0/dist/quadqr.min.js"></script>
|
|
85
|
+
<script>
|
|
86
|
+
const code = QuadQR.encodeText("No build step");
|
|
87
|
+
console.log(code.version, code.matrix);
|
|
88
|
+
</script>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
For production sites, pin the exact package version in the CDN URL.
|
package/docs/NODE.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Node.js
|
|
2
|
+
|
|
3
|
+
QuadQR uses the same matrix codec and image scanner in Node.js and the browser. The `quadqr/node` entry only adds Node-specific file and PNG helpers.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
The package targets Node.js 20.19 or newer.
|
|
8
|
+
|
|
9
|
+
## ESM
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { encodeText } from "quadqr";
|
|
13
|
+
import { savePNG, scanFile } from "quadqr/node";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## CommonJS
|
|
17
|
+
|
|
18
|
+
On supported modern Node versions:
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
const QuadQR = require("quadqr");
|
|
22
|
+
const QuadQRNode = require("quadqr/node");
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Generate PNG
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const code = QuadQR.encodeText("Server generated");
|
|
29
|
+
await QuadQRNode.savePNG(code, "output.png", {
|
|
30
|
+
moduleSize: 12,
|
|
31
|
+
quietZone: 4
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`toPNG()` returns a `Buffer` instead of writing it:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const png = QuadQRNode.toPNG(code);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This is useful for HTTP responses, object storage, email attachments, or database/blob workflows.
|
|
42
|
+
|
|
43
|
+
## Scan PNG
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const result = await QuadQRNode.scanFile("output.png");
|
|
47
|
+
console.log(result.text);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or scan an in-memory buffer:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const result = await QuadQRNode.scanBuffer(pngBuffer);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
PNG decoding is dependency-free and supports normal non-interlaced 8-bit RGB/RGBA PNGs plus indexed PNGs.
|
|
57
|
+
|
|
58
|
+
## JPEG, WebP, and AVIF
|
|
59
|
+
|
|
60
|
+
QuadQR does not force a native image dependency on every user. If your application already uses `sharp`, `scanBuffer()` / `scanFile()` can use it for formats other than PNG.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install sharp
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If `sharp` is not installed, pass decoded RGBA pixels directly to `scanImageData()` or use PNG input.
|
|
67
|
+
|
|
68
|
+
## Secure Node workflow
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import {
|
|
72
|
+
decryptDecoded,
|
|
73
|
+
encodeSecureText
|
|
74
|
+
} from "quadqr";
|
|
75
|
+
import { savePNG, scanFile } from "quadqr/node";
|
|
76
|
+
|
|
77
|
+
const code = await encodeSecureText("private", {
|
|
78
|
+
security: {
|
|
79
|
+
mode: "password",
|
|
80
|
+
password: process.env.QUADQR_PASSWORD
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await savePNG(code, "secure.png");
|
|
85
|
+
|
|
86
|
+
const locked = await scanFile("secure.png");
|
|
87
|
+
const unlocked = await decryptDecoded(locked, {
|
|
88
|
+
password: process.env.QUADQR_PASSWORD
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
console.log(unlocked.text);
|
|
92
|
+
```
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Publishing QuadQR to npm
|
|
2
|
+
|
|
3
|
+
This repository is already configured as a public npm package named `quadqr` at version `0.7.0`.
|
|
4
|
+
|
|
5
|
+
Before the first publish, confirm that the package name is available. If it is already owned by somebody else, use a scoped name such as `@akanshsirohi/quadqr` and update the CDN examples accordingly.
|
|
6
|
+
|
|
7
|
+
## 1. Create/sign in to npm
|
|
8
|
+
|
|
9
|
+
Create an account at https://www.npmjs.com/ and enable two-factor authentication.
|
|
10
|
+
|
|
11
|
+
Then authenticate the CLI:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm login
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Check the active account:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm whoami
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 2. Install repository dependencies
|
|
24
|
+
|
|
25
|
+
QuadQR currently has no runtime dependencies, but run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
so your local package lock and npm metadata are synchronized.
|
|
32
|
+
|
|
33
|
+
## 3. Verify the package name
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm view quadqr
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If npm returns a package owned by somebody else, do not publish over it. Change `package.json` to a scoped name that you own.
|
|
40
|
+
|
|
41
|
+
## 4. Run the full release checks
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm test
|
|
45
|
+
npm run benchmark
|
|
46
|
+
npm run pack:check
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`pack:check` rebuilds `dist/` and displays exactly what npm will include.
|
|
50
|
+
|
|
51
|
+
You can inspect the final tarball too:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm pack
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 5. Publish
|
|
58
|
+
|
|
59
|
+
For the current unscoped public package:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm publish
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For a scoped public package:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm publish --access public
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
npm publishing requires an account that satisfies npm's current 2FA/authentication requirements.
|
|
72
|
+
|
|
73
|
+
## 6. Verify installation from a clean directory
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
mkdir quadqr-consumer-test
|
|
77
|
+
cd quadqr-consumer-test
|
|
78
|
+
npm init -y
|
|
79
|
+
npm install quadqr
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then test ESM, Node PNG, and the CLI.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx quadqr keygen
|
|
86
|
+
npx quadqr encode "Hello from npm" -o hello.png
|
|
87
|
+
npx quadqr decode hello.png
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 7. Verify CDN availability
|
|
91
|
+
|
|
92
|
+
After npm and CDN caches update, test:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
https://cdn.jsdelivr.net/npm/quadqr@0.7.0/dist/quadqr.min.js
|
|
96
|
+
https://unpkg.com/quadqr@0.7.0/dist/quadqr.min.js
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Then call `QuadQR.encodeText(...)` from a plain HTML page.
|
|
100
|
+
|
|
101
|
+
## Releasing the next version
|
|
102
|
+
|
|
103
|
+
Use semantic versioning:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm version patch
|
|
107
|
+
# or
|
|
108
|
+
npm version minor
|
|
109
|
+
# or
|
|
110
|
+
npm version major
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Then run the release checks again and publish.
|
|
114
|
+
|
|
115
|
+
Never reuse an npm version that has already been published. Published package versions are immutable.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# QuadQR Documentation
|
|
2
|
+
|
|
3
|
+
QuadQR is an experimental four-state RGBW matrix symbology. Each data cell carries exactly two bits using red, green, blue, or white. The library adds Spectrum ECC, perspective-aware scanning, optional authenticated encryption, browser camera support, Node.js PNG support, a CDN/global build, and an optional prebuilt WebAssembly accelerator.
|
|
4
|
+
|
|
5
|
+
## Documentation map
|
|
6
|
+
|
|
7
|
+
- [Getting Started](./GETTING_STARTED.md)
|
|
8
|
+
- [API Reference](./API.md)
|
|
9
|
+
- [Browser and CDN](./BROWSER_CDN.md)
|
|
10
|
+
- [Node.js](./NODE.md)
|
|
11
|
+
- [Secure Payloads](./SECURITY.md)
|
|
12
|
+
- [WASM](./WASM.md)
|
|
13
|
+
- [Publishing to npm](./PUBLISHING.md)
|
|
14
|
+
- [Wire Format](../FORMAT.md)
|
|
15
|
+
|
|
16
|
+
## Package entry points
|
|
17
|
+
|
|
18
|
+
| Entry | Purpose |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `quadqr` | Runtime-neutral core API plus optional WASM loader |
|
|
21
|
+
| `quadqr/browser` | Browser ESM entry |
|
|
22
|
+
| `quadqr/node` | Node.js core plus PNG/file helpers |
|
|
23
|
+
| `quadqr/benchmark` | Capacity and codec benchmark helpers |
|
|
24
|
+
| `quadqr/quadqr.min.js` | Classic browser global bundle for CDNs |
|
|
25
|
+
|
|
26
|
+
The package is designed so the matrix codec is shared across all runtimes. Browser and Node adapters only handle runtime-specific input/output.
|
package/docs/SECURITY.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Secure Payloads
|
|
2
|
+
|
|
3
|
+
QuadQR Secure Payload v1 is an optional layer above the matrix codec and Spectrum ECC.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
plaintext
|
|
7
|
+
-> encryption/authentication
|
|
8
|
+
-> secure envelope
|
|
9
|
+
-> QuadQR framing
|
|
10
|
+
-> Spectrum ECC
|
|
11
|
+
-> RGBW matrix
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Scanning and error correction therefore remain independent from encryption.
|
|
15
|
+
|
|
16
|
+
## Password mode
|
|
17
|
+
|
|
18
|
+
Password mode uses:
|
|
19
|
+
|
|
20
|
+
- PBKDF2-HMAC-SHA-256
|
|
21
|
+
- random 16-byte salt
|
|
22
|
+
- configurable iteration count, default 600,000
|
|
23
|
+
- AES-256-GCM
|
|
24
|
+
- random 12-byte nonce
|
|
25
|
+
- authenticated metadata
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const code = await encodeSecureText("private", {
|
|
29
|
+
security: {
|
|
30
|
+
mode: "password",
|
|
31
|
+
password: "long unique password"
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The password is never stored in the QuadQR symbol.
|
|
37
|
+
|
|
38
|
+
## Raw 256-bit key mode
|
|
39
|
+
|
|
40
|
+
Raw-key mode accepts exactly 32 random bytes.
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
const key = generateRaw256Key();
|
|
44
|
+
|
|
45
|
+
const code = await encodeSecureText("machine secret", {
|
|
46
|
+
security: {
|
|
47
|
+
mode: "raw-key",
|
|
48
|
+
key
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
By default QuadQR stores only a short SHA-256 fingerprint/key ID so an application can identify the required key without embedding the actual secret.
|
|
54
|
+
|
|
55
|
+
## Decryption
|
|
56
|
+
|
|
57
|
+
A secure scan stays locked:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
const locked = scanImageData(imageData);
|
|
61
|
+
|
|
62
|
+
if (locked.secure) {
|
|
63
|
+
const unlocked = await decryptDecoded(locked, {
|
|
64
|
+
password: "long unique password"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
AES-GCM authentication rejects an incorrect password/key or modified ciphertext.
|
|
70
|
+
|
|
71
|
+
## Threat model notes
|
|
72
|
+
|
|
73
|
+
- Secure Payload protects payload confidentiality and integrity.
|
|
74
|
+
- It does not hide the fact that a QuadQR symbol exists.
|
|
75
|
+
- Key IDs are routing hints, not secrets.
|
|
76
|
+
- A photographed code can still be copied, so encryption alone is not anti-replay protection.
|
|
77
|
+
- Applications needing ticket/access anti-replay should combine QuadQR with server-side nonce/state validation or signed short-lived tokens.
|
|
78
|
+
- Do not hard-code long-lived raw keys in public client-side JavaScript if those keys are meant to remain secret.
|
package/docs/WASM.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# WebAssembly
|
|
2
|
+
|
|
3
|
+
QuadQR ships a small precompiled WebAssembly module at:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
dist/wasm/quadqr-core.wasm
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The normal JavaScript codec does not require it. This is intentional: installation, browser loading, serverless execution, and restricted environments should still work when WASM is unavailable.
|
|
10
|
+
|
|
11
|
+
## Enable it
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { initWasm } from "quadqr";
|
|
15
|
+
|
|
16
|
+
const state = await initWasm();
|
|
17
|
+
console.log(state);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Current 0.7.x acceleration covers CRC-32, which is used by QuadQR framing integrity checks. Once initialized, encode/decode calls automatically use the installed synchronous accelerator.
|
|
21
|
+
|
|
22
|
+
## Why start small?
|
|
23
|
+
|
|
24
|
+
QuadQR scanning and Spectrum ECC are still evolving. Keeping the first WASM ABI narrow lets the library ship a stable prebuilt binary without forcing the matrix format to depend on a specific compiler/runtime implementation.
|
|
25
|
+
|
|
26
|
+
Future profiling can move heavier hot paths, such as image classification or selected ECC operations, behind the same optional acceleration layer without changing the public QuadQR wire format.
|
|
27
|
+
|
|
28
|
+
## Disable
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
import { disableWasm } from "quadqr";
|
|
32
|
+
|
|
33
|
+
disableWasm();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The JavaScript fallback is restored immediately.
|