bysquare 1.2.1 → 1.2.2

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
@@ -39,7 +39,7 @@ npm install --global bysquare
39
39
 
40
40
  ### **generate(model: Model): Promise\<string>**
41
41
 
42
- ```typescript
42
+ ```ts
43
43
  import { generate, Model, parse } from "bysquare"
44
44
 
45
45
  const model: Model = {
@@ -58,9 +58,9 @@ generate(model).then((qrString: string) => {
58
58
  })
59
59
  ```
60
60
 
61
- ### **parse(qrString: string): Promise\<Model>**
61
+ ### **parse(qr: string): Promise\<Model>**
62
62
 
63
- ```typescript
63
+ ```ts
64
64
  import { Model, parse } from "bysquare"
65
65
 
66
66
  const qrString = "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000"
@@ -70,6 +70,17 @@ parse(qrString).then((model: Model) => {
70
70
  })
71
71
  ```
72
72
 
73
+ ### **detect(qr: string): Boolean**
74
+
75
+ ```ts
76
+ import { detect } from "bysquare"
77
+
78
+ const qrString = "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000"
79
+ const isBysquare = detect(qrString)
80
+
81
+ // your logic...
82
+ ```
83
+
73
84
  ## CLI
74
85
 
75
86
  You can use json file with valid model to generate qr-string.
@@ -86,25 +97,23 @@ You can use json file with valid model to generate qr-string.
86
97
  # "BankAccounts": 1
87
98
  # }
88
99
 
89
- > npx bysquare ./example.json
90
- > 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
100
+ $ npx bysquare ./example.json
101
+ $ 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
91
102
  ```
92
103
 
93
104
  You can also use stdin.
94
105
 
95
106
  ```sh
96
- > echo '
97
- {
98
- "IBAN": "SK9611000000002918599669",
99
- "Amount": 100.0,
100
- "CurrencyCode": "EUR",
101
- "VariableSymbol": "123",
102
- "Payments": 1,
103
- "PaymentOptions": 1,
104
- "BankAccounts": 1
105
- }' \
106
- | bysquare
107
- > 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
107
+ $ bysquare <<< '{
108
+ "IBAN": "SK9611000000002918599669",
109
+ "Amount": 100.0,
110
+ "CurrencyCode": "EUR",
111
+ "VariableSymbol": "123",
112
+ "Payments": 1,
113
+ "PaymentOptions": 1,
114
+ "BankAccounts": 1
115
+ }'
116
+ $ 0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000
108
117
  ```
109
118
 
110
119
  ## Model
@@ -173,6 +182,7 @@ Versioning
173
182
  - Commit and tag
174
183
  - Run the `postversion` script
175
184
  - Checkout to master
176
- - Push commits and tag, git push, git push --tags
185
+ - Push commits and tag, git push && git push --tags
186
+ - npm publish --dry-run
177
187
  - Publish to npm, npm publish
178
188
  -->
package/lib/cli.js CHANGED
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const readline_1 = require("readline");
10
- const generate_1 = __importDefault(require("./generate"));
10
+ const generate_1 = require("./generate");
11
11
  if (process.stdin.isTTY) {
12
12
  /** bysquare "file" */
13
13
  handleInput(process.argv[2]);
@@ -47,7 +47,7 @@ async function jsonStringToQrString(stdin) {
47
47
  return new Promise((resolve, reject) => {
48
48
  try {
49
49
  const data = JSON.parse(stdin);
50
- const qrString = (0, generate_1.default)(data);
50
+ const qrString = (0, generate_1.generate)(data);
51
51
  resolve(qrString);
52
52
  }
53
53
  catch (e) {
package/lib/generate.d.ts CHANGED
@@ -1,3 +1,47 @@
1
- import { Model } from ".";
2
- declare function generate(model: Model): Promise<string>;
3
- export default generate;
1
+ /// <reference types="node" />
2
+ import { Model } from "./index";
3
+ /**
4
+ * ```
5
+ * | Attribute | Number of bits | Possible values | Note
6
+ * --------------------------------------------------------------------------------------------
7
+ * | BySquareType | 4 | 0-15 | by square type
8
+ * | Version | 4 | 0-15 | version of the by square type
9
+ * | DocumentType | 4 | 0-15 | document type within given by square type
10
+ * | Reserved | 4 | 0-15 | bits reserved for future needs
11
+ * ```
12
+ *
13
+ * @see {spec 3.5.}
14
+ */
15
+ export declare function createBysquareHeader(header?: [
16
+ bySquareType: number,
17
+ version: number,
18
+ documentType: number,
19
+ reserved: number
20
+ ]): Buffer;
21
+ export declare function createChecksum(tabbedInput: string): Buffer;
22
+ /**
23
+ * Appending CRC32 checksum
24
+ *
25
+ * @see {spec 3.10.}
26
+ */
27
+ export declare function dataWithChecksum(model: Model): Buffer;
28
+ /**
29
+ * Order keys by specification
30
+ * Fill empty values
31
+ * Transform to tabbed string
32
+ */
33
+ export declare function createTabbedString(model: Model): string;
34
+ /**
35
+ * The bit sequence is split into 5 bit chunks which are mapped onto the
36
+ * characters
37
+ *
38
+ * @see {spec 3.13. Table 9 – Encoding table}
39
+ */
40
+ export declare const SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
41
+ /**
42
+ * Alphanumeric conversion using Base32hex
43
+ *
44
+ * @see {spec 3.13.}
45
+ */
46
+ export declare function alphanumericConversion(data: Buffer): string;
47
+ export declare function generate(model: Model): Promise<string>;
package/lib/generate.js CHANGED
@@ -1,18 +1,154 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
4
20
  };
5
21
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const lzma_native_1 = __importDefault(require("lzma-native"));
7
- const utils_1 = require("./utils");
22
+ exports.generate = exports.alphanumericConversion = exports.SUBST = exports.createTabbedString = exports.dataWithChecksum = exports.createChecksum = exports.createBysquareHeader = void 0;
23
+ const lzma = __importStar(require("lzma-native"));
24
+ const index_1 = require("./index");
25
+ // echo "Hello" | xz --format=raw --lzma1=lc=3,lp=0,pb=2,dict=32KiB --stdout | hexdump -C
26
+ /**
27
+ * ```
28
+ * | Attribute | Number of bits | Possible values | Note
29
+ * --------------------------------------------------------------------------------------------
30
+ * | BySquareType | 4 | 0-15 | by square type
31
+ * | Version | 4 | 0-15 | version of the by square type
32
+ * | DocumentType | 4 | 0-15 | document type within given by square type
33
+ * | Reserved | 4 | 0-15 | bits reserved for future needs
34
+ * ```
35
+ *
36
+ * @see {spec 3.5.}
37
+ */
38
+ function createBysquareHeader(
39
+ // prettier-ignore
40
+ header = [
41
+ 0b0000_0000, 0b0000_0000,
42
+ 0b0000_0000, 0b0000_0000
43
+ ]) {
44
+ const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15);
45
+ if (!isValid) {
46
+ throw new Error("Header range of values must be <0,15>");
47
+ }
48
+ const [BySquareType, Version, DocumentType, Reserved] = header;
49
+ /** Combine 4-nibbles to 2-bytes */
50
+ const headerBuffer = Buffer.from([
51
+ (BySquareType << 4) | (Version << 0),
52
+ (DocumentType << 4) | (Reserved << 0)
53
+ ]);
54
+ return headerBuffer;
55
+ }
56
+ exports.createBysquareHeader = createBysquareHeader;
57
+ /**
58
+ * LZMA Compression header
59
+ *
60
+ * @see {spec 3.11.}
61
+ */
62
+ function createCompresionHeader(byteLength) {
63
+ const dataSize = Buffer.alloc(2);
64
+ dataSize.writeInt16LE(byteLength, 0);
65
+ return dataSize;
66
+ }
67
+ function createChecksum(tabbedInput) {
68
+ // @ts-ignore: Wrong return type
69
+ const data = lzma.crc32(tabbedInput);
70
+ const crc32 = Buffer.alloc(4);
71
+ crc32.writeUInt32LE(data);
72
+ return crc32;
73
+ }
74
+ exports.createChecksum = createChecksum;
75
+ /**
76
+ * Appending CRC32 checksum
77
+ *
78
+ * @see {spec 3.10.}
79
+ */
80
+ function dataWithChecksum(model) {
81
+ const tabbedString = createTabbedString(model);
82
+ const checksum = createChecksum(tabbedString);
83
+ const merged = Buffer.concat([
84
+ checksum,
85
+ Buffer.from(tabbedString, "utf-8")
86
+ ]);
87
+ return merged;
88
+ }
89
+ exports.dataWithChecksum = dataWithChecksum;
90
+ /**
91
+ * Order keys by specification
92
+ * Fill empty values
93
+ * Transform to tabbed string
94
+ */
95
+ function createTabbedString(model) {
96
+ const tabbedModel = Object.keys(model)
97
+ .reduce((acc, key) => {
98
+ acc[index_1.ModelOrdered[key]] = String(model[key] ?? "");
99
+ return acc;
100
+ }, Array(33).fill(""))
101
+ .join("\t");
102
+ return tabbedModel;
103
+ }
104
+ exports.createTabbedString = createTabbedString;
105
+ /**
106
+ * The bit sequence is split into 5 bit chunks which are mapped onto the
107
+ * characters
108
+ *
109
+ * @see {spec 3.13. Table 9 – Encoding table}
110
+ */
111
+ exports.SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
112
+ /**
113
+ * Alphanumeric conversion using Base32hex
114
+ *
115
+ * @see {spec 3.13.}
116
+ */
117
+ function alphanumericConversion(data) {
118
+ let paddedBinString = data.reduce((acc, byte) => acc + byte.toString(2).padStart(8, "0"), "");
119
+ let paddedLength = paddedBinString.length;
120
+ const remainder = paddedLength % 5;
121
+ if (remainder) {
122
+ paddedBinString += Array(5 - remainder)
123
+ .fill("0")
124
+ .join("");
125
+ paddedLength += 5 - remainder;
126
+ }
127
+ /**
128
+ * Map a binary number of 5 bits to a string representation 2^5
129
+ * SUBST[0...32] represents char
130
+ *
131
+ * @see {@link SUBST}
132
+ */
133
+ let encoded = "";
134
+ for (let i = 0; i < paddedLength / 5; i++) {
135
+ const binStart = 5 * i;
136
+ const binEnd = 5 * i + 5;
137
+ const sliced = paddedBinString.slice(binStart, binEnd);
138
+ const key = parseInt(sliced, 2);
139
+ encoded += exports.SUBST[key];
140
+ }
141
+ return encoded;
142
+ }
143
+ exports.alphanumericConversion = alphanumericConversion;
8
144
  function generate(model) {
9
- const dataBuffer = (0, utils_1.dataWithChecksum)(model);
145
+ const dataBuffer = dataWithChecksum(model);
10
146
  const dataChunks = [];
11
147
  return new Promise((resolve, reject) => {
12
- const encoder = lzma_native_1.default.createStream("rawEncoder", {
148
+ const encoder = lzma.createStream("rawEncoder", {
13
149
  synchronous: true,
14
150
  // @ts-ignore: Missing filter types
15
- filters: [{ id: lzma_native_1.default.FILTER_LZMA1 }]
151
+ filters: [{ id: lzma.FILTER_LZMA1 }]
16
152
  });
17
153
  encoder
18
154
  .on("data", (chunk) => {
@@ -20,50 +156,20 @@ function generate(model) {
20
156
  })
21
157
  .on("error", reject)
22
158
  .on("end", () => {
23
- /**
24
- * The header of compressed data is 2 bytes long and contains only
25
- * one 16­bit unsigned integer (word, little­endian), which is the
26
- * size of the decompressed data (spec 3.11)
27
- */
28
- const size = Buffer.alloc(2);
29
- size.writeInt16LE(dataBuffer.byteLength, 0);
30
- /** (spec 3.5) */
31
- const header = (0, utils_1.createHeader)();
32
- /** Merged binary data (spec 3.15.) */
33
- const merged = Buffer.concat([
34
- header,
35
- size,
36
- Buffer.concat(dataChunks)
159
+ const headerBysquare = createBysquareHeader();
160
+ const headerCompression = createCompresionHeader(dataBuffer.byteLength);
161
+ const mergeData = Buffer.concat([
162
+ headerBysquare,
163
+ headerCompression,
164
+ ...dataChunks
37
165
  ]);
38
- let paddedBinString = merged.reduce((acc, byte) => acc + byte.toString(2).padStart(8, "0"), "");
39
- let paddedBinLength = paddedBinString.length;
40
- const remainder = paddedBinLength % 5;
41
- if (remainder) {
42
- paddedBinString += Array(5 - remainder)
43
- .fill("0")
44
- .join("");
45
- paddedBinLength += 5 - remainder;
46
- }
47
- /**
48
- * Map a binary number of 5 bits to a string representation 2^5
49
- * '0123456789ABCDEFGHIJKLMNOPQRSTUV'[0...32] represents char
50
- */
51
- let output = "";
52
- for (let i = 0; i < paddedBinLength / 5; i++) {
53
- const binStart = 5 * i;
54
- const binEnd = 5 * i + 5;
55
- const slice = paddedBinString.slice(binStart, binEnd);
56
- const key = parseInt(slice, 2);
57
- output += utils_1.SUBST[key];
58
- }
59
- return resolve(output);
166
+ const output = alphanumericConversion(mergeData);
167
+ resolve(output);
60
168
  })
61
169
  .write(dataBuffer, (error) => {
62
- if (error) {
63
- reject(error);
64
- }
170
+ error && reject(error);
65
171
  encoder.end();
66
172
  });
67
173
  });
68
174
  }
69
- exports.default = generate;
175
+ exports.generate = generate;
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { default as parse } from "./parse";
2
- export { default as generate } from "./generate";
1
+ export { parse, detect } from "./parse";
2
+ export { generate } from "./generate";
3
3
  export * from "./types";
package/lib/index.js CHANGED
@@ -9,13 +9,11 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
9
9
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
10
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
11
  };
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
14
- };
15
12
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.generate = exports.parse = void 0;
13
+ exports.generate = exports.detect = exports.parse = void 0;
17
14
  var parse_1 = require("./parse");
18
- Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
15
+ Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parse_1.parse; } });
16
+ Object.defineProperty(exports, "detect", { enumerable: true, get: function () { return parse_1.detect; } });
19
17
  var generate_1 = require("./generate");
20
- Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return __importDefault(generate_1).default; } });
18
+ Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return generate_1.generate; } });
21
19
  __exportStar(require("./types"), exports);
package/lib/parse.d.ts CHANGED
@@ -1,3 +1,27 @@
1
- import { Model } from "./types";
2
- declare function parse(qr: string): Promise<Model>;
3
- export default parse;
1
+ /// <reference types="node" />
2
+ import { Model } from ".";
3
+ /**
4
+ * Generating by square Code
5
+ *
6
+ * @see {spec 3.14.}
7
+ */
8
+ export declare function createModel(tabbedString: string): Model;
9
+ /**
10
+ * Decoding client data from QR Code 2005 symbol
11
+ *
12
+ * @see {spec 3.16.}
13
+ */
14
+ export declare function parse(qr: string): Promise<Model>;
15
+ /**
16
+ * Reverse alphanumeric conversion using Base32hex
17
+ *
18
+ * @see {spec 3.13.}
19
+ */
20
+ export declare function inverseAlphanumericConversion(qr: string): Buffer;
21
+ /**
22
+ * Simple binary header detector
23
+ *
24
+ * The Bysquare header is pretty useless, so the detection isn't as reliable as
25
+ * I'd like
26
+ */
27
+ export declare function detect(qr: string): boolean;
package/lib/parse.js CHANGED
@@ -1,49 +1,137 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
4
20
  };
5
21
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const lzma_native_1 = __importDefault(require("lzma-native"));
7
- const utils_1 = require("./utils");
8
- function parse(qr) {
9
- const binary = [...qr].reduce((acc, char) => {
10
- acc += utils_1.SUBST.indexOf(char).toString(2).padStart(5, "0");
22
+ exports.detect = exports.inverseAlphanumericConversion = exports.parse = exports.createModel = void 0;
23
+ const lzma = __importStar(require("lzma-native"));
24
+ const _1 = require(".");
25
+ const generate_1 = require("./generate");
26
+ /**
27
+ * Generating by square Code
28
+ *
29
+ * @see {spec 3.14.}
30
+ */
31
+ function createModel(tabbedString) {
32
+ const model = tabbedString
33
+ .split("\t")
34
+ .filter((ch) => !(ch === "\x00"))
35
+ .reduce((acc, value, i) => {
36
+ const key = _1.ModelOrdered[i];
37
+ if (value === "") {
38
+ return acc;
39
+ }
40
+ const numericKeys = [
41
+ "Payments",
42
+ "PaymentOptions",
43
+ "Amount",
44
+ "BankAccounts",
45
+ "StandingOrderExt",
46
+ "Day",
47
+ "Month",
48
+ "DirectDebitExt",
49
+ "DirectDebitScheme",
50
+ "DirectDebitType",
51
+ "MaxAmount"
52
+ ];
53
+ if (numericKeys.includes(key)) {
54
+ acc[key] = Number(value);
55
+ return acc;
56
+ }
57
+ acc[key] = value;
11
58
  return acc;
12
- }, "");
13
- let bytes = [];
14
- for (let nth = 0, leftCount = 0; binary.length > leftCount; nth++) {
15
- /** string representation of 8xbits */
16
- const slice = binary.slice(leftCount, (leftCount += 8));
17
- const byte = parseInt(slice, 2);
18
- bytes[nth] = byte;
19
- }
20
- const input = Buffer.from(bytes);
21
- // const header = inpEditorConfig.EditorConfigut.slice(0, 2)
22
- // const size = input.slice(2, 4)
23
- const data = input.slice(4);
59
+ }, {});
60
+ return model;
61
+ }
62
+ exports.createModel = createModel;
63
+ /**
64
+ * Decoding client data from QR Code 2005 symbol
65
+ *
66
+ * @see {spec 3.16.}
67
+ */
68
+ function parse(qr) {
69
+ const inversed = inverseAlphanumericConversion(qr);
70
+ const headerBysquare = inversed.slice(0, 2);
71
+ const headerCompression = inversed.slice(2, 4);
72
+ const compressedData = inversed.slice(4);
24
73
  // @ts-ignore: Missing decored types
25
- const decoder = lzma_native_1.default.createStream("rawDecoder", {
74
+ const decoder = lzma.createStream("rawDecoder", {
26
75
  synchronous: true,
27
76
  // @ts-ignore: Missing filter types
28
- filters: [{ id: lzma_native_1.default.FILTER_LZMA1 }]
77
+ filters: [{ id: lzma.FILTER_LZMA1 }]
29
78
  });
30
79
  return new Promise((resolve, reject) => {
31
80
  decoder
32
81
  .on("error", reject)
33
82
  .on("data", (decompress) => {
34
- const checksum = decompress.slice(0, 4);
83
+ const _crc32 = decompress.slice(0, 4);
35
84
  const data = decompress.slice(4).toString();
36
- // TODO: Not neccesary to validate, but data can be corrupted
37
- // if (!createChecksum(data).equals(checksum)) {
38
- // reject("Checksum conflict")
39
- // }
40
- const model = (0, utils_1.createModel)(data);
85
+ const model = createModel(data);
41
86
  resolve(model);
42
87
  })
43
- .write(data, (err) => {
44
- err && reject(err);
88
+ .write(compressedData, (error) => {
89
+ error && reject(error);
45
90
  decoder.end();
46
91
  });
47
92
  });
48
93
  }
49
- exports.default = parse;
94
+ exports.parse = parse;
95
+ /**
96
+ * Reverse alphanumeric conversion using Base32hex
97
+ *
98
+ * @see {spec 3.13.}
99
+ */
100
+ function inverseAlphanumericConversion(qr) {
101
+ const binary = [...qr].reduce((acc, char) => {
102
+ acc += generate_1.SUBST.indexOf(char).toString(2).padStart(5, "0");
103
+ return acc;
104
+ }, "");
105
+ let bytes = [];
106
+ for (let nth = 0, leftCount = 0; binary.length > leftCount; nth++) {
107
+ /** string representation of 8-bits */
108
+ const slice = binary.slice(leftCount, (leftCount += 8));
109
+ const byte = parseInt(slice, 2);
110
+ bytes[nth] = byte;
111
+ }
112
+ return Buffer.from(bytes);
113
+ }
114
+ exports.inverseAlphanumericConversion = inverseAlphanumericConversion;
115
+ /**
116
+ * Simple binary header detector
117
+ *
118
+ * The Bysquare header is pretty useless, so the detection isn't as reliable as
119
+ * I'd like
120
+ */
121
+ function detect(qr) {
122
+ const inversed = inverseAlphanumericConversion(qr);
123
+ if (inversed.byteLength < 2) {
124
+ return false;
125
+ }
126
+ const headerBysquare = inversed.slice(0, 2);
127
+ return [...headerBysquare.toString("hex")]
128
+ .map((nibble) => parseInt(nibble, 16))
129
+ .every((nibble, index) => {
130
+ // check version, actual v1.x.x
131
+ if (index === 1) {
132
+ return nibble <= 2;
133
+ }
134
+ return 0 <= nibble && nibble <= 15;
135
+ });
136
+ }
137
+ exports.detect = detect;
package/lib/types.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Table 15. PAY by square sequence data model (page 30.)
3
- * @see{./docs/specification_v1.1.0.pdf}
4
3
  */
5
4
  export interface Model {
6
5
  /**
@@ -12,8 +11,6 @@ export interface Model {
12
11
  */
13
12
  Payments: number;
14
13
  /**
15
- * Needs to be filled in with “paymentorder” option
16
- *
17
14
  * Max length 1
18
15
  */
19
16
  PaymentOptions: number;
@@ -72,8 +69,9 @@ export interface Model {
72
69
  */
73
70
  IBAN: string;
74
71
  /**
75
- * Max length 11
76
72
  * Format ISO 9362, 8 or 11 characters long
73
+ *
74
+ * Max length 11
77
75
  */
78
76
  BIC?: string;
79
77
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bysquare",
3
3
  "description": "It's a national standard for payment QR codes adopted by Slovak Banking Association (SBA)",
4
- "version": "1.2.1",
4
+ "version": "1.2.2",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Filip Seman <filip.seman@protonmail.com>",
7
7
  "keywords": [
@@ -21,6 +21,7 @@
21
21
  "homepage": "https://github.com/xseman/bysquare#readme",
22
22
  "funding": "https://github.com/sponsors/xseman",
23
23
  "scripts": {
24
+ "prebuild": "rm -rf ./lib || :",
24
25
  "build": "tsc && chmod +x ./lib/cli.js",
25
26
  "test": "vitest",
26
27
  "test:watch": "vitest --watch",
@@ -28,7 +29,7 @@
28
29
  "format": "npx prettier --write ./*/**.ts --ignore-path ./.gitignore",
29
30
  "version": "git flow release start v$npm_package_version",
30
31
  "prepare": "",
31
- "prepublishOnly": "rm -rf ./lib && npm run build",
32
+ "prepublishOnly": "npm run build",
32
33
  "preversion": "git checkout develop",
33
34
  "postversion": ""
34
35
  },
package/lib/utils.d.ts DELETED
@@ -1,32 +0,0 @@
1
- /// <reference types="node" />
2
- import { Model } from "./types";
3
- /**
4
- * spec 3.13 (Table 9 – Encoding table)
5
- */
6
- declare const SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
7
- /**
8
- * ```
9
- * | Attribute | Number of bits | Possible values | Note
10
- * --------------------------------------------------------------------------------------------
11
- * | BySquareType | 4 | 0-15 | by square type
12
- * | Version | 4 | 0-15 | version 4 0­15 version of the by sq
13
- * | DocumentType | 4 | 0-15 | document type within given by square type
14
- * | Reserved | 4 | 0-15 | bits reserved for future needs
15
- * ```
16
- */
17
- declare function createHeader(header?: [
18
- BySquareType: number,
19
- Version: number,
20
- DocumentType: number,
21
- Reserved: number
22
- ]): Buffer;
23
- declare function createChecksum(tabbedInput: string): Buffer;
24
- declare function dataWithChecksum(model: Model): Buffer;
25
- /**
26
- * Order keys by specification
27
- * Fill empty values
28
- * Transform to tabbed string
29
- */
30
- declare function createTabbedString(model: Model): string;
31
- declare function createModel(tabbedString: string): Model;
32
- export { createChecksum, createHeader, createModel, createTabbedString, dataWithChecksum, SUBST };
package/lib/utils.js DELETED
@@ -1,106 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SUBST = exports.dataWithChecksum = exports.createTabbedString = exports.createModel = exports.createHeader = exports.createChecksum = void 0;
7
- const lzma_native_1 = __importDefault(require("lzma-native"));
8
- const types_1 = require("./types");
9
- /**
10
- * spec 3.13 (Table 9 – Encoding table)
11
- */
12
- const SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
13
- exports.SUBST = SUBST;
14
- /**
15
- * ```
16
- * | Attribute | Number of bits | Possible values | Note
17
- * --------------------------------------------------------------------------------------------
18
- * | BySquareType | 4 | 0-15 | by square type
19
- * | Version | 4 | 0-15 | version 4 0­15 version of the by sq
20
- * | DocumentType | 4 | 0-15 | document type within given by square type
21
- * | Reserved | 4 | 0-15 | bits reserved for future needs
22
- * ```
23
- */
24
- function createHeader(
25
- // prettier-ignore
26
- header = [
27
- 0b0000_0000, 0b0000_0000,
28
- 0b0000_0000, 0b0000_0000
29
- ]) {
30
- const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15);
31
- if (!isValid)
32
- throw new Error();
33
- const [BySquareType, Version, DocumentType, Reserved] = header;
34
- /** Combine 4-nibbles to 2-bytes */
35
- const headerBuffer = Buffer.from([
36
- (BySquareType << 4) | (Version << 0),
37
- (DocumentType << 4) | (Reserved << 0)
38
- ]);
39
- return headerBuffer;
40
- }
41
- exports.createHeader = createHeader;
42
- function createChecksum(tabbedInput) {
43
- // @ts-ignore: Wrong return type
44
- const data = lzma_native_1.default.crc32(tabbedInput);
45
- const crc32 = Buffer.alloc(4);
46
- crc32.writeUInt32LE(data);
47
- return crc32;
48
- }
49
- exports.createChecksum = createChecksum;
50
- function dataWithChecksum(model) {
51
- const tabbedString = createTabbedString(model);
52
- const checksum = createChecksum(tabbedString);
53
- const merged = Buffer.concat([
54
- checksum,
55
- Buffer.from(tabbedString, "utf-8")
56
- ]);
57
- return merged;
58
- }
59
- exports.dataWithChecksum = dataWithChecksum;
60
- /**
61
- * Order keys by specification
62
- * Fill empty values
63
- * Transform to tabbed string
64
- */
65
- function createTabbedString(model) {
66
- const tabbedModel = Object.keys(model)
67
- .reduce((acc, key) => {
68
- acc[types_1.ModelOrdered[key]] = String(model[key] ?? "");
69
- return acc;
70
- }, Array(33).fill(""))
71
- .join("\t");
72
- return tabbedModel;
73
- }
74
- exports.createTabbedString = createTabbedString;
75
- function createModel(tabbedString) {
76
- const model = tabbedString
77
- .split("\t")
78
- .filter((ch) => !(ch === "\x00"))
79
- .reduce((acc, value, i) => {
80
- const key = types_1.ModelOrdered[i];
81
- if (value === "") {
82
- return acc;
83
- }
84
- const numericKeys = [
85
- "Payments",
86
- "PaymentOptions",
87
- "Amount",
88
- "BankAccounts",
89
- "StandingOrderExt",
90
- "Day",
91
- "Month",
92
- "DirectDebitExt",
93
- "DirectDebitScheme",
94
- "DirectDebitType",
95
- "MaxAmount"
96
- ];
97
- if (numericKeys.includes(key)) {
98
- acc[key] = Number(value);
99
- return acc;
100
- }
101
- acc[key] = value;
102
- return acc;
103
- }, {});
104
- return model;
105
- }
106
- exports.createModel = createModel;