bysquare 2.12.4 → 2.12.5

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,2 +1,2 @@
1
- export declare function encode(input: Uint8Array, addPadding?: boolean): string;
1
+ export declare function encode(input: ArrayLike<number>, addPadding?: boolean): string;
2
2
  export declare function decode(input: string, isLoose?: boolean): Uint8Array;
package/lib/decode.d.ts CHANGED
@@ -31,6 +31,7 @@ export declare const parse: typeof decode;
31
31
  * Decoding client data from QR Code 2005 symbol
32
32
  *
33
33
  * @see 3.16.
34
+ * @param qr base32hex encoded bysqaure binary data
34
35
  */
35
36
  export declare function decode(qr: string): DataModel;
36
37
  /**
@@ -38,5 +39,7 @@ export declare function decode(qr: string): DataModel;
38
39
  *
39
40
  * There is not magic header in the bysquare specification.
40
41
  * Version is just 4 bites, so it is possible to have false positives.
42
+ *
43
+ * @deprecated Will be removed as of v3.
41
44
  */
42
45
  export declare function detect(qr: string): boolean;
package/lib/decode.js CHANGED
@@ -60,7 +60,6 @@ export function deserialize(qr) {
60
60
  const originatorRefInfo = data.shift();
61
61
  const paymentNote = data.shift();
62
62
  let payment = {
63
- bankAccounts: [],
64
63
  type: Number(paymentOptions),
65
64
  currencyCode: currency ?? CurrencyCode.EUR,
66
65
  amount: Number(ammount),
@@ -70,6 +69,7 @@ export function deserialize(qr) {
70
69
  specificSymbol: specificSymbol || undefined,
71
70
  originatorsReferenceInformation: originatorRefInfo || undefined,
72
71
  paymentNote: paymentNote || undefined,
72
+ bankAccounts: [],
73
73
  };
74
74
  const numberOfAccounts = Number(data.shift());
75
75
  for (let j = 0; j < numberOfAccounts; j++) {
@@ -156,14 +156,15 @@ export const parse = decode;
156
156
  * Decoding client data from QR Code 2005 symbol
157
157
  *
158
158
  * @see 3.16.
159
+ * @param qr base32hex encoded bysqaure binary data
159
160
  */
160
161
  export function decode(qr) {
161
162
  const bytes = base32hex.decode(qr);
162
- const bysquareHeader = bytes.slice(0, 2);
163
- const decodedBysquareHeader = bysquareHeaderDecoder(bysquareHeader);
164
- if ((decodedBysquareHeader.version > Version["1.1.0"])) {
163
+ const headerBytes = bytes.slice(0, 2);
164
+ const headerData = bysquareHeaderDecoder(headerBytes);
165
+ if ((headerData.version > Version["1.1.0"])) {
165
166
  throw new DecodeError(DecodeErrorMessage.UnsupportedVersion, {
166
- version: decodedBysquareHeader.version,
167
+ version: headerData.version,
167
168
  });
168
169
  }
169
170
  /**
@@ -181,14 +182,22 @@ export function decode(qr) {
181
182
  * +---------------+---------------------------+-------------------+
182
183
  */
183
184
  const defaultProperties = [0x5D]; // lc=3, lp=0, pb=2
184
- const defaultDictionarySize = [0x00, 0x02, 0x00, 0x00]; // 2^17
185
+ const defaultDictionarySize = [0x00, 0x00, 0x20, 0x00]; // 2^21 = 2097152
186
+ // Parse the payload length from bytes 2-3 and properly expand to 8-byte uncompressed size
187
+ const payloadLengthBytes = bytes.slice(2, 4);
188
+ const payloadLength = payloadLengthBytes[0] | (payloadLengthBytes[1] << 8);
185
189
  const uncompressedSize = new Uint8Array(8);
186
- uncompressedSize.set(bytes.slice(2, 4));
187
- const header = new Uint8Array([
190
+ // Set the full 32-bit value in little-endian format
191
+ uncompressedSize[0] = payloadLength & 0xFF;
192
+ uncompressedSize[1] = (payloadLength >> 8) & 0xFF;
193
+ uncompressedSize[2] = (payloadLength >> 16) & 0xFF;
194
+ uncompressedSize[3] = (payloadLength >> 24) & 0xFF;
195
+ // Bytes 4-7 remain 0 for sizes < 2^32
196
+ const header = [
188
197
  ...defaultProperties,
189
198
  ...defaultDictionarySize,
190
199
  ...uncompressedSize,
191
- ]);
200
+ ];
192
201
  const payload = bytes.slice(4);
193
202
  const body = new Uint8Array([
194
203
  ...header,
@@ -201,9 +210,12 @@ export function decode(qr) {
201
210
  catch (error) {
202
211
  throw new DecodeError(DecodeErrorMessage.LZMADecompressionFailed, { error });
203
212
  }
204
- if (typeof decompressed === "string") {
205
- return deserialize(decompressed);
213
+ if (!decompressed) {
214
+ throw new DecodeError(DecodeErrorMessage.LZMADecompressionFailed, {
215
+ error: "Decompression returned undefined",
216
+ });
206
217
  }
218
+ // Extract checksum and body
207
219
  const _checksum = decompressed.slice(0, 4);
208
220
  const decompressedBody = decompressed.slice(4);
209
221
  const decoded = new TextDecoder("utf-8").decode(decompressedBody.buffer);
@@ -214,6 +226,8 @@ export function decode(qr) {
214
226
  *
215
227
  * There is not magic header in the bysquare specification.
216
228
  * Version is just 4 bites, so it is possible to have false positives.
229
+ *
230
+ * @deprecated Will be removed as of v3.
217
231
  */
218
232
  export function detect(qr) {
219
233
  let decoded;
package/lib/encode.d.ts CHANGED
@@ -47,25 +47,25 @@ export declare const MAX_COMPRESSED_SIZE = 131072;
47
47
  *
48
48
  * @see 3.5.
49
49
  */
50
- export declare function headerBysquare(
50
+ export declare function buildBysquareHeader(
51
51
  /** dprint-ignore */
52
52
  header?: [
53
53
  bySquareType: number,
54
54
  version: number,
55
55
  documentType: number,
56
56
  reserved: number
57
- ]): Uint8Array;
57
+ ]): number[];
58
58
  /**
59
- * Creates a one-byte array that represents the length of compressed data in
59
+ * Creates a 2-byte array that represents the length of compressed data in
60
60
  * combination with CRC32 in bytes.
61
61
  */
62
- export declare function headerDataLength(length: number): Uint8Array;
62
+ export declare function buildPayloadLength(length: number): Uint8Array;
63
63
  /**
64
64
  * Transfer object to a tabbed string and append a CRC32 checksum
65
65
  *
66
66
  * @see 3.10.
67
67
  */
68
- export declare function addChecksum(serialized: string): Uint8Array;
68
+ export declare function addChecksum(tabbedPayload: string): Uint8Array;
69
69
  /**
70
70
  * Transform data to ordered tab-separated intermediate representation ready for
71
71
  * encoding
@@ -93,6 +93,12 @@ type Options = {
93
93
  export declare const generate: typeof encode;
94
94
  /**
95
95
  * Generate QR string ready for encoding into text QR code
96
+ *
97
+ * @param model - Data model to encode
98
+ * @param options - Options for encoding
99
+ *
100
+ * @default options.deburr - true
101
+ * @default options.validate - true
96
102
  */
97
103
  export declare function encode(model: DataModel, options?: Options): string;
98
104
  export {};
package/lib/encode.js CHANGED
@@ -54,7 +54,7 @@ export const MAX_COMPRESSED_SIZE = 131_072; // 2^17
54
54
  *
55
55
  * @see 3.5.
56
56
  */
57
- export function headerBysquare(
57
+ export function buildBysquareHeader(
58
58
  /** dprint-ignore */
59
59
  header = [
60
60
  0x00, 0x00,
@@ -74,17 +74,17 @@ header = [
74
74
  }
75
75
  const [bySquareType, version, documentType, reserved,] = header;
76
76
  // Combine 4-nibbles to 2-bytes
77
- const mergedNibbles = Uint8Array.from([
77
+ const mergedNibbles = [
78
78
  (bySquareType << 4) | (version << 0),
79
79
  (documentType << 4) | (reserved << 0),
80
- ]);
80
+ ];
81
81
  return mergedNibbles;
82
82
  }
83
83
  /**
84
- * Creates a one-byte array that represents the length of compressed data in
84
+ * Creates a 2-byte array that represents the length of compressed data in
85
85
  * combination with CRC32 in bytes.
86
86
  */
87
- export function headerDataLength(length) {
87
+ export function buildPayloadLength(length) {
88
88
  if (length >= MAX_COMPRESSED_SIZE) {
89
89
  throw new EncodeError(EncodeErrorMessage.HeaderDataSize, {
90
90
  actualSize: length,
@@ -100,10 +100,10 @@ export function headerDataLength(length) {
100
100
  *
101
101
  * @see 3.10.
102
102
  */
103
- export function addChecksum(serialized) {
103
+ export function addChecksum(tabbedPayload) {
104
104
  const checksum = new ArrayBuffer(4);
105
- new DataView(checksum).setUint32(0, crc32(serialized), true);
106
- const byteArray = new TextEncoder().encode(serialized);
105
+ new DataView(checksum).setUint32(0, crc32(tabbedPayload), true);
106
+ const byteArray = new TextEncoder().encode(tabbedPayload);
107
107
  return Uint8Array.from([
108
108
  ...new Uint8Array(checksum),
109
109
  ...Uint8Array.from(byteArray),
@@ -188,22 +188,33 @@ export function removeDiacritics(model) {
188
188
  export const generate = encode;
189
189
  /**
190
190
  * Generate QR string ready for encoding into text QR code
191
+ *
192
+ * @param model - Data model to encode
193
+ * @param options - Options for encoding
194
+ *
195
+ * @default options.deburr - true
196
+ * @default options.validate - true
191
197
  */
192
- export function encode(model, options) {
193
- const { deburr = true, validate = true } = options ?? {};
194
- if (deburr) {
198
+ export function encode(model, options = { deburr: true, validate: true }) {
199
+ if (options.deburr) {
195
200
  removeDiacritics(model);
196
201
  }
197
- if (validate) {
202
+ if (options.validate) {
198
203
  validateDataModel(model);
199
204
  }
200
- const payload = serialize(model);
201
- const payloadChecked = addChecksum(payload);
202
- const payloadCompressed = Uint8Array.from(compress(payloadChecked));
205
+ const payloadTabbed = serialize(model);
206
+ const payloadChecked = addChecksum(payloadTabbed);
207
+ const payloadCompressed = compress(payloadChecked);
203
208
  /**
209
+ * Header is ommited, the bysquare doesn't include it in the output
210
+ *
211
+ * ---
204
212
  * The LZMA files has a 13-byte header that is followed by the LZMA
205
213
  * compressed data.
206
214
  *
215
+ * NOTE: We use a custom compression function that sets dictionary size to 2^17
216
+ * This is required for compatibility with existing QR codes
217
+ *
207
218
  * @see https://docs.fileformat.com/compression/lzma/
208
219
  *
209
220
  * +---------------+---------------------------+-------------------+
@@ -212,14 +223,14 @@ export function encode(model, options) {
212
223
  * | Properties | Dictionary Size | Uncompressed Size |
213
224
  * +---------------+---------------------------+-------------------+
214
225
  */
215
- const _lzmaHeader = Uint8Array.from(payloadCompressed.subarray(0, 13));
216
- const lzmaBody = Uint8Array.from(payloadCompressed.subarray(13));
217
- const output = Uint8Array.from([
226
+ const _lzmaHeader = payloadCompressed.subarray(0, 13);
227
+ const lzmaBody = payloadCompressed.subarray(13);
228
+ const output = new Uint8Array([
218
229
  // NOTE: Newer version 1.1.0 is not supported by all apps (e.g., TatraBanka).
219
230
  // We recommend using version "1.0.0" for better compatibility.
220
231
  // ...headerBysquare([0x00, Version["1.1.0"], 0x00, 0x00]),
221
- ...headerBysquare([0x00, Version["1.0.0"], 0x00, 0x00]),
222
- ...headerDataLength(payloadChecked.byteLength),
232
+ ...buildBysquareHeader([0x00, Version["1.0.0"], 0x00, 0x00]),
233
+ ...buildPayloadLength(payloadChecked.byteLength),
223
234
  ...lzmaBody,
224
235
  ]);
225
236
  return base32hex.encode(output, false);
package/lib/types.d.ts CHANGED
@@ -62,7 +62,7 @@ export type Periodicity = typeof Periodicity[keyof typeof Periodicity];
62
62
  * either day of the month (number between 1 and 31) or day of the week
63
63
  * (1=Monday,2=Tuesday, …, 7=Sunday).
64
64
  *
65
- * Maximálna dĺžka 2
65
+ * @maximum 2
66
66
  */
67
67
  export type Day = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31;
68
68
  /**
@@ -95,11 +95,9 @@ export type BankAccount = {
95
95
  /**
96
96
  * Medzinárodné číslo bankového účtu vo formáte IBAN. Príklad:
97
97
  *
98
- * Maximálna dĺžka 34
99
- *
100
- * Pattern: `[A-Z]{2}[0-9]{2}[A-Z0-9]{0,30}`
101
- *
102
98
  * @example `"SK8209000000000011424060"`
99
+ * @maximum 34
100
+ * @pattern [A-Z]{2}[0-9]{2}[A-Z0-9]{0,30}
103
101
  */
104
102
  iban: string;
105
103
  /**
@@ -107,9 +105,8 @@ export type BankAccount = {
107
105
  *
108
106
  * Formát [ISO 9362](https://en.wikipedia.org/wiki/ISO_9362) (swift) 8 or 11 characters long
109
107
  *
110
- * Pattern: `[A-Z]{4}[A-Z]{2}[A-Z\d]{2}([A-Z\d]{3})?`
111
- *
112
108
  * @example "TATRSKBX"
109
+ * @pattern [A-Z]{4}[A-Z]{2}[A-Z\d]{2}([A-Z\d]{3})?
113
110
  */
114
111
  bic?: string;
115
112
  };
@@ -133,7 +130,7 @@ export type DirectDebitScheme = typeof DirectDebitScheme[keyof typeof DirectDebi
133
130
  /**
134
131
  * Typ inkasa. Uvádza ja jedna z možností:
135
132
  *
136
- * Maximálna dĺžka 1
133
+ * @maximum 1
137
134
  *
138
135
  * - one-off - jednorázové inkaso
139
136
  * - recurrent - opakované inkaso
@@ -153,19 +150,19 @@ export type Beneficiary = {
153
150
  /**
154
151
  * Rozšírenie o meno príjemcu
155
152
  *
156
- * Maximálna dĺžka 70
153
+ * @maximum 70
157
154
  */
158
155
  name?: string;
159
156
  /**
160
157
  * Rozšírenie o adresu príjemcu
161
158
  *
162
- * Maximálna dĺžka 70
159
+ * @maximum 70
163
160
  */
164
161
  street?: string;
165
162
  /**
166
163
  * Rozšírenie o adresu príjemcu (druhý riadok)
167
164
  *
168
- * Maximálna dĺžka 70
165
+ * @maximum 70
169
166
  */
170
167
  city?: string;
171
168
  };
@@ -179,15 +176,14 @@ export type SimplePayment = {
179
176
  * deväťdesiatdeväť sa uvádza ako `1.99`. Desať celých peťdesiat sa uvádza
180
177
  * ako `10.5`. Nula celá nula osem sa uvádza ako `0.08`.
181
178
  *
182
- * Maximálna dĺžka 15
179
+ * @maximum 15
183
180
  */
184
181
  amount?: number;
185
182
  /**
186
183
  * Mena v [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) formáte (3 písmená).
187
184
  *
188
- * Pattern: [A-Z]{3}
189
- *
190
185
  * @example "EUR"
186
+ * @pattern [A-Z]{3}
191
187
  */
192
188
  currencyCode: string | keyof typeof CurrencyCode;
193
189
  /**
@@ -200,35 +196,35 @@ export type SimplePayment = {
200
196
  /**
201
197
  * Variabilný symbol je maximálne 10 miestne číslo.
202
198
  *
203
- * Maximálna dĺžka 10
204
- * Pattern: [0-9]{0,10}
199
+ * @maximum 10
200
+ * @pattern [0-9]{0,10}
205
201
  */
206
202
  variableSymbol?: string;
207
203
  /**
208
204
  * Konštantný symbol je 4 miestne identifikačné číslo.
209
205
  *
210
- * Maximálna dĺžka 4
211
- * Pattern: [0-9]{0,4}
206
+ * @maximum 4
207
+ * @pattern [0-9]{0,4}
212
208
  */
213
209
  constantSymbol?: string;
214
210
  /**
215
211
  * Špecifický symbol je maximálne 10 miestne číslo.
216
212
  *
217
- * Maximálna dĺžka 10
218
- * Pattern: [0-9]{0,10}
213
+ * @maximum 10
214
+ * @pattern [0-9]{0,10}
219
215
  */
220
216
  specificSymbol?: string;
221
217
  /**
222
218
  * Referenčná informácia prijímateľa podľa SEPA.
223
219
  *
224
- * Maximálna dĺžka 35
220
+ * @maximum 35
225
221
  */
226
222
  originatorsReferenceInformation?: string;
227
223
  /**
228
224
  * Správa pre prijímateľa. Údaje o platbe, na základe ktorých príjemca bude
229
225
  * môcť platbu identifikovať.
230
226
  *
231
- * Maximálna dĺžka 140
227
+ * @maximum 140
232
228
  */
233
229
  paymentNote?: string;
234
230
  /**
@@ -276,31 +272,31 @@ export type DirectDebit = SimplePayment & {
276
272
  /**
277
273
  * Identifikácia mandátu medzi veriteľom a dlžníkom podľa SEPA.
278
274
  *
279
- * Maximálna dĺžka 35
275
+ * @maximum 35
280
276
  */
281
277
  mandateId?: string;
282
278
  /**
283
279
  * Identifikácia veriteľa podľa SEPA.
284
280
  *
285
- * Maximálna dĺžka 35
281
+ * @maximum 35
286
282
  */
287
283
  creditorId?: string;
288
284
  /**
289
285
  * Identifikácia zmluvy medzi veriteľom a dlžníkom podľa SEPA.
290
286
  *
291
- * Maximálna dĺžka 35
287
+ * @maximum 35
292
288
  */
293
289
  contractId?: string;
294
290
  /**
295
291
  * Maximálna čiastka inkasa.
296
292
  *
297
- * Maximálna dĺžka 15
293
+ * @maximum 15
298
294
  */
299
295
  maxAmount?: number;
300
296
  /**
301
297
  * Dátum platnosti inkasa. Platnosť inkasa zaníka dňom tohto dátumu.
302
298
  *
303
- * Maximálna dĺžka 8
299
+ * @maximum 8
304
300
  * Formát `YYYYMMDD`
305
301
  */
306
302
  validTillDate?: string;
@@ -314,7 +310,7 @@ export type DataModel = {
314
310
  * Číslo faktúry v prípade, že údaje sú súčasťou faktúry, alebo
315
311
  * identifikátor pre intérne potreby vystavovateľa.
316
312
  *
317
- * Maximálna dĺžka 10
313
+ * @maximum 10
318
314
  */
319
315
  invoiceId?: string;
320
316
  /**
package/lib/types.js CHANGED
@@ -95,7 +95,7 @@ export const DirectDebitScheme = {
95
95
  /**
96
96
  * Typ inkasa. Uvádza ja jedna z možností:
97
97
  *
98
- * Maximálna dĺžka 1
98
+ * @maximum 1
99
99
  *
100
100
  * - one-off - jednorázové inkaso
101
101
  * - recurrent - opakované inkaso
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "bysquare",
3
3
  "description": "It's a national standard for payment QR codes adopted by Slovak Banking Association (SBA)",
4
4
  "type": "module",
5
- "version": "2.12.4",
5
+ "version": "2.12.5",
6
6
  "license": "Apache-2.0",
7
7
  "funding": "https://github.com/sponsors/xseman",
8
8
  "homepage": "https://github.com/xseman/bysquare#readme",
@@ -22,18 +22,19 @@
22
22
  "fmt": "dprint fmt",
23
23
  "fmt:check": "dprint check",
24
24
  "typecheck": "tsc --noEmit",
25
- "test": "bun test --coverage ./src/*.test.ts",
26
- "test:watch": "bun test --watch --coverage ./src/*.test.ts"
25
+ "test": "bun test --coverage .",
26
+ "test:watch": "bun test --watch --coverage ."
27
27
  },
28
28
  "dependencies": {
29
- "lzma1": "0.0.3",
29
+ "lzma1": "0.1.2",
30
30
  "validator": "^13.12.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@types/node": "^22.7.0",
34
- "@types/validator": "^13.12.0",
35
- "dprint": "~0.49.0",
36
- "typescript": "~5.8.0"
33
+ "@types/bun": "^1.2.19",
34
+ "@types/node": "^24.1.0",
35
+ "@types/validator": "^13.15.2",
36
+ "dprint": "~0.50.0",
37
+ "typescript": "~5.8.3"
37
38
  },
38
39
  "bin": "./lib/cli.js",
39
40
  "exports": {