/
gtin.ts
68 lines (59 loc) · 1.75 KB
/
gtin.ts
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* cdigit
*
* @copyright 2018-2023 LiosK
* @license (MIT OR Apache-2.0)
*/
import type { CdigitAlgo } from "../type.js";
class GTIN implements CdigitAlgo {
constructor(readonly name: string, readonly longName: string) {}
computeFromNumVals(ns: number[]): number[] {
if (ns.length === 0) {
throw new SyntaxError("string to be protected is empty");
} else if (ns.some((e) => e < 0 || e > 9 || !Number.isInteger(e))) {
throw new SyntaxError("invalid numerical value detected");
}
let sum = 0;
let odd = 1;
for (let i = ns.length - 1; i >= 0; i -= 1) {
if (sum > 0xffff_ffff_ffff) {
// ~2^48 at max
sum %= 10;
}
sum += ns[i] * (odd ? 3 : 1);
odd ^= 1;
}
return [(10 - (sum % 10)) % 10];
}
compute(s: string): string {
const ds = String(s).replace(/[^0-9]/g, "");
const ns = [...ds].map(Number);
return String(this.computeFromNumVals(ns)[0]);
}
parse(s: string): [string, string] {
const m = String(s).match(/^(.*)([0-9])$/s);
if (m != null) {
return [m[1], m[2]];
} else {
throw new SyntaxError("could not find check character(s)");
}
}
generate(s: string): string {
return `${s}${this.compute(s)}`;
}
validate(s: string): boolean {
const [bare, cc] = this.parse(s);
return this.compute(bare) === cc;
}
}
/**
* The standard check digit algorithm for GS1 data structures (including GTIN).
*
* Note: This implementation does not check the length of a number; however, it
* is not recommended to use numbers longer than 18 digits because GS1 General
* Specifications do not explicitly specify an algorithm for them.
*/
export const gtin: CdigitAlgo = new GTIN(
"gtin",
"GTINs (including UPC, EAN, ISBN-13, etc.)"
);