Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: improve naming and jsdoc #39

Merged
merged 6 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ transformed into images depends on how you implement it. See

## Installation

**NOTE**: This package is native [ESM][mozzila-esm] and no longer provides a
CommonJS export. If your project uses CommonJS, you will have to convert to ESM
or use the dynamic [`import()`][mozzila-import] function.
> [!NOTE]
> This package is native [ESM][mozzila-esm] and no longer provides a
> CommonJS export. If your project uses CommonJS, you will have to convert to ESM
> or use the dynamic [`import()`][mozzila-import] function.

[mozzila-esm]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
[mozzila-import]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
Expand Down Expand Up @@ -60,12 +61,6 @@ import {
</script>
```

## How it works

### Encoding sequence

![logic](./docs/uml/logic.svg)

## Usage

### Encode
Expand Down Expand Up @@ -143,6 +138,12 @@ qrstring argument should be a valid QR code string.
npx bysquare --decode <qrstring>
```

## How it works

### Encoding sequence

![logic](./docs/uml/logic.svg)

## Platform support

I mainly focus on LTS versions of Node.js and try to use the most idiomatic
Expand All @@ -163,6 +164,12 @@ to use without additional setup, showing its improved maturity.

The latest version of Chrome, Firefox, and Safari.

## Troubleshooting & Recommendations

### Encoded data are without diacritics

The library removes all diacritics from the input data to ensure maximum compatibility, as not all banks support diacritics, which may lead to errors. If you need to retain diacritics, disable deburr option when encoding data - `encode(model, { deburr: false })`.

## Related

- <https://bysquare.com/>
Expand Down
16 changes: 11 additions & 5 deletions src/deburr.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** Used to map Latin Unicode letters to basic Latin letters. */
/** dprint-ignore */
/** Used to map Latin Unicode letters to basic Latin letters. */
const deburredLettersMap: { [key: string]: string; } = {
// Latin-1 Supplement block.
'\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
Expand Down Expand Up @@ -61,14 +61,20 @@ export function deburrLetter(key: string) {
return deburredLettersMap[key];
}

/** Used to match Latin Unicode letters (excluding mathematical operators). */
/**
* Used to match Latin Unicode letters (excluding mathematical operators).
*/
const reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;

/** Used to compose unicode character classes. */
/**
* Used to compose unicode character classes.
*/
const rsComboMarksRange = "\\u0300-\\u036f\\ufe20-\\ufe23",
rsComboSymbolsRange = "\\u20d0-\\u20f0";

/** Used to compose unicode capture groups. */
/**
* Used to compose unicode capture groups.
*/
const rsCombo = "[" + rsComboMarksRange + rsComboSymbolsRange + "]";

/**
Expand All @@ -78,7 +84,7 @@ const rsCombo = "[" + rsComboMarksRange + rsComboSymbolsRange + "]";
const reComboMark = RegExp(rsCombo, "g");

/**
* @desc Deburrs string by converting Latin-1 Supplement and Latin Extended-A letters to basic Latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
* Deburrs string by converting Latin-1 Supplement and Latin Extended-A letters to basic Latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
*/
export function deburr(text: string): string {
return text.replace(reLatin, deburrLetter).replace(reComboMark, "");
Expand Down
9 changes: 6 additions & 3 deletions src/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ interface Header {
* the input header array into four nibbles representing the bysquare header
* values.
*
* @param header 2-bytes sie
* @param header 2-bytes size
*/
function bysquareHeaderDecoder(header: Uint8Array): Header {
const bytes = (header[0] << 8) | header[1];
Expand Down Expand Up @@ -212,8 +212,11 @@ export function decode(qr: string): DataModel {
}

const bysquareHeader = bytes.slice(0, 2);
if ((bysquareHeaderDecoder(bysquareHeader).version > Version["1.1.0"])) {
throw new Error("Unsupported Bysquare version");
const decodedBysquareHeader = bysquareHeaderDecoder(bysquareHeader);
if ((decodedBysquareHeader.version > Version["1.1.0"])) {
throw new Error(
`Unsupported Bysquare version '${decodedBysquareHeader.version}' in header detected. Only '0' and '1' values are supported`,
);
}

/**
Expand Down
28 changes: 19 additions & 9 deletions src/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { deburr } from "./deburr.js";
import {
DataModel,
PaymentOptions,
Version,
} from "./types.js";

const MAX_COMPRESSED_SIZE = 131_072; // 2^17

/**
* Returns a 2 byte buffer that represents the header of the bysquare
* specification
Expand All @@ -32,9 +35,17 @@ export function headerBysquare(
0x00, 0x00
],
): Uint8Array {
const isValid = header.every((nibble) => 0 <= nibble && nibble <= 15);
if (!isValid) {
throw new Error("Invalid header byte value, valid range <0,15>");
if (header[0] < 0 || header[0] > 15) {
throw new Error(`Invalid BySquareType value '${header[0]}' in header, valid range <0,15>`);
}
if (header[1] < 0 || header[1] > 15) {
throw new Error(`Invalid Version value '${header[1]}' in header, valid range <0,15>`);
}
if (header[2] < 0 || header[2] > 15) {
throw new Error(`Invalid DocumentType value '${header[2]}' in header, valid range <0,15>`);
}
if (header[3] < 0 || header[3] > 15) {
throw new Error(`Invalid Reserved value '${header[3]}' in header, valid range <0,15>`);
}

const [
Expand All @@ -58,8 +69,8 @@ export function headerBysquare(
* combination with CRC32 in bytes.
*/
export function headerDataLength(length: number): Uint8Array {
if (length >= 131072 /** 2^17 */) {
throw new Error("The maximum compressed data size has been reached");
if (length >= MAX_COMPRESSED_SIZE) {
throw new Error(`Data size ${length} exceeds limit of ${MAX_COMPRESSED_SIZE} bytes`);
}

const header = new ArrayBuffer(2);
Expand Down Expand Up @@ -202,11 +213,10 @@ export function encode(
const lzmaBody = Uint8Array.from(compressed.subarray(13));

const output = Uint8Array.from([
// FIXME:
// for now other implementation of bysquare doesn't recognize header if
// version is specified like TatraBanka
// NOTE: Newer version 1.1.0 is not supported by all apps (e.g., TatraBanka).
// We recommend using version "1.0.0" for better compatibility.
// ...headerBysquare([0x00, Version["1.1.0"], 0x00, 0x00]),
...headerBysquare([0x00, 0x00, 0x00, 0x00]),
...headerBysquare([0x00, Version["1.0.0"], 0x00, 0x00]),
...headerDataLength(withChecksum.byteLength),
...lzmaBody,
]);
Expand Down
Loading