novac 2.0.1 → 2.2.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/LICENSE +1 -1
- package/README.md +1574 -597
- package/bin/novac +468 -171
- package/bin/nvc +522 -0
- package/bin/nvml +78 -17
- package/demo.nv +0 -0
- package/demo_builtins.nv +0 -0
- package/demo_http.nv +0 -0
- package/examples/bf.nv +69 -0
- package/examples/math.nv +21 -0
- package/kits/birdAPI/kitdef.js +954 -0
- package/kits/kitRNG/kitdef.js +740 -0
- package/kits/kitSSH/kitdef.js +1272 -0
- package/kits/kitadb/kitdef.js +606 -0
- package/kits/kitai/kitdef.js +2185 -0
- package/kits/kitansi/kitdef.js +1402 -0
- package/kits/kitcanvas/kitdef.js +914 -0
- package/kits/kitclippy/kitdef.js +925 -0
- package/kits/kitformat/kitdef.js +1485 -0
- package/kits/kitgps/kitdef.js +1862 -0
- package/kits/kitlibproc/kitdef.js +3 -2
- package/kits/kitmatrix/ex.js +19 -0
- package/kits/kitmatrix/kitdef.js +960 -0
- package/kits/kitmorse/kitdef.js +229 -0
- package/kits/kitmpatch/kitdef.js +906 -0
- package/kits/kitnet/kitdef.js +1401 -0
- package/kits/kitnovacweb/README.md +1416 -143
- package/kits/kitnovacweb/kitdef.js +92 -2
- package/kits/kitnovacweb/nvml/executor.js +578 -176
- package/kits/kitnovacweb/nvml/index.js +2 -2
- package/kits/kitnovacweb/nvml/lexer.js +72 -69
- package/kits/kitnovacweb/nvml/parser.js +328 -159
- package/kits/kitnovacweb/nvml/renderer.js +770 -270
- package/kits/kitparse/kitdef.js +1688 -0
- package/kits/kitproto/kitdef.js +613 -0
- package/kits/kitqr/kitdef.js +637 -0
- package/kits/kitregex++/kitdef.js +1353 -0
- package/kits/kitrequire/kitdef.js +1599 -0
- package/kits/kitx11/kitdef.js +1 -0
- package/kits/kitx11/kitx11.js +2472 -0
- package/kits/kitx11/kitx11_conn.js +948 -0
- package/kits/kitx11/kitx11_worker.js +121 -0
- package/kits/libtea/kitdef.js +2691 -0
- package/kits/libterm/ex.js +285 -0
- package/kits/libterm/kitdef.js +1927 -0
- package/novac/LICENSE +21 -0
- package/novac/README.md +1823 -0
- package/novac/bin/novac +950 -0
- package/novac/bin/nvc +522 -0
- package/novac/bin/nvml +542 -0
- package/novac/demo.nv +245 -0
- package/novac/demo_builtins.nv +209 -0
- package/novac/demo_http.nv +62 -0
- package/novac/examples/bf.nv +69 -0
- package/novac/examples/math.nv +21 -0
- package/novac/kits/kitai/kitdef.js +2185 -0
- package/novac/kits/kitansi/kitdef.js +1402 -0
- package/novac/kits/kitformat/kitdef.js +1485 -0
- package/novac/kits/kitgps/kitdef.js +1862 -0
- package/novac/kits/kitlibfs/kitdef.js +231 -0
- package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
- package/novac/kits/kitmatrix/ex.js +19 -0
- package/novac/kits/kitmatrix/kitdef.js +960 -0
- package/novac/kits/kitmpatch/kitdef.js +906 -0
- package/novac/kits/kitnovacweb/README.md +1572 -0
- package/novac/kits/kitnovacweb/demo.nv +12 -0
- package/novac/kits/kitnovacweb/demo.nvml +71 -0
- package/novac/kits/kitnovacweb/index.nova +12 -0
- package/novac/kits/kitnovacweb/kitdef.js +692 -0
- package/novac/kits/kitnovacweb/nova.kit.json +8 -0
- package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
- package/novac/kits/kitnovacweb/nvml/index.js +67 -0
- package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
- package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
- package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
- package/novac/kits/kitparse/kitdef.js +1688 -0
- package/novac/kits/kitregex++/kitdef.js +1353 -0
- package/novac/kits/kitrequire/kitdef.js +1599 -0
- package/novac/kits/kitx11/kitdef.js +1 -0
- package/novac/kits/kitx11/kitx11.js +2472 -0
- package/novac/kits/kitx11/kitx11_conn.js +948 -0
- package/novac/kits/kitx11/kitx11_worker.js +121 -0
- package/novac/kits/libtea/tf.js +2691 -0
- package/novac/kits/libterm/ex.js +285 -0
- package/novac/kits/libterm/kitdef.js +1927 -0
- package/novac/node_modules/chalk/license +9 -0
- package/novac/node_modules/chalk/package.json +83 -0
- package/novac/node_modules/chalk/readme.md +297 -0
- package/novac/node_modules/chalk/source/index.d.ts +325 -0
- package/novac/node_modules/chalk/source/index.js +225 -0
- package/novac/node_modules/chalk/source/utilities.js +33 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/novac/node_modules/commander/LICENSE +22 -0
- package/novac/node_modules/commander/Readme.md +1176 -0
- package/novac/node_modules/commander/esm.mjs +16 -0
- package/novac/node_modules/commander/index.js +24 -0
- package/novac/node_modules/commander/lib/argument.js +150 -0
- package/novac/node_modules/commander/lib/command.js +2777 -0
- package/novac/node_modules/commander/lib/error.js +39 -0
- package/novac/node_modules/commander/lib/help.js +747 -0
- package/novac/node_modules/commander/lib/option.js +380 -0
- package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/novac/node_modules/commander/package-support.json +19 -0
- package/novac/node_modules/commander/package.json +82 -0
- package/novac/node_modules/commander/typings/esm.d.mts +3 -0
- package/novac/node_modules/commander/typings/index.d.ts +1113 -0
- package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
- package/novac/node_modules/node-addon-api/README.md +95 -0
- package/novac/node_modules/node-addon-api/common.gypi +21 -0
- package/novac/node_modules/node-addon-api/except.gypi +25 -0
- package/novac/node_modules/node-addon-api/index.js +14 -0
- package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
- package/novac/node_modules/node-addon-api/napi.h +3364 -0
- package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
- package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
- package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/novac/node_modules/node-addon-api/package-support.json +21 -0
- package/novac/node_modules/node-addon-api/package.json +480 -0
- package/novac/node_modules/node-addon-api/tools/README.md +73 -0
- package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/novac/node_modules/serialize-javascript/LICENSE +27 -0
- package/novac/node_modules/serialize-javascript/README.md +149 -0
- package/novac/node_modules/serialize-javascript/index.js +297 -0
- package/novac/node_modules/serialize-javascript/package.json +33 -0
- package/novac/package.json +27 -0
- package/novac/scripts/update-bin.js +24 -0
- package/novac/src/core/bstd.js +1035 -0
- package/novac/src/core/config.js +155 -0
- package/novac/src/core/describe.js +187 -0
- package/novac/src/core/emitter.js +499 -0
- package/novac/src/core/error.js +86 -0
- package/novac/src/core/executor.js +5606 -0
- package/novac/src/core/formatter.js +686 -0
- package/novac/src/core/lexer.js +1026 -0
- package/novac/src/core/nova_builtins.js +717 -0
- package/novac/src/core/nova_thread_worker.js +166 -0
- package/novac/src/core/parser.js +2181 -0
- package/novac/src/core/types.js +112 -0
- package/novac/src/index.js +28 -0
- package/novac/src/runtime/stdlib.js +244 -0
- package/package.json +6 -3
- package/scripts/update-bin.js +0 -0
- package/src/core/bstd.js +838 -362
- package/src/core/executor.js +2578 -170
- package/src/core/lexer.js +502 -54
- package/src/core/nova_builtins.js +21 -3
- package/src/core/parser.js +413 -72
- package/src/core/types.js +30 -2
- package/src/index.js +0 -0
- package/examples/example-project/README.md +0 -3
- package/examples/example-project/src/main.nova +0 -3
- package/src/core/environment.js +0 -0
- /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
// kitqr — novac QR code generation kit
|
|
2
|
+
// Pure JS, no native dependencies.
|
|
3
|
+
// Encodes text/URLs/data → QR matrix → ASCII / Unicode-art / SVG output.
|
|
4
|
+
//
|
|
5
|
+
// Supports QR versions 1-40, error correction L/M/Q/H, byte mode encoding.
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// ─── CONSTANTS ────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const EC = { L: 0, M: 1, Q: 2, H: 3 };
|
|
12
|
+
const EC_NAME = ['L', 'M', 'Q', 'H'];
|
|
13
|
+
|
|
14
|
+
// Number of data codewords per version per EC level (versions 1-10 subset for brevity)
|
|
15
|
+
// Full table: ISO 18004:2015 Table 9
|
|
16
|
+
const DATA_CODEWORDS = [
|
|
17
|
+
// L M Q H
|
|
18
|
+
null,
|
|
19
|
+
[null, 19, 16, 13, 9 ], // v1
|
|
20
|
+
[null, 34, 28, 22, 16], // v2
|
|
21
|
+
[null, 55, 44, 34, 26], // v3
|
|
22
|
+
[null, 80, 64, 48, 36], // v4
|
|
23
|
+
[null, 108, 86, 62, 46], // v5
|
|
24
|
+
[null, 136, 108, 76, 60], // v6
|
|
25
|
+
[null, 156, 124, 88, 66], // v7
|
|
26
|
+
[null, 194, 154, 110, 86], // v8
|
|
27
|
+
[null, 232, 182, 132, 100], // v9
|
|
28
|
+
[null, 274, 216, 154, 122], // v10
|
|
29
|
+
[null, 324, 254, 180, 140], // v11
|
|
30
|
+
[null, 370, 290, 206, 158], // v12
|
|
31
|
+
[null, 428, 334, 244, 180], // v13
|
|
32
|
+
[null, 461, 365, 261, 197], // v14
|
|
33
|
+
[null, 523, 415, 295, 223], // v15
|
|
34
|
+
[null, 589, 453, 325, 253], // v16
|
|
35
|
+
[null, 647, 507, 367, 283], // v17
|
|
36
|
+
[null, 721, 563, 397, 313], // v18
|
|
37
|
+
[null, 795, 627, 445, 341], // v19
|
|
38
|
+
[null, 861, 669, 485, 385], // v20
|
|
39
|
+
[null, 932, 714, 512, 406], // v21
|
|
40
|
+
[null,1006, 782, 568, 442], // v22
|
|
41
|
+
[null,1094, 860, 614, 464], // v23
|
|
42
|
+
[null,1174, 914, 664, 514], // v24
|
|
43
|
+
[null,1276,1000, 718, 538], // v25
|
|
44
|
+
[null,1370,1062, 754, 596], // v26
|
|
45
|
+
[null,1468,1128, 808, 628], // v27
|
|
46
|
+
[null,1531,1193, 871, 661], // v28
|
|
47
|
+
[null,1631,1267, 911, 701], // v29
|
|
48
|
+
[null,1735,1373, 985, 745], // v30
|
|
49
|
+
[null,1843,1455,1033, 793], // v31
|
|
50
|
+
[null,1955,1541,1115, 845], // v32
|
|
51
|
+
[null,2071,1631,1171, 901], // v33
|
|
52
|
+
[null,2191,1725,1231, 961], // v34
|
|
53
|
+
[null,2306,1812,1286, 986], // v35
|
|
54
|
+
[null,2434,1914,1354,1054], // v36
|
|
55
|
+
[null,2566,1992,1426,1096], // v37
|
|
56
|
+
[null,2702,2102,1502,1142], // v38
|
|
57
|
+
[null,2812,2216,1582,1222], // v39
|
|
58
|
+
[null,2956,2334,1666,1276], // v40
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// EC codewords per block and block counts — ISO 18004 Table 9 (abbreviated)
|
|
62
|
+
// Format: [ecPerBlock, blocks1, dataPerBlock1, blocks2, dataPerBlock2]
|
|
63
|
+
const EC_BLOCKS = [
|
|
64
|
+
null,
|
|
65
|
+
// v1
|
|
66
|
+
[[7,1,19],[10,1,16],[13,1,13],[17,1,9]],
|
|
67
|
+
// v2
|
|
68
|
+
[[10,1,34],[16,1,28],[22,1,22],[28,1,16]],
|
|
69
|
+
// v3
|
|
70
|
+
[[15,1,55],[26,1,44],[18,2,17],[22,2,13]],
|
|
71
|
+
// v4
|
|
72
|
+
[[20,1,80],[18,2,32],[26,2,24],[16,4,9]],
|
|
73
|
+
// v5
|
|
74
|
+
[[26,1,108],[24,2,43],[18,2,15,2,16],[22,2,11,2,12]],
|
|
75
|
+
// v6
|
|
76
|
+
[[18,2,68],[16,4,27],[24,4,19],[28,4,15]],
|
|
77
|
+
// v7
|
|
78
|
+
[[20,2,78],[18,4,31],[18,2,14,4,15],[26,4,13,1,14]],
|
|
79
|
+
// v8
|
|
80
|
+
[[24,2,97],[22,2,38,2,39],[22,4,18,2,19],[26,4,14,2,15]],
|
|
81
|
+
// v9
|
|
82
|
+
[[30,2,116],[22,3,36,2,37],[20,4,16,4,17],[24,4,12,4,13]],
|
|
83
|
+
// v10
|
|
84
|
+
[[18,2,68,2,69],[26,4,43,1,44],[24,6,19,2,20],[28,6,15,2,16]],
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Alignment pattern positions per version
|
|
88
|
+
const ALIGN_POS = [
|
|
89
|
+
null,
|
|
90
|
+
[], // v1
|
|
91
|
+
[6,18], // v2
|
|
92
|
+
[6,22], // v3
|
|
93
|
+
[6,26], // v4
|
|
94
|
+
[6,30], // v5
|
|
95
|
+
[6,34], // v6
|
|
96
|
+
[6,22,38], // v7
|
|
97
|
+
[6,24,42], // v8
|
|
98
|
+
[6,26,46], // v9
|
|
99
|
+
[6,28,50], // v10
|
|
100
|
+
[6,30,54], // v11
|
|
101
|
+
[6,32,58], // v12
|
|
102
|
+
[6,34,62], // v13
|
|
103
|
+
[6,26,46,66], // v14
|
|
104
|
+
[6,26,48,70], // v15
|
|
105
|
+
[6,26,50,74], // v16
|
|
106
|
+
[6,30,54,78], // v17
|
|
107
|
+
[6,30,56,82], // v18
|
|
108
|
+
[6,30,58,86], // v19
|
|
109
|
+
[6,34,62,90], // v20
|
|
110
|
+
[6,28,50,72,94], // v21
|
|
111
|
+
[6,26,50,74,98], // v22
|
|
112
|
+
[6,30,54,78,102], // v23
|
|
113
|
+
[6,28,54,80,106], // v24
|
|
114
|
+
[6,32,58,84,110], // v25
|
|
115
|
+
[6,30,58,86,114], // v26
|
|
116
|
+
[6,34,62,90,118], // v27
|
|
117
|
+
[6,26,50,74,98,122], // v28
|
|
118
|
+
[6,30,54,78,102,126], // v29
|
|
119
|
+
[6,26,52,78,104,130], // v30
|
|
120
|
+
[6,30,56,82,108,134], // v31
|
|
121
|
+
[6,34,60,86,112,138], // v32
|
|
122
|
+
[6,30,58,86,114,142], // v33
|
|
123
|
+
[6,34,62,90,118,146], // v34
|
|
124
|
+
[6,30,54,82,110,138,166], // v35
|
|
125
|
+
[6,24,50,76,102,128,154], // v36
|
|
126
|
+
[6,28,54,80,106,132,158], // v37
|
|
127
|
+
[6,32,58,84,110,136,162], // v38
|
|
128
|
+
[6,26,54,82,110,138,166], // v39
|
|
129
|
+
[6,30,58,86,114,142,170], // v40
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
// Format information strings (15-bit, pre-computed) — ISO 18004 Table C.1
|
|
133
|
+
const FORMAT_INFO = [
|
|
134
|
+
// EC L (00)
|
|
135
|
+
[0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976],
|
|
136
|
+
// EC M (01)
|
|
137
|
+
[0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0],
|
|
138
|
+
// EC Q (10)
|
|
139
|
+
[0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED],
|
|
140
|
+
// EC H (11)
|
|
141
|
+
[0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x0762, 0x0255, 0x0D0C, 0x083B],
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
// Version information strings for v7-v40 — ISO 18004 Table D.1
|
|
145
|
+
const VERSION_INFO = [
|
|
146
|
+
null,null,null,null,null,null,null,
|
|
147
|
+
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847,
|
|
148
|
+
0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6,
|
|
149
|
+
0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E,
|
|
150
|
+
0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA,
|
|
151
|
+
0x2379F, 0x24B0B, 0x2542E, 0x26A64, 0x27541, 0x28C69,
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
// GF(256) log/exp tables for Reed-Solomon
|
|
155
|
+
const GF_EXP = new Uint8Array(512);
|
|
156
|
+
const GF_LOG = new Uint8Array(256);
|
|
157
|
+
(function buildGF() {
|
|
158
|
+
let x = 1;
|
|
159
|
+
for (let i = 0; i < 255; i++) {
|
|
160
|
+
GF_EXP[i] = x;
|
|
161
|
+
GF_LOG[x] = i;
|
|
162
|
+
x = (x << 1) ^ (x & 0x80 ? 0x11D : 0);
|
|
163
|
+
}
|
|
164
|
+
for (let i = 255; i < 512; i++) GF_EXP[i] = GF_EXP[i - 255];
|
|
165
|
+
})();
|
|
166
|
+
|
|
167
|
+
function gfMul(a, b) {
|
|
168
|
+
if (a === 0 || b === 0) return 0;
|
|
169
|
+
return GF_EXP[GF_LOG[a] + GF_LOG[b]];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function rsGeneratorPoly(degree) {
|
|
173
|
+
let g = [1];
|
|
174
|
+
for (let i = 0; i < degree; i++) {
|
|
175
|
+
const ng = new Array(g.length + 1).fill(0);
|
|
176
|
+
for (let j = 0; j < g.length; j++) {
|
|
177
|
+
ng[j] ^= gfMul(g[j], GF_EXP[i]);
|
|
178
|
+
ng[j + 1] ^= g[j];
|
|
179
|
+
}
|
|
180
|
+
g = ng;
|
|
181
|
+
}
|
|
182
|
+
return g;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function rsEncode(data, ecLen) {
|
|
186
|
+
const gen = rsGeneratorPoly(ecLen);
|
|
187
|
+
const msg = [...data, ...new Array(ecLen).fill(0)];
|
|
188
|
+
for (let i = 0; i < data.length; i++) {
|
|
189
|
+
const coeff = msg[i];
|
|
190
|
+
if (coeff === 0) continue;
|
|
191
|
+
for (let j = 0; j < gen.length; j++) {
|
|
192
|
+
msg[i + j] ^= gfMul(gen[j], coeff);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return msg.slice(data.length);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ─── ENCODING ─────────────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
function encodeData(text, version, ecLevel) {
|
|
201
|
+
const bytes = Array.from(Buffer.from(text, 'utf8'));
|
|
202
|
+
const charCount = bytes.length;
|
|
203
|
+
const totalBits = DATA_CODEWORDS[version][ecLevel] * 8;
|
|
204
|
+
|
|
205
|
+
const bits = [];
|
|
206
|
+
const push = (val, len) => {
|
|
207
|
+
for (let i = len - 1; i >= 0; i--) bits.push((val >> i) & 1);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Mode indicator: byte mode = 0100
|
|
211
|
+
push(0b0100, 4);
|
|
212
|
+
// Character count: 8 bits for v1-9, 16 for v10-26, 16 for v27-40
|
|
213
|
+
const ccBits = version < 10 ? 8 : 16;
|
|
214
|
+
push(charCount, ccBits);
|
|
215
|
+
// Data
|
|
216
|
+
for (const b of bytes) push(b, 8);
|
|
217
|
+
// Terminator
|
|
218
|
+
const term = Math.min(4, totalBits - bits.length);
|
|
219
|
+
for (let i = 0; i < term; i++) bits.push(0);
|
|
220
|
+
// Byte-align
|
|
221
|
+
while (bits.length % 8 !== 0) bits.push(0);
|
|
222
|
+
// Pad codewords
|
|
223
|
+
const padWords = [0xEC, 0x11];
|
|
224
|
+
let pi = 0;
|
|
225
|
+
while (bits.length < totalBits) { push(padWords[pi++ % 2], 8); }
|
|
226
|
+
|
|
227
|
+
// Convert to bytes
|
|
228
|
+
const codewords = [];
|
|
229
|
+
for (let i = 0; i < bits.length; i += 8) {
|
|
230
|
+
let byte = 0;
|
|
231
|
+
for (let j = 0; j < 8; j++) byte = (byte << 1) | bits[i + j];
|
|
232
|
+
codewords.push(byte);
|
|
233
|
+
}
|
|
234
|
+
return codewords;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function interleaveBlocks(codewords, version, ecLevel) {
|
|
238
|
+
const spec = EC_BLOCKS[version]?.[ecLevel];
|
|
239
|
+
if (!spec) return codewords; // fallback
|
|
240
|
+
|
|
241
|
+
const [ecPerBlock, b1count, b1data, b2count = 0, b2data = 0] = spec;
|
|
242
|
+
|
|
243
|
+
const blocks = [];
|
|
244
|
+
let pos = 0;
|
|
245
|
+
|
|
246
|
+
const makeBlocks = (count, dataLen) => {
|
|
247
|
+
for (let i = 0; i < count; i++) {
|
|
248
|
+
const data = codewords.slice(pos, pos + dataLen);
|
|
249
|
+
pos += dataLen;
|
|
250
|
+
const ec = rsEncode(data, ecPerBlock);
|
|
251
|
+
blocks.push({ data, ec });
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
makeBlocks(b1count, b1data);
|
|
256
|
+
if (b2count) makeBlocks(b2count, b2data);
|
|
257
|
+
|
|
258
|
+
const result = [];
|
|
259
|
+
const maxData = Math.max(b1data, b2data || 0);
|
|
260
|
+
for (let i = 0; i < maxData; i++)
|
|
261
|
+
for (const b of blocks) if (i < b.data.length) result.push(b.data[i]);
|
|
262
|
+
for (let i = 0; i < ecPerBlock; i++)
|
|
263
|
+
for (const b of blocks) result.push(b.ec[i]);
|
|
264
|
+
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── MATRIX ───────────────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
const DARK = 1, LIGHT = 0, RESERVED = 2;
|
|
271
|
+
|
|
272
|
+
function makeMatrix(size) {
|
|
273
|
+
return Array.from({ length: size }, () => new Int8Array(size).fill(-1));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function setFinderPattern(matrix, row, col) {
|
|
277
|
+
for (let r = -1; r <= 7; r++) {
|
|
278
|
+
for (let c = -1; c <= 7; c++) {
|
|
279
|
+
const mr = row + r, mc = col + c;
|
|
280
|
+
if (mr < 0 || mr >= matrix.length || mc < 0 || mc >= matrix.length) continue;
|
|
281
|
+
const inModule = r >= 0 && r <= 6 && c >= 0 && c <= 6;
|
|
282
|
+
const onBorder = r === 0 || r === 6 || c === 0 || c === 6;
|
|
283
|
+
const inInner = r >= 2 && r <= 4 && c >= 2 && c <= 4;
|
|
284
|
+
matrix[mr][mc] = inModule && (onBorder || inInner) ? DARK : LIGHT;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function setTimingPattern(matrix, size) {
|
|
290
|
+
for (let i = 8; i < size - 8; i++) {
|
|
291
|
+
const v = i % 2 === 0 ? DARK : LIGHT;
|
|
292
|
+
matrix[6][i] = v;
|
|
293
|
+
matrix[i][6] = v;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function setAlignmentPatterns(matrix, version) {
|
|
298
|
+
const pos = ALIGN_POS[version] || [];
|
|
299
|
+
for (const r of pos) {
|
|
300
|
+
for (const c of pos) {
|
|
301
|
+
if (matrix[r][c] !== -1) continue;
|
|
302
|
+
for (let dr = -2; dr <= 2; dr++) {
|
|
303
|
+
for (let dc = -2; dc <= 2; dc++) {
|
|
304
|
+
const onEdge = Math.abs(dr) === 2 || Math.abs(dc) === 2;
|
|
305
|
+
const center = dr === 0 && dc === 0;
|
|
306
|
+
matrix[r + dr][c + dc] = onEdge || center ? DARK : LIGHT;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function reserveFormatArea(matrix, size) {
|
|
314
|
+
// Reserve format info areas
|
|
315
|
+
for (let i = 0; i <= 8; i++) {
|
|
316
|
+
if (matrix[8][i] === -1) matrix[8][i] = RESERVED;
|
|
317
|
+
if (matrix[i][8] === -1) matrix[i][8] = RESERVED;
|
|
318
|
+
}
|
|
319
|
+
for (let i = size - 8; i < size; i++) {
|
|
320
|
+
if (matrix[8][i] === -1) matrix[8][i] = RESERVED;
|
|
321
|
+
if (matrix[i][8] === -1) matrix[i][8] = RESERVED;
|
|
322
|
+
}
|
|
323
|
+
// Dark module
|
|
324
|
+
matrix[size - 8][8] = DARK;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function reserveVersionArea(matrix, size, version) {
|
|
328
|
+
if (version < 7) return;
|
|
329
|
+
for (let i = 0; i < 6; i++) {
|
|
330
|
+
for (let j = size - 11; j < size - 8; j++) {
|
|
331
|
+
matrix[i][j] = RESERVED;
|
|
332
|
+
matrix[j][i] = RESERVED;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function placeData(matrix, size, data) {
|
|
338
|
+
let bitIdx = 0;
|
|
339
|
+
const bits = [];
|
|
340
|
+
for (const byte of data) for (let i = 7; i >= 0; i--) bits.push((byte >> i) & 1);
|
|
341
|
+
|
|
342
|
+
let up = true;
|
|
343
|
+
for (let col = size - 1; col >= 1; col -= 2) {
|
|
344
|
+
if (col === 6) col--;
|
|
345
|
+
for (let rowOff = 0; rowOff < size; rowOff++) {
|
|
346
|
+
const row = up ? size - 1 - rowOff : rowOff;
|
|
347
|
+
for (let dc = 0; dc < 2; dc++) {
|
|
348
|
+
const c = col - dc;
|
|
349
|
+
if (matrix[row][c] !== -1) continue;
|
|
350
|
+
matrix[row][c] = bitIdx < bits.length ? bits[bitIdx++] : LIGHT;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
up = !up;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Mask patterns
|
|
358
|
+
const MASKS = [
|
|
359
|
+
(r, c) => (r + c) % 2 === 0,
|
|
360
|
+
(r, _) => r % 2 === 0,
|
|
361
|
+
(_, c) => c % 3 === 0,
|
|
362
|
+
(r, c) => (r + c) % 3 === 0,
|
|
363
|
+
(r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,
|
|
364
|
+
(r, c) => (r * c) % 2 + (r * c) % 3 === 0,
|
|
365
|
+
(r, c) => ((r * c) % 2 + (r * c) % 3) % 2 === 0,
|
|
366
|
+
(r, c) => ((r + c) % 2 + (r * c) % 3) % 2 === 0,
|
|
367
|
+
];
|
|
368
|
+
|
|
369
|
+
function applyMask(matrix, size, maskIdx) {
|
|
370
|
+
const fn = MASKS[maskIdx];
|
|
371
|
+
const m = matrix.map(r => Int8Array.from(r));
|
|
372
|
+
for (let r = 0; r < size; r++)
|
|
373
|
+
for (let c = 0; c < size; c++)
|
|
374
|
+
if (m[r][c] !== RESERVED && fn(r, c)) m[r][c] ^= 1;
|
|
375
|
+
return m;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function penaltyScore(matrix, size) {
|
|
379
|
+
let score = 0;
|
|
380
|
+
// Rule 1: 5+ in a row
|
|
381
|
+
for (let r = 0; r < size; r++) {
|
|
382
|
+
for (let c = 0; c < size; ) {
|
|
383
|
+
const v = matrix[r][c];
|
|
384
|
+
let run = 1;
|
|
385
|
+
while (c + run < size && matrix[r][c + run] === v) run++;
|
|
386
|
+
if (run >= 5) score += 3 + (run - 5);
|
|
387
|
+
c += run;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
for (let c = 0; c < size; c++) {
|
|
391
|
+
for (let r = 0; r < size; ) {
|
|
392
|
+
const v = matrix[r][c];
|
|
393
|
+
let run = 1;
|
|
394
|
+
while (r + run < size && matrix[r + run][c] === v) run++;
|
|
395
|
+
if (run >= 5) score += 3 + (run - 5);
|
|
396
|
+
r += run;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Rule 2: 2×2 blocks
|
|
400
|
+
for (let r = 0; r < size - 1; r++)
|
|
401
|
+
for (let c = 0; c < size - 1; c++)
|
|
402
|
+
if (matrix[r][c] === matrix[r+1][c] &&
|
|
403
|
+
matrix[r][c] === matrix[r][c+1] &&
|
|
404
|
+
matrix[r][c] === matrix[r+1][c+1]) score += 3;
|
|
405
|
+
// Rule 3: finder-like patterns
|
|
406
|
+
const pat1 = [1,0,1,1,1,0,1,0,0,0,0];
|
|
407
|
+
const pat2 = [0,0,0,0,1,0,1,1,1,0,1];
|
|
408
|
+
const check = (arr) => {
|
|
409
|
+
for (let i = 0; i <= arr.length - 11; i++) {
|
|
410
|
+
if (pat1.every((v,j) => arr[i+j] === v) || pat2.every((v,j) => arr[i+j] === v)) score += 40;
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
for (let r = 0; r < size; r++) check(Array.from(matrix[r]));
|
|
414
|
+
for (let c = 0; c < size; c++) check(Array.from({ length: size }, (_,r) => matrix[r][c]));
|
|
415
|
+
// Rule 4: dark module ratio
|
|
416
|
+
let dark = 0;
|
|
417
|
+
for (let r = 0; r < size; r++) for (let c = 0; c < size; c++) if (matrix[r][c] === DARK) dark++;
|
|
418
|
+
const pct = dark / (size * size) * 100;
|
|
419
|
+
score += Math.abs(Math.floor(pct / 5) * 5 - 50) / 5 * 10 +
|
|
420
|
+
Math.abs(Math.ceil(pct / 5) * 5 - 50) / 5 * 10;
|
|
421
|
+
return score;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function writeFormatInfo(matrix, size, ecLevel, maskIdx) {
|
|
425
|
+
const info = FORMAT_INFO[ecLevel][maskIdx];
|
|
426
|
+
const bits = [];
|
|
427
|
+
for (let i = 14; i >= 0; i--) bits.push((info >> i) & 1);
|
|
428
|
+
|
|
429
|
+
// Around top-left finder
|
|
430
|
+
let bi = 0;
|
|
431
|
+
for (let i = 0; i <= 5; i++) matrix[8][i] = bits[bi++];
|
|
432
|
+
matrix[8][7] = bits[bi++];
|
|
433
|
+
matrix[8][8] = bits[bi++];
|
|
434
|
+
matrix[7][8] = bits[bi++];
|
|
435
|
+
for (let i = 5; i >= 0; i--) matrix[i][8] = bits[bi++];
|
|
436
|
+
|
|
437
|
+
// Bottom-left and top-right
|
|
438
|
+
bi = 0;
|
|
439
|
+
for (let i = size - 1; i >= size - 8; i--) matrix[i][8] = bits[bi++];
|
|
440
|
+
for (let i = size - 8; i < size; i++) matrix[8][i] = bits[bi++];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function writeVersionInfo(matrix, size, version) {
|
|
444
|
+
if (version < 7) return;
|
|
445
|
+
const info = VERSION_INFO[version];
|
|
446
|
+
let bi = 17;
|
|
447
|
+
for (let i = 0; i < 6; i++) {
|
|
448
|
+
for (let j = 0; j < 3; j++) {
|
|
449
|
+
const bit = (info >> bi--) & 1;
|
|
450
|
+
matrix[i][size - 11 + j] = bit;
|
|
451
|
+
matrix[size - 11 + j][i] = bit;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ─── MAIN ENCODE ──────────────────────────────────────────────────────────────
|
|
457
|
+
|
|
458
|
+
function encode(text, opts = {}) {
|
|
459
|
+
const ecLevel = typeof opts.ec === 'string' ? EC[opts.ec.toUpperCase()] ?? EC.M : (opts.ec ?? EC.M);
|
|
460
|
+
const quiet = opts.quiet ?? 4;
|
|
461
|
+
|
|
462
|
+
// Find minimum version
|
|
463
|
+
const bytes = Buffer.from(text, 'utf8');
|
|
464
|
+
let version = opts.version ?? 0;
|
|
465
|
+
|
|
466
|
+
if (!version) {
|
|
467
|
+
// byte mode char count overhead: mode(4) + cc(8 for v1-9, 16 for v10+) + data
|
|
468
|
+
for (let v = 1; v <= 40; v++) {
|
|
469
|
+
const ccBits = v < 10 ? 8 : 16;
|
|
470
|
+
const needed = Math.ceil((4 + ccBits + bytes.length * 8) / 8);
|
|
471
|
+
if (needed <= DATA_CODEWORDS[v][ecLevel]) { version = v; break; }
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (!version) throw new Error('Data too large for QR code (max version 40)');
|
|
475
|
+
|
|
476
|
+
const size = version * 4 + 17;
|
|
477
|
+
|
|
478
|
+
// Build codewords
|
|
479
|
+
const dataWords = encodeData(text, version, ecLevel);
|
|
480
|
+
const finalWords = interleaveBlocks(dataWords, version, ecLevel);
|
|
481
|
+
|
|
482
|
+
// Build matrix
|
|
483
|
+
const matrix = makeMatrix(size);
|
|
484
|
+
setFinderPattern(matrix, 0, 0);
|
|
485
|
+
setFinderPattern(matrix, 0, size - 7);
|
|
486
|
+
setFinderPattern(matrix, size - 7, 0);
|
|
487
|
+
setTimingPattern(matrix, size);
|
|
488
|
+
setAlignmentPatterns(matrix, version);
|
|
489
|
+
reserveFormatArea(matrix, size);
|
|
490
|
+
reserveVersionArea(matrix, size, version);
|
|
491
|
+
placeData(matrix, size, finalWords);
|
|
492
|
+
writeVersionInfo(matrix, size, version);
|
|
493
|
+
|
|
494
|
+
// Choose best mask
|
|
495
|
+
let bestMask = 0, bestScore = Infinity;
|
|
496
|
+
for (let m = 0; m < 8; m++) {
|
|
497
|
+
const masked = applyMask(matrix, size, m);
|
|
498
|
+
writeFormatInfo(masked, size, ecLevel, m);
|
|
499
|
+
const s = penaltyScore(masked, size);
|
|
500
|
+
if (s < bestScore) { bestScore = s; bestMask = m; }
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const finalMatrix = applyMask(matrix, size, bestMask);
|
|
504
|
+
writeFormatInfo(finalMatrix, size, ecLevel, bestMask);
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
matrix: finalMatrix,
|
|
508
|
+
size,
|
|
509
|
+
version,
|
|
510
|
+
ec: EC_NAME[ecLevel],
|
|
511
|
+
mask: bestMask,
|
|
512
|
+
quiet,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ─── RENDERERS ────────────────────────────────────────────────────────────────
|
|
517
|
+
|
|
518
|
+
function toAscii(qr, opts = {}) {
|
|
519
|
+
const dark = opts.dark ?? '##';
|
|
520
|
+
const light = opts.light ?? ' ';
|
|
521
|
+
const q = qr.quiet;
|
|
522
|
+
const pad = light.repeat(qr.size + q * 2);
|
|
523
|
+
const lines = [pad.repeat ? pad : pad];
|
|
524
|
+
|
|
525
|
+
const emptyRow = light.repeat(qr.size + q * 2);
|
|
526
|
+
for (let i = 0; i < q; i++) lines.push(emptyRow);
|
|
527
|
+
|
|
528
|
+
for (let r = 0; r < qr.size; r++) {
|
|
529
|
+
let line = light.repeat(q);
|
|
530
|
+
for (let c = 0; c < qr.size; c++) {
|
|
531
|
+
line += qr.matrix[r][c] === DARK ? dark : light;
|
|
532
|
+
}
|
|
533
|
+
line += light.repeat(q);
|
|
534
|
+
lines.push(line);
|
|
535
|
+
}
|
|
536
|
+
for (let i = 0; i < q; i++) lines.push(emptyRow);
|
|
537
|
+
return lines.join('\n');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function toUnicode(qr) {
|
|
541
|
+
// Uses half-block characters: each char covers 2 rows
|
|
542
|
+
const q = qr.quiet;
|
|
543
|
+
const wide = qr.size + q * 2;
|
|
544
|
+
const lines = [];
|
|
545
|
+
const get = (r, c) => {
|
|
546
|
+
const mr = r - q, mc = c - q;
|
|
547
|
+
if (mr < 0 || mr >= qr.size || mc < 0 || mc >= qr.size) return false;
|
|
548
|
+
return qr.matrix[mr][mc] === DARK;
|
|
549
|
+
};
|
|
550
|
+
for (let r = 0; r < qr.size + q * 2; r += 2) {
|
|
551
|
+
let line = '';
|
|
552
|
+
for (let c = 0; c < wide; c++) {
|
|
553
|
+
const top = get(r, c), bot = get(r + 1, c);
|
|
554
|
+
if (top && bot) line += '█';
|
|
555
|
+
else if (top) line += '▀';
|
|
556
|
+
else if (bot) line += '▄';
|
|
557
|
+
else line += ' ';
|
|
558
|
+
}
|
|
559
|
+
lines.push(line);
|
|
560
|
+
}
|
|
561
|
+
return lines.join('\n');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function toSVG(qr, opts = {}) {
|
|
565
|
+
const moduleSize = opts.moduleSize ?? 4;
|
|
566
|
+
const dark = opts.dark ?? '#000000';
|
|
567
|
+
const light = opts.light ?? '#ffffff';
|
|
568
|
+
const q = qr.quiet;
|
|
569
|
+
const total = (qr.size + q * 2) * moduleSize;
|
|
570
|
+
|
|
571
|
+
const rects = [];
|
|
572
|
+
for (let r = 0; r < qr.size; r++) {
|
|
573
|
+
for (let c = 0; c < qr.size; c++) {
|
|
574
|
+
if (qr.matrix[r][c] === DARK) {
|
|
575
|
+
const x = (c + q) * moduleSize;
|
|
576
|
+
const y = (r + q) * moduleSize;
|
|
577
|
+
rects.push(`<rect x="${x}" y="${y}" width="${moduleSize}" height="${moduleSize}" fill="${dark}"/>`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
583
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
|
|
584
|
+
viewBox="0 0 ${total} ${total}" width="${total}" height="${total}">
|
|
585
|
+
<rect width="${total}" height="${total}" fill="${light}"/>
|
|
586
|
+
${rects.join('\n ')}
|
|
587
|
+
</svg>`;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function toMatrix(qr) {
|
|
591
|
+
// Returns a plain 2D array of booleans (true=dark)
|
|
592
|
+
return qr.matrix.map(row => Array.from(row).map(v => v === DARK));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function toDataURL(qr, opts = {}) {
|
|
596
|
+
// Returns a data URL with SVG content (base64)
|
|
597
|
+
const svg = toSVG(qr, opts);
|
|
598
|
+
return 'data:image/svg+xml;base64,' + Buffer.from(svg).toString('base64');
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ─── HIGH-LEVEL API ───────────────────────────────────────────────────────────
|
|
602
|
+
|
|
603
|
+
function qr(text, opts = {}) {
|
|
604
|
+
const encoded = encode(text, opts);
|
|
605
|
+
const format = opts.format ?? 'unicode';
|
|
606
|
+
switch (format) {
|
|
607
|
+
case 'ascii': return toAscii(encoded, opts);
|
|
608
|
+
case 'unicode': return toUnicode(encoded);
|
|
609
|
+
case 'svg': return toSVG(encoded, opts);
|
|
610
|
+
case 'matrix': return toMatrix(encoded);
|
|
611
|
+
case 'dataurl': return toDataURL(encoded, opts);
|
|
612
|
+
case 'raw': return encoded;
|
|
613
|
+
default: throw new Error(`Unknown format: ${format}. Use 'ascii', 'unicode', 'svg', 'matrix', 'dataurl', or 'raw'.`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// ─── EXPORTS ─────────────────────────────────────────────────────────────────
|
|
618
|
+
|
|
619
|
+
module.exports = {
|
|
620
|
+
kitdef: {
|
|
621
|
+
// High-level: encode and render in one call
|
|
622
|
+
qr,
|
|
623
|
+
|
|
624
|
+
// Low-level: encode only → returns raw QR object { matrix, size, version, ec, mask }
|
|
625
|
+
encode,
|
|
626
|
+
|
|
627
|
+
// Renderers (accept raw QR object)
|
|
628
|
+
toAscii,
|
|
629
|
+
toUnicode,
|
|
630
|
+
toSVG,
|
|
631
|
+
toMatrix,
|
|
632
|
+
toDataURL,
|
|
633
|
+
|
|
634
|
+
// EC level constants
|
|
635
|
+
EC,
|
|
636
|
+
}
|
|
637
|
+
};
|