bysquare 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.
@@ -1,14 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generate = exports.toIntermediate = exports.prepareCompression = exports.checksum = exports.bysquareHeader = void 0;
29
+ exports.generate = exports.serialize = exports.addChecksum = exports.headerDataLength = exports.headerBysquare = void 0;
30
+ const crc_32_1 = __importDefault(require("crc-32"));
7
31
  const lodash_deburr_1 = __importDefault(require("lodash.deburr"));
8
- const lzma_native_1 = __importDefault(require("lzma-native"));
32
+ const lzma = __importStar(require("lzma1"));
9
33
  const rfc4648_1 = require("rfc4648");
10
34
  const types_js_1 = require("./types.js");
11
- // echo "Hello" | xz --format=raw --lzma1=lc=3,lp=0,pb=2,dict=32KiB --stdout | hexdump -C
12
35
  /**
13
36
  * Returns a 2 byte buffer that represents the header of the bysquare
14
37
  * specification
@@ -22,129 +45,132 @@ const types_js_1 = require("./types.js");
22
45
  * | Reserved | 4 | 0-15 | bits reserved for future needs
23
46
  * ```
24
47
  *
25
- * @see 3.5. by square header
48
+ * @see 3.5.
26
49
  */
27
- function bysquareHeader(header = [
28
- 0, 0,
29
- 0, 0
50
+ function headerBysquare(
51
+ /** dprint-ignore */
52
+ header = [
53
+ 0x00, 0x00,
54
+ 0x00, 0x00
30
55
  ]) {
31
56
  const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15);
32
57
  if (!isValid) {
33
- throw new Error(`Invalid header byte value, valid range <0,15>`);
58
+ throw new Error("Invalid header byte value, valid range <0,15>");
34
59
  }
35
60
  const [bySquareType, version, documentType, reserved] = header;
36
61
  // Combine 4-nibbles to 2-bytes
37
- const mergedNibbles = Buffer.from([
62
+ const mergedNibbles = Uint8Array.from([
38
63
  (bySquareType << 4) | (version << 0),
39
- (documentType << 4) | (reserved << 0),
64
+ (documentType << 4) | (reserved << 0)
40
65
  ]);
41
66
  return mergedNibbles;
42
67
  }
43
- exports.bysquareHeader = bysquareHeader;
68
+ exports.headerBysquare = headerBysquare;
44
69
  /**
45
- * Allocates a new buffer of a 2 bytes that represents LZMA header which
46
- * contains 16-bit unsigned integer (word, little-endian), which is the size of
47
- * the decompressed data. Therefore the maximum size of compressed data is
48
- * limited to 65535
70
+ * The function first sets default values for the lc, lp, and pb properties,
71
+ * which represent the number of literal context bits, literal position bits,
72
+ * and position bits, respectively. These values are then used to calculate the
73
+ * properties value, which is a single byte that encodes all three properties.
49
74
  *
50
- * @see 3.11. LZMA Compression
75
+ * @see 3.11.
51
76
  */
52
- function lzmaHeader(decompressedData) {
53
- const bytesCount = decompressedData.length;
54
- if (bytesCount >= 2 ** 16) {
55
- throw new Error("The maximum compressed data size has been reached");
56
- }
57
- const dataSize = Buffer.alloc(2);
58
- dataSize.writeInt16LE(bytesCount);
59
- return dataSize;
77
+ function headerLzmaProps() {
78
+ const lc = 3;
79
+ const lp = 0;
80
+ const pb = 2;
81
+ const properties = (((pb * 5) + lp) * 9) + lc;
82
+ const header = new ArrayBuffer(1);
83
+ new DataView(header).setUint8(0, properties);
84
+ return new Uint8Array(header);
60
85
  }
61
86
  /**
62
- * @see 3.10 Appending CRC32 checksum
87
+ * Creates a one-byte array that represents the length of compressed data in
88
+ * combination with CRC32 in bytes.
63
89
  */
64
- function checksum(intermediate) {
65
- // @ts-ignore: Wrong return type
66
- const data = lzma_native_1.default.crc32(intermediate);
67
- const crc32 = Buffer.alloc(4);
68
- crc32.writeUInt32LE(data);
69
- return crc32;
90
+ function headerDataLength(length) {
91
+ if (length >= 2 ** 16) {
92
+ throw new Error("The maximum compressed data size has been reached");
93
+ }
94
+ const header = new ArrayBuffer(1);
95
+ new DataView(header).setUint8(0, length);
96
+ return new Uint8Array(header);
70
97
  }
71
- exports.checksum = checksum;
98
+ exports.headerDataLength = headerDataLength;
72
99
  /**
73
100
  * Transfer object to a tabbed string and append a CRC32 checksum
74
101
  *
75
- * @see 3.10. Appending CRC32 checksum
102
+ * @see 3.10.
76
103
  */
77
- function prepareCompression(model) {
78
- const intermediate = toIntermediate(model);
79
- return Buffer.concat([
80
- checksum(intermediate),
81
- Buffer.from(intermediate, "utf-8")
104
+ function addChecksum(serialized) {
105
+ const checksum = new ArrayBuffer(4);
106
+ new DataView(checksum).setUint32(0, crc_32_1.default.str(serialized), true);
107
+ const byteArray = new TextEncoder().encode(serialized);
108
+ return Uint8Array.from([
109
+ ...new Uint8Array(checksum),
110
+ ...Uint8Array.from(byteArray)
82
111
  ]);
83
112
  }
84
- exports.prepareCompression = prepareCompression;
113
+ exports.addChecksum = addChecksum;
85
114
  /**
86
115
  * Transform data to ordered tab-separated intermediate representation ready for
87
116
  * encoding
88
117
  *
89
- * @see Table 15 PAY by square sequence data model
118
+ * @see Table 15.
90
119
  */
91
- function toIntermediate(data) {
92
- const intermediate = new Array();
93
- intermediate.push(data.invoiceId?.toString());
94
- intermediate.push(data.payments.length.toString());
120
+ function serialize(data) {
121
+ const serialized = new Array();
122
+ serialized.push(data.invoiceId?.toString());
123
+ serialized.push(data.payments.length.toString());
95
124
  for (const p of data.payments) {
96
- intermediate.push(p.type.toString());
97
- intermediate.push(p.amount?.toString());
98
- intermediate.push(p.currencyCode);
99
- intermediate.push(p.paymentDueDate);
100
- intermediate.push(p.variableSymbol);
101
- intermediate.push(p.constantSymbol);
102
- intermediate.push(p.specificSymbol);
103
- intermediate.push(p.originatorRefInfo);
104
- intermediate.push(p.paymentNote);
105
- intermediate.push(p.bankAccounts.length.toString());
125
+ serialized.push(p.type.toString());
126
+ serialized.push(p.amount?.toString());
127
+ serialized.push(p.currencyCode);
128
+ serialized.push(p.paymentDueDate);
129
+ serialized.push(p.variableSymbol);
130
+ serialized.push(p.constantSymbol);
131
+ serialized.push(p.specificSymbol);
132
+ serialized.push(p.originatorRefInfo);
133
+ serialized.push(p.paymentNote);
134
+ serialized.push(p.bankAccounts.length.toString());
106
135
  for (const ba of p.bankAccounts) {
107
- intermediate.push(ba.iban);
108
- intermediate.push(ba.bic);
136
+ serialized.push(ba.iban);
137
+ serialized.push(ba.bic);
109
138
  }
110
139
  if (p.type === types_js_1.PaymentOptions.StandingOrder) {
111
- intermediate.push('1');
112
- intermediate.push(p.day?.toString());
113
- intermediate.push(p.month?.toString());
114
- intermediate.push(p.periodicity);
115
- intermediate.push(p.lastDate);
140
+ serialized.push("1");
141
+ serialized.push(p.day?.toString());
142
+ serialized.push(p.month?.toString());
143
+ serialized.push(p.periodicity);
144
+ serialized.push(p.lastDate);
116
145
  }
117
146
  else {
118
- intermediate.push('0');
147
+ serialized.push("0");
119
148
  }
120
149
  if (p.type === types_js_1.PaymentOptions.DirectDebit) {
121
- intermediate.push('1');
122
- intermediate.push(p.directDebitScheme?.toString());
123
- intermediate.push(p.directDebitType?.toString());
124
- intermediate.push(p.variableSymbol?.toString());
125
- intermediate.push(p.specificSymbol?.toString());
126
- intermediate.push(p.originatorRefInfo?.toString());
127
- intermediate.push(p.mandateId?.toString());
128
- intermediate.push(p.creditorId?.toString());
129
- intermediate.push(p.contractId?.toString());
130
- intermediate.push(p.maxAmount?.toString());
131
- intermediate.push(p.validTillDate?.toString());
150
+ serialized.push("1");
151
+ serialized.push(p.directDebitScheme?.toString());
152
+ serialized.push(p.directDebitType?.toString());
153
+ serialized.push(p.variableSymbol?.toString());
154
+ serialized.push(p.specificSymbol?.toString());
155
+ serialized.push(p.originatorRefInfo?.toString());
156
+ serialized.push(p.mandateId?.toString());
157
+ serialized.push(p.creditorId?.toString());
158
+ serialized.push(p.contractId?.toString());
159
+ serialized.push(p.maxAmount?.toString());
160
+ serialized.push(p.validTillDate?.toString());
132
161
  }
133
162
  else {
134
- intermediate.push('0');
163
+ serialized.push("0");
135
164
  }
136
165
  }
137
166
  for (const p of data.payments) {
138
- intermediate.push(p.beneficiary?.name);
139
- intermediate.push(p.beneficiary?.street);
140
- intermediate.push(p.beneficiary?.city);
167
+ serialized.push(p.beneficiary?.name);
168
+ serialized.push(p.beneficiary?.street);
169
+ serialized.push(p.beneficiary?.city);
141
170
  }
142
- return intermediate.join('\t');
171
+ return serialized.join("\t");
143
172
  }
144
- exports.toIntermediate = toIntermediate;
145
- /**
146
- * Transfer diacritics to basic latin letters
147
- */
173
+ exports.serialize = serialize;
148
174
  function removeDiacritics(model) {
149
175
  for (const payment of model.payments) {
150
176
  if (payment.paymentNote) {
@@ -168,40 +194,20 @@ function generate(model, options = { deburr: true }) {
168
194
  if (options.deburr) {
169
195
  removeDiacritics(model);
170
196
  }
171
- const data = prepareCompression(model);
172
- const compressedData = [];
173
- return new Promise((resolve, reject) => {
174
- const encoder = lzma_native_1.default.createStream("rawEncoder", {
175
- synchronous: true,
176
- // @ts-ignore: Missing filter types
177
- filters: [
178
- {
179
- // @ts-ignore: Missing filter types
180
- id: lzma_native_1.default.FILTER_LZMA1,
181
- lc: 3,
182
- lp: 0,
183
- pb: 2,
184
- dict_size: 2 ** 17, // 128 kilobytes
185
- },
186
- ],
187
- });
188
- encoder
189
- .on("end", () => {
190
- const output = Buffer.concat([
191
- bysquareHeader(),
192
- lzmaHeader(data),
193
- ...compressedData
194
- ]);
195
- resolve(rfc4648_1.base32hex.stringify(output, { pad: false }));
196
- })
197
- .on("data", (chunk) => {
198
- compressedData.push(chunk);
199
- })
200
- .on("error", reject)
201
- .write(data, (error) => {
202
- error && reject(error);
203
- encoder.end();
204
- });
197
+ const payload = serialize(model);
198
+ const withChecksum = addChecksum(payload);
199
+ const compressed = Uint8Array.from(lzma.compress(withChecksum));
200
+ /** Exclude the LZMA header and retain raw compressed data */
201
+ const _headerLzma = Uint8Array.from(compressed.subarray(0, 13));
202
+ const compressedPayload = Uint8Array.from(compressed.subarray(13));
203
+ const output = Uint8Array.from([
204
+ ...headerBysquare([0x00, 1 /* Version["1.1.0"] */, 0x00, 0x00]),
205
+ ...headerLzmaProps(),
206
+ ...headerDataLength(withChecksum.byteLength),
207
+ ...compressedPayload
208
+ ]);
209
+ return rfc4648_1.base32hex.stringify(output, {
210
+ pad: false
205
211
  });
206
212
  }
207
213
  exports.generate = generate;
@@ -1,18 +1,29 @@
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": "2.0.1",
4
+ "version": "2.2.0",
5
5
  "license": "Apache-2.0",
6
- "type": "commonjs",
7
6
  "funding": "https://github.com/sponsors/xseman",
8
7
  "homepage": "https://github.com/xseman/bysquare#readme",
9
- "author": {
10
- "name": "Filip Seman",
11
- "email": "filip.seman@protonmail.com"
8
+ "author": "Filip Seman <filip.seman@pm.me>",
9
+ "keywords": [
10
+ "pay by square",
11
+ "by square",
12
+ "paybysquare",
13
+ "bysquare",
14
+ "payments",
15
+ "qr-string",
16
+ "generate",
17
+ "parse"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/xseman/bysquare.git"
12
22
  },
13
23
  "scripts": {
14
24
  "prebuild": "rm -rf ./lib || :",
15
25
  "build": "tsc --project ./tsconfig.json && tsc --project ./tsconfig.cjs.json",
26
+ "typecheck": "tsc --noEmit",
16
27
  "postbuild": "bash -c 'chmod +x ./lib/{cjs,mjs}/cli.js' && cp package.json ./lib/mjs/ && jq '.type=\"commonjs\"' package.json > lib/cjs/package.json",
17
28
  "test": "xv --loader=tsx ./src",
18
29
  "test:watch": "find ./src/*.ts | entr xv --loader=tsx ./src",
@@ -24,18 +35,20 @@
24
35
  "postversion": ""
25
36
  },
26
37
  "dependencies": {
38
+ "crc-32": "~1.2.0",
27
39
  "lodash.deburr": "~4.1.0",
28
- "lzma-native": "7.0.1",
29
- "rfc4648": "1.5.2"
40
+ "lzma1": "~0.0.1",
41
+ "rfc4648": "~1.5.0"
30
42
  },
31
43
  "devDependencies": {
32
44
  "@types/lodash.deburr": "~4.1.0",
33
- "@types/lzma-native": "^4.0.1",
34
- "@types/node": "~18.11.0",
45
+ "@types/node": ">=16.14",
46
+ "dprint": "~0.35.0",
35
47
  "tsx": "~3.12.0",
36
- "typescript": "~4.9.0",
48
+ "typescript": "~5.0.0",
37
49
  "xv": "~2.1.0"
38
50
  },
51
+ "type": "commonjs",
39
52
  "bin": "lib/mjs/cli.js",
40
53
  "types": "lib/mjs/index.d.ts",
41
54
  "exports": {
@@ -49,21 +62,7 @@
49
62
  "!lib/*.test.*"
50
63
  ],
51
64
  "engines": {
52
- "node": ">=16.0",
65
+ "node": ">=16.14",
53
66
  "npm": ">=7.0"
54
- },
55
- "repository": {
56
- "type": "git",
57
- "url": "git+https://github.com/xseman/bysquare.git"
58
- },
59
- "keywords": [
60
- "pay by square",
61
- "by square",
62
- "paybysquare",
63
- "bysquare",
64
- "payments",
65
- "qr-string",
66
- "qr",
67
- "cli"
68
- ]
67
+ }
69
68
  }
@@ -1,12 +1,21 @@
1
1
  import { DataModel } from "./index.js";
2
2
  /**
3
- * @see 3.14. Generating by square Code
3
+ * Generating by square Code
4
+ *
5
+ * @see 3.14.
4
6
  */
5
- export declare function buildModel(qr: string): DataModel;
7
+ export declare function deserialize(qr: string): DataModel;
8
+ export declare class DecodeError extends Error {
9
+ cause: Error;
10
+ name: string;
11
+ constructor(cause: Error, msg?: string);
12
+ }
6
13
  /**
7
- * @see 3.16. Decoding client data from QR Code 2005 symbol
14
+ * Decoding client data from QR Code 2005 symbol
15
+ *
16
+ * @see 3.16.
8
17
  */
9
- export declare function parse(qr: string): Promise<DataModel>;
18
+ export declare function parse(qr: string): DataModel;
10
19
  /**
11
20
  * Detect if qr string contains bysquare header.
12
21
  *