bysquare 1.0.8 → 1.1.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
@@ -17,6 +17,10 @@ other payment regulations.
17
17
  This library is un-opinionated. Image generation from qr-code string depends on
18
18
  your implementation. See [examples](examples).
19
19
 
20
+ ## How it works
21
+
22
+ ![diagram](./uml/logic.svg)
23
+
20
24
  ## Install
21
25
 
22
26
  Node.js
@@ -33,12 +37,10 @@ npm install --global bysquare
33
37
 
34
38
  ## API
35
39
 
36
- ### `function generate(model: Model): Promise<string>`
37
-
38
- **Example (TypeScript)**
40
+ ### **generate(model: Model): Promise<string>**
39
41
 
40
42
  ```typescript
41
- import { generate, Model } from "bysquare";
43
+ import { generate, parse, Model } from "bysquare";
42
44
 
43
45
  const model: Model = {
44
46
  IBAN: "SK9611000000002918599669",
@@ -50,40 +52,28 @@ const model: Model = {
50
52
  BankAccounts: 1,
51
53
  };
52
54
 
53
- generate(model).then((qrString) => {
55
+ generate(model).then((qrString: string) => {
56
+ // "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000"
54
57
  // your logic...
55
58
  });
56
59
  ```
57
60
 
58
- ### `function parse(qrString: string): Promise<Model>`
59
-
60
- **Example (TypeScript)**
61
+ ### **parse(qrString: string): Promise<Model>**
61
62
 
62
63
  ```typescript
63
- import { parse } from "bysquare";
64
-
65
- const generated = "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000";
66
-
67
- parse(generated).then((model) => {
68
- console.log(model);
69
- // {
70
- // IBAN: 'SK9611000000002918599669',
71
- // Amount: 100.0,
72
- // CurrencyCode: "EUR",
73
- // VariableSymbol: "123",
74
- // Payments: 1,
75
- // PaymentOptions: 1,
76
- // BankAccounts: 1,
77
- // };
64
+ import { parse, Model } from "bysquare";
65
+
66
+ const qrString = "0004G0005ES17OQ09C98Q7ME34TCR3V71LVKD2AE6EGHKR82DKS5NBJ3331VUFQIV0JGMR743UJCKSAKEM9QGVVVOIVH000"
67
+
68
+ parse(qrString).then((model: Model) => {
69
+ // your logic...
78
70
  });
79
71
  ```
80
72
 
81
- ### CLI
73
+ ## CLI
82
74
 
83
75
  You can use json file with valid model to generate qr-string.
84
76
 
85
- **Example**
86
-
87
77
  ```sh
88
78
  # example.json
89
79
  # {
@@ -102,8 +92,6 @@ You can use json file with valid model to generate qr-string.
102
92
 
103
93
  You can also use stdin.
104
94
 
105
- **Example**
106
-
107
95
  ```sh
108
96
  > echo '
109
97
  {
@@ -158,10 +146,6 @@ You can also use stdin.
158
146
  | BeneficiaryAddressLine1 | `string` | no |
159
147
  | BeneficiaryAddressLine2 | `string` | no |
160
148
 
161
- ## How it works
162
-
163
- ![diagram](./uml/logic.svg)
164
-
165
149
  ## Resources
166
150
 
167
151
  - <https://bysquare.com/>
package/lib/cli.js CHANGED
@@ -41,11 +41,12 @@ var fs_1 = require("fs");
41
41
  var readline_1 = require("readline");
42
42
  var main_1 = require("./main");
43
43
  if (process.stdin.isTTY) {
44
- // bysquare "file"
44
+ /** bysquare "file" */
45
45
  handleInput(process.argv[2]);
46
46
  }
47
47
  else {
48
- // echo "data" | bysquare
48
+ /** echo "data" | bysquare */
49
+ ;
49
50
  (function () { return __awaiter(void 0, void 0, void 0, function () {
50
51
  var stdin, qrString;
51
52
  return __generator(this, function (_a) {
@@ -101,9 +102,8 @@ function jsonStringToQrString(stdin) {
101
102
  return [2 /*return*/, new Promise(function (resolve, reject) {
102
103
  try {
103
104
  var data = JSON.parse(stdin);
104
- (0, main_1.generate)(data).then(function (qrString) {
105
- resolve(qrString);
106
- });
105
+ var qrString = (0, main_1.generate)(data);
106
+ resolve(qrString);
107
107
  }
108
108
  catch (e) {
109
109
  reject(e);
@@ -119,7 +119,7 @@ function handleStdin() {
119
119
  readline = (0, readline_1.createInterface)({
120
120
  input: process.stdin,
121
121
  output: process.stdout,
122
- terminal: false,
122
+ terminal: false
123
123
  });
124
124
  lines = [];
125
125
  return [2 /*return*/, new Promise(function (resolve, reject) {
@@ -146,7 +146,6 @@ function help() {
146
146
  "",
147
147
  "Flags:",
148
148
  " -h, --help display this help and exit",
149
- " -v, --version display actual version",
150
149
  "",
151
150
  "If <file> is omitted, reads from stdin.",
152
151
  "",
@@ -154,7 +153,7 @@ function help() {
154
153
  " bysquare ./example.json",
155
154
  "",
156
155
  " echo ",
157
- ' {',
156
+ " {",
158
157
  ' "IBAN": "SK9611000000002918599669"',
159
158
  ' "Amount": 100.0',
160
159
  ' "CurrencyCode": "EUR"',
@@ -163,6 +162,6 @@ function help() {
163
162
  ' "PaymentOptions": 1',
164
163
  ' "BankAccounts": 1',
165
164
  " }'",
166
- " | bysquare",
165
+ " | bysquare"
167
166
  ].join("\n");
168
167
  }
package/lib/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export * from './main';
2
- export * from './types';
2
+ export * from './schema';
package/lib/index.js CHANGED
@@ -11,4 +11,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  __exportStar(require("./main"), exports);
14
- __exportStar(require("./types"), exports);
14
+ __exportStar(require("./schema"), exports);
package/lib/main.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { Model } from './types';
3
- export declare function generate(model: Model): Promise<string>;
2
+ import { Model } from "./schema";
4
3
  /**
5
4
  * ```
6
5
  * | Attribute | Number of bits | Possible values | Note
@@ -17,8 +16,9 @@ export declare function createHeader(header?: [
17
16
  DocumentType: number,
18
17
  Reserved: number
19
18
  ]): Buffer;
20
- export declare function tabbedStringWithChecksumBuffer(model: Model): Buffer;
21
- export declare function checksumFromTabbedString(tabbedString: string): Buffer;
19
+ export declare function createChecksum(tabbedInput: string): Buffer;
20
+ export declare function dataWithChecksum(model: Model): Buffer;
21
+ export declare function generate(model: Model): Promise<string>;
22
22
  export declare function createTabbedString(model: Model): string;
23
- export declare function createModelFromTabbedString(tabbedModel: string): Model;
24
- export declare function parse(qrString: string): Promise<Model>;
23
+ export declare function createModel(tabbedString: string): Model;
24
+ export declare function parse(qr: string): Promise<Model>;
package/lib/main.js CHANGED
@@ -24,71 +24,13 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
24
24
  }
25
25
  return to.concat(ar || Array.prototype.slice.call(from));
26
26
  };
27
+ var __importDefault = (this && this.__importDefault) || function (mod) {
28
+ return (mod && mod.__esModule) ? mod : { "default": mod };
29
+ };
27
30
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.parse = exports.createModelFromTabbedString = exports.createTabbedString = exports.checksumFromTabbedString = exports.tabbedStringWithChecksumBuffer = exports.createHeader = exports.generate = void 0;
29
- var types_1 = require("./types");
30
- var lzma = require('lzma-native');
31
- function generate(model) {
32
- var dataBufferWithChecksum = tabbedStringWithChecksumBuffer(model);
33
- var encoder = lzma.createStream('rawEncoder', {
34
- synchronous: true,
35
- filters: [{ id: lzma.FILTER_LZMA1 }],
36
- });
37
- var data = [];
38
- encoder.on('data', function (chunk) {
39
- data.push(chunk);
40
- });
41
- var compressPromise = new Promise(function (resolve, reject) {
42
- encoder.write(dataBufferWithChecksum, function () {
43
- encoder.end();
44
- });
45
- encoder.on('error', reject);
46
- encoder.on('end', function () {
47
- /** (spec 3.5) */
48
- var bySquareHeader = createHeader();
49
- /**
50
- * The header of compressed data is 2 bytes long and contains only
51
- * one 16­bit unsigned integer (word, little­endian), which is the
52
- * size of the decompressed data (spec 3.11)
53
- */
54
- var decompressedSize = Buffer.alloc(2);
55
- decompressedSize.writeInt16LE(dataBufferWithChecksum.byteLength, 0);
56
- /**
57
- * Merged binary data (spec 3.15.)
58
- */
59
- var merged = Buffer.concat([
60
- bySquareHeader,
61
- decompressedSize,
62
- Buffer.concat(data),
63
- ]);
64
- var paddedBinString = merged.reduce(function (acc, byte) { return (acc += byte.toString(2).padStart(8, '0')); }, '');
65
- var paddedBinLength = paddedBinString.length;
66
- var remainder = paddedBinLength % 5;
67
- if (remainder) {
68
- paddedBinString += Array(5 - remainder)
69
- .fill('0')
70
- .join('');
71
- paddedBinLength += 5 - remainder;
72
- }
73
- /**
74
- * Map a binary number of 5 bits to a string representation 2^5
75
- * '0123456789ABCDEFGHIJKLMNOPQRSTUV'[0...32] represents char
76
- */
77
- var subst = '0123456789ABCDEFGHIJKLMNOPQRSTUV';
78
- var output = '';
79
- for (var i = 0; i < paddedBinLength / 5; i++) {
80
- var binStart = 5 * i;
81
- var binEnd = 5 * i + 5;
82
- var slice = paddedBinString.slice(binStart, binEnd);
83
- var key = parseInt(slice, 2);
84
- output += subst[key];
85
- }
86
- resolve(output);
87
- });
88
- });
89
- return compressPromise;
90
- }
91
- exports.generate = generate;
31
+ exports.parse = exports.createModel = exports.createTabbedString = exports.generate = exports.dataWithChecksum = exports.createChecksum = exports.createHeader = void 0;
32
+ var lzma_native_1 = __importDefault(require("lzma-native"));
33
+ var schema_1 = require("./schema");
92
34
  /**
93
35
  * ```
94
36
  * | Attribute | Number of bits | Possible values | Note
@@ -99,7 +41,9 @@ exports.generate = generate;
99
41
  * | Reserved | 4 | 0-15 | bits reserved for future needs
100
42
  * ```
101
43
  */
102
- function createHeader(header) {
44
+ function createHeader(
45
+ // prettier-ignore
46
+ header) {
103
47
  if (header === void 0) { header = [
104
48
  0,
105
49
  0,
@@ -113,27 +57,93 @@ function createHeader(header) {
113
57
  /** Combine 4-nibbles to 2-bytes */
114
58
  var headerBuffer = Buffer.from([
115
59
  (BySquareType << 4) | (Version << 0),
116
- (DocumentType << 4) | (Reserved << 0),
60
+ (DocumentType << 4) | (Reserved << 0)
117
61
  ]);
118
62
  return headerBuffer;
119
63
  }
120
64
  exports.createHeader = createHeader;
121
- function tabbedStringWithChecksumBuffer(model) {
65
+ function createChecksum(tabbedInput) {
66
+ // @ts-ignore: Wrong return type
67
+ var data = lzma_native_1.default.crc32(tabbedInput);
68
+ var crc32 = Buffer.alloc(4);
69
+ crc32.writeUInt32LE(data);
70
+ return crc32;
71
+ }
72
+ exports.createChecksum = createChecksum;
73
+ function dataWithChecksum(model) {
122
74
  var tabbedString = createTabbedString(model);
123
- var checksum = checksumFromTabbedString(tabbedString);
124
- var data = Buffer.concat([
75
+ var checksum = createChecksum(tabbedString);
76
+ var merged = Buffer.concat([
125
77
  checksum,
126
- Buffer.from(tabbedString, 'utf-8'),
78
+ Buffer.from(tabbedString, "utf-8")
127
79
  ]);
128
- return data;
80
+ return merged;
129
81
  }
130
- exports.tabbedStringWithChecksumBuffer = tabbedStringWithChecksumBuffer;
131
- function checksumFromTabbedString(tabbedString) {
132
- var crc32 = Buffer.alloc(4);
133
- crc32.writeInt32LE(lzma.crc32(tabbedString), 0);
134
- return crc32;
82
+ exports.dataWithChecksum = dataWithChecksum;
83
+ /**
84
+ * spec 3.13 (Table 9 – Encoding table)
85
+ */
86
+ var SUBST = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
87
+ function generate(model) {
88
+ var dataBuffer = dataWithChecksum(model);
89
+ var dataChunks = [];
90
+ return new Promise(function (resolve, reject) {
91
+ var encoder = lzma_native_1.default.createStream("rawEncoder", {
92
+ synchronous: true,
93
+ // @ts-ignore: Missing filter types
94
+ filters: [{ id: lzma_native_1.default.FILTER_LZMA1 }]
95
+ });
96
+ encoder
97
+ .on("data", function (chunk) {
98
+ dataChunks.push(chunk);
99
+ })
100
+ .on("error", Promise.reject)
101
+ .on("end", function () {
102
+ /**
103
+ * The header of compressed data is 2 bytes long and contains only
104
+ * one 16­bit unsigned integer (word, little­endian), which is the
105
+ * size of the decompressed data (spec 3.11)
106
+ */
107
+ var size = Buffer.alloc(2);
108
+ size.writeInt16LE(dataBuffer.byteLength, 0);
109
+ /** (spec 3.5) */
110
+ var header = createHeader();
111
+ /** Merged binary data (spec 3.15.) */
112
+ var merged = Buffer.concat([
113
+ header,
114
+ size,
115
+ Buffer.concat(dataChunks)
116
+ ]);
117
+ var paddedBinString = merged.reduce(function (acc, byte) { return acc + byte.toString(2).padStart(8, "0"); }, "");
118
+ var paddedBinLength = paddedBinString.length;
119
+ var remainder = paddedBinLength % 5;
120
+ if (remainder) {
121
+ paddedBinString += Array(5 - remainder)
122
+ .fill("0")
123
+ .join("");
124
+ paddedBinLength += 5 - remainder;
125
+ }
126
+ /**
127
+ * Map a binary number of 5 bits to a string representation 2^5
128
+ * '0123456789ABCDEFGHIJKLMNOPQRSTUV'[0...32] represents char
129
+ */
130
+ var output = "";
131
+ for (var i = 0; i < paddedBinLength / 5; i++) {
132
+ var binStart = 5 * i;
133
+ var binEnd = 5 * i + 5;
134
+ var slice = paddedBinString.slice(binStart, binEnd);
135
+ var key = parseInt(slice, 2);
136
+ output += SUBST[key];
137
+ }
138
+ return resolve(output);
139
+ })
140
+ .write(dataBuffer, function (err) {
141
+ err && reject(err);
142
+ encoder.end();
143
+ });
144
+ });
135
145
  }
136
- exports.checksumFromTabbedString = checksumFromTabbedString;
146
+ exports.generate = generate;
137
147
  function createTabbedString(model) {
138
148
  /**
139
149
  * Order keys by specification
@@ -143,35 +153,36 @@ function createTabbedString(model) {
143
153
  var tabbedModel = Object.keys(model)
144
154
  .reduce(function (acc, key) {
145
155
  var _a;
146
- acc[types_1.ModelOrdered[key]] = String((_a = model[key]) !== null && _a !== void 0 ? _a : '');
156
+ acc[schema_1.ModelOrdered[key]] = String((_a = model[key]) !== null && _a !== void 0 ? _a : "");
147
157
  return acc;
148
- }, Array(33).fill(''))
149
- .join('\t');
158
+ }, Array(33).fill(""))
159
+ .join("\t");
150
160
  return tabbedModel;
151
161
  }
152
162
  exports.createTabbedString = createTabbedString;
153
- function createModelFromTabbedString(tabbedModel) {
154
- var model = tabbedModel
155
- .split('\t')
163
+ function createModel(tabbedString) {
164
+ var model = tabbedString
165
+ .split("\t")
166
+ .filter(function (ch) { return !(ch === "\x00"); })
156
167
  .reduce(function (acc, value, i) {
157
- var key = types_1.ModelOrdered[i];
158
- if (value === '') {
168
+ var key = schema_1.ModelOrdered[i];
169
+ if (value === "") {
159
170
  return acc;
160
171
  }
161
- var numberKeys = [
162
- 'Payments',
163
- 'PaymentOptions',
164
- 'Amount',
165
- 'BankAccounts',
166
- 'StandingOrderExt',
167
- 'Day',
168
- 'Month',
169
- 'DirectDebitExt',
170
- 'DirectDebitScheme',
171
- 'DirectDebitType',
172
- 'MaxAmount',
172
+ var numericKeys = [
173
+ "Payments",
174
+ "PaymentOptions",
175
+ "Amount",
176
+ "BankAccounts",
177
+ "StandingOrderExt",
178
+ "Day",
179
+ "Month",
180
+ "DirectDebitExt",
181
+ "DirectDebitScheme",
182
+ "DirectDebitType",
183
+ "MaxAmount"
173
184
  ];
174
- if (!!Number(value) && numberKeys.includes(key)) {
185
+ if (numericKeys.includes(key)) {
175
186
  acc[key] = Number(value);
176
187
  return acc;
177
188
  }
@@ -180,46 +191,44 @@ function createModelFromTabbedString(tabbedModel) {
180
191
  }, {});
181
192
  return model;
182
193
  }
183
- exports.createModelFromTabbedString = createModelFromTabbedString;
184
- function parse(qrString) {
185
- var subst = '0123456789ABCDEFGHIJKLMNOPQRSTUV';
186
- var paddedBinString = __spreadArray([], __read(qrString), false).reduce(function (acc, char) {
187
- acc += subst.indexOf(char).toString(2).padStart(5, '0');
194
+ exports.createModel = createModel;
195
+ function parse(qr) {
196
+ var binary = __spreadArray([], __read(qr), false).reduce(function (acc, char) {
197
+ acc += SUBST.indexOf(char).toString(2).padStart(5, "0");
188
198
  return acc;
189
- }, '');
199
+ }, "");
190
200
  var bytes = [];
191
- for (var count = 0, leftCount = 0; paddedBinString.length > leftCount; count++) {
192
- var byte = parseInt(paddedBinString.slice(leftCount, (leftCount += 8)), 2)
193
- .toString(16)
194
- .padStart(2, '0');
195
- bytes[count] = byte;
201
+ for (var nth = 0, leftCount = 0; binary.length > leftCount; nth++) {
202
+ var byte = parseInt(binary.slice(leftCount, (leftCount += 8)), 2);
203
+ bytes[nth] = byte;
196
204
  }
197
- var binaryData = Buffer.from(bytes.join(''), 'hex');
198
- var _header = binaryData.slice(0, 2);
199
- var _decompressSize = binaryData.slice(2, 4);
200
- var data = binaryData.slice(4, binaryData.length);
201
- var decoder = lzma.createStream('rawDecoder', {
205
+ var input = Buffer.from(bytes);
206
+ // const header = input.slice(0, 2)
207
+ // const size = input.slice(2, 4)
208
+ var data = input.slice(4);
209
+ // @ts-ignore: Missing decored types
210
+ var decoder = lzma_native_1.default.createStream("rawDecoder", {
202
211
  synchronous: true,
203
- filters: [{ id: lzma.FILTER_LZMA1 }],
212
+ // @ts-ignore: Missing filter types
213
+ filters: [{ id: lzma_native_1.default.FILTER_LZMA1 }]
204
214
  });
205
- decoder.write(data, undefined, function () {
206
- decoder.end();
207
- });
208
- var paymentPromise = new Promise(function (resolve, reject) {
209
- decoder.on('error', reject);
210
- decoder.on('data', function (decompressed) {
211
- var checksum = decompressed.slice(0, 4);
212
- var data = decompressed.slice(4, decompressed.length);
213
- var crc32 = Buffer.alloc(4);
214
- crc32.writeInt32LE(lzma.crc32(data), 0);
215
- if (!crc32.equals(checksum)) {
216
- reject('Checksum conflict');
217
- }
218
- var decoded = data.toString();
219
- var model = createModelFromTabbedString(decoded);
215
+ return new Promise(function (resolve, reject) {
216
+ decoder
217
+ .on("error", reject)
218
+ .on("data", function (decompress) {
219
+ var checksum = decompress.slice(0, 4);
220
+ var data = decompress.slice(4).toString();
221
+ // TODO: Not neccesary to validate, but data can be corrupted
222
+ // if (!createChecksum(data).equals(checksum)) {
223
+ // reject("Checksum conflict")
224
+ // }
225
+ var model = createModel(data);
220
226
  resolve(model);
227
+ })
228
+ .write(data, function (err) {
229
+ err && reject(err);
230
+ decoder.end();
221
231
  });
222
232
  });
223
- return paymentPromise;
224
233
  }
225
234
  exports.parse = parse;
@@ -1,4 +1,6 @@
1
1
  /**
2
+ * TODO: yup schema, infer interface
3
+ *
2
4
  * Data model
3
5
  * (Appendix D, table)
4
6
  */
File without changes
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.0.8",
4
+ "version": "1.1.0",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Filip Seman <filip.seman@protonmail.com>",
7
7
  "keywords": [
@@ -34,19 +34,24 @@
34
34
  "lzma-native": "8.0.6"
35
35
  },
36
36
  "devDependencies": {
37
+ "@types/lzma-native": "^4.0.1",
37
38
  "@types/node": "14.14.20",
38
39
  "typescript": "4.5.5",
39
40
  "xv": "1.1.1"
40
41
  },
41
- "engines": {
42
- "node": ">=14.14.x",
43
- "npm": ">= 7.0"
44
- },
45
42
  "files": [
46
43
  "lib",
47
44
  "!lib/*.test.*"
48
45
  ],
49
46
  "bin": "lib/cli.js",
50
47
  "main": "lib/index.js",
51
- "types": "lib/index.d.ts"
48
+ "types": "lib/index.d.ts",
49
+ "engines": {
50
+ "node": ">=14.14.x",
51
+ "npm": ">= 7.0"
52
+ },
53
+ "os": [
54
+ "darwin",
55
+ "linux"
56
+ ]
52
57
  }