Skip to content

Commit

Permalink
refactor: add types
Browse files Browse the repository at this point in the history
Motivation
----------
This will add types.

The behaviour has changed minimally. The conditions don't check for
`key in data` but simply `data.key`. I think considering the
differences, it's perfectly for fine for this library.

How to test
-----------
1. `npm run build`
2. Have a look in `dist/`

close derhuerst#6
  • Loading branch information
roschaefer committed Sep 13, 2024
1 parent 62d7b9d commit 067372f
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 108 deletions.
Empty file added dist/.gitkeep
Empty file.
13 changes: 13 additions & 0 deletions dist/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var _1 = __importDefault(require("."));
console.log((0, _1.default)({
name: 'Red Cross of Belgium',
iban: 'BE72000000001616',
amount: 123.45,
unstructuredReference: 'Urgency fund',
information: 'Sample QR code'
}));
12 changes: 12 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type QrCodeData = {
name: string;
iban: string;
bic?: string;
amount?: number;
purposeCode?: string;
structuredReference?: string;
unstructuredReference?: string;
information?: string;
};
declare const generateQrCode: (data: QrCodeData) => string;
export = generateQrCode;
86 changes: 86 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"use strict";
var iban_1 = require("iban");
var SERVICE_TAG = 'BCD';
var VERSION = '002';
var CHARACTER_SET = 1;
var IDENTIFICATION_CODE = 'SCT';
var assertNonEmptyString = function (val, name) {
if (typeof val !== 'string' || !val) {
throw new Error(name + ' must be a non-empty string.');
}
};
var generateQrCode = function (data) {
if (!data)
throw new Error('data must be an object.');
// > AT-21 Name of the Beneficiary
assertNonEmptyString(data.name, 'data.name');
if (data.name.length > 70)
throw new Error('data.name must have <=70 characters');
// > AT-23 BIC of the Beneficiary Bank
if (data.bic) {
assertNonEmptyString(data.bic, 'data.bic');
if (data.bic.length > 11)
throw new Error('data.bic must have <=11 characters');
// todo: validate more?
}
// > AT-20 Account number of the Beneficiary
// > Only IBAN is allowed.
assertNonEmptyString(data.iban, 'data.iban');
if (!(0, iban_1.isValid)(data.iban)) {
throw new Error('data.iban must be a valid iban code.');
}
// > AT-04 Amount of the Credit Transfer in Euro
// > Amount must be 0.01 or more and 999999999.99 or less
if (data.amount !== null) {
if (typeof data.amount !== 'number')
throw new Error('data.amount must be a number or null.');
if (data.amount < 0.01 || data.amount > 999999999.99) {
throw new Error('data.amount must be >=0.01 and <=999999999.99.');
}
}
// > AT-44 Purpose of the Credit Transfer
if (data.purposeCode) {
assertNonEmptyString(data.purposeCode, 'data.purposeCode');
if (data.purposeCode.length > 4)
throw new Error('data.purposeCode must have <=4 characters');
// todo: validate against AT-44
}
// > AT-05 Remittance Information (Structured)
// > Creditor Reference (ISO 11649 RF Creditor Reference may be used)
if (data.structuredReference) {
assertNonEmptyString(data.structuredReference, 'data.structuredReference');
if (data.structuredReference.length > 35)
throw new Error('data.structuredReference must have <=35 characters');
// todo: validate against AT-05
}
// > AT-05 Remittance Information (Unstructured)
if (data.unstructuredReference) {
assertNonEmptyString(data.unstructuredReference, 'data.unstructuredReference');
if (data.unstructuredReference.length > 140)
throw new Error('data.unstructuredReference must have <=140 characters');
}
if ('structuredReference' in data && 'unstructuredReference' in data) {
throw new Error('Use either data.structuredReference or data.unstructuredReference.');
}
// > Beneficiary to originator information
if (data.information) {
assertNonEmptyString(data.information, 'data.information');
if (data.information.length > 70)
throw new Error('data.information must have <=70 characters');
}
return [
SERVICE_TAG,
VERSION,
CHARACTER_SET,
IDENTIFICATION_CODE,
data.bic,
data.name,
(0, iban_1.electronicFormat)(data.iban),
data.amount === null ? '' : 'EUR' + data.amount.toFixed(2),
data.purposeCode || '',
data.structuredReference || '',
data.unstructuredReference || '',
data.information || '',
].join('\n');
};
module.exports = generateQrCode;
46 changes: 46 additions & 0 deletions dist/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var assert_1 = require("assert");
var _1 = __importDefault(require("."));
var ex1 = {
name: 'Red Cross of Belgium',
iban: 'BE72000000001616',
bic: 'BPOTBEB1',
amount: 123.45,
reference: 'Urgency fund',
purposeCode: 'abc',
structuredReference: '123',
information: 'foo bar'
};
(0, assert_1.strictEqual)((0, _1.default)(ex1), "BCD\n002\n1\nSCT\nBPOTBEB1\nRed Cross of Belgium\nBE72000000001616\nEUR123.45\nabc\n123\n\nfoo bar");
// missing data.iban
{
var ex2_1 = __assign({}, ex1);
delete ex2_1.iban;
(0, assert_1.throws)(function () {
(0, _1.default)(ex2_1);
}, 'throws with missing data.iban');
}
// invalid data.iban
{
(0, assert_1.throws)(function () {
(0, _1.default)(__assign(__assign({}, ex1), { iban: 'BE00000000000000' }));
}, 'throws with invalid data.iban');
}
// omitted amount
(0, assert_1.strictEqual)((0, _1.default)(__assign(__assign({}, ex1), { amount: null })), "BCD\n002\n1\nSCT\nBPOTBEB1\nRed Cross of Belgium\nBE72000000001616\n\nabc\n123\n\nfoo bar");
console.info('seems to work ✓');
4 changes: 1 addition & 3 deletions example.js → example.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
'use strict'

const generateQrCode = require('.')
import generateQrCode from '.'

console.log(generateQrCode({
name: 'Red Cross of Belgium',
Expand Down
91 changes: 0 additions & 91 deletions index.js

This file was deleted.

102 changes: 102 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { isValid, electronicFormat } from 'iban'

const SERVICE_TAG = 'BCD'
const VERSION = '002'
const CHARACTER_SET = 1
const IDENTIFICATION_CODE = 'SCT'

const assertNonEmptyString = (val: unknown, name: string) => {
if (typeof val !== 'string' || !val) {
throw new Error(name + ' must be a non-empty string.')
}
}

type QrCodeData = {
name: string
iban: string
bic?: string
amount?: number
purposeCode?: string
structuredReference?: string
unstructuredReference?: string
information?: string
}

const generateQrCode = (data: QrCodeData) => {
if (!data) throw new Error('data must be an object.')

// > AT-21 Name of the Beneficiary
assertNonEmptyString(data.name, 'data.name')
if (data.name.length > 70) throw new Error('data.name must have <=70 characters')

// > AT-23 BIC of the Beneficiary Bank
if (data.bic) {
assertNonEmptyString(data.bic, 'data.bic')
if (data.bic.length > 11) throw new Error('data.bic must have <=11 characters')
// todo: validate more?
}

// > AT-20 Account number of the Beneficiary
// > Only IBAN is allowed.
assertNonEmptyString(data.iban, 'data.iban')
if (!isValid(data.iban)) {
throw new Error('data.iban must be a valid iban code.')
}

// > AT-04 Amount of the Credit Transfer in Euro
// > Amount must be 0.01 or more and 999999999.99 or less
if (data.amount !== null) {
if (typeof data.amount !== 'number') throw new Error('data.amount must be a number or null.')
if (data.amount < 0.01 || data.amount > 999999999.99) {
throw new Error('data.amount must be >=0.01 and <=999999999.99.')
}
}

// > AT-44 Purpose of the Credit Transfer
if (data.purposeCode) {
assertNonEmptyString(data.purposeCode, 'data.purposeCode')
if (data.purposeCode.length > 4) throw new Error('data.purposeCode must have <=4 characters')
// todo: validate against AT-44
}

// > AT-05 Remittance Information (Structured)
// > Creditor Reference (ISO 11649 RF Creditor Reference may be used)
if (data.structuredReference) {
assertNonEmptyString(data.structuredReference, 'data.structuredReference')
if (data.structuredReference.length > 35)
throw new Error('data.structuredReference must have <=35 characters')
// todo: validate against AT-05
}
// > AT-05 Remittance Information (Unstructured)
if (data.unstructuredReference) {
assertNonEmptyString(data.unstructuredReference, 'data.unstructuredReference')
if (data.unstructuredReference.length > 140)
throw new Error('data.unstructuredReference must have <=140 characters')
}
if ('structuredReference' in data && 'unstructuredReference' in data) {
throw new Error('Use either data.structuredReference or data.unstructuredReference.')
}

// > Beneficiary to originator information
if (data.information) {
assertNonEmptyString(data.information, 'data.information')
if (data.information.length > 70) throw new Error('data.information must have <=70 characters')
}

return [
SERVICE_TAG,
VERSION,
CHARACTER_SET,
IDENTIFICATION_CODE,
data.bic,
data.name,
electronicFormat(data.iban),
data.amount === null ? '' : 'EUR' + data.amount.toFixed(2),
data.purposeCode || '',
data.structuredReference || '',
data.unstructuredReference || '',
data.information || '',
].join('\n')
}

export = generateQrCode
Loading

0 comments on commit 067372f

Please sign in to comment.