-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
107 lines (86 loc) · 2.23 KB
/
index.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
interface IValidationOption {
checkPrefix?: boolean;
}
export const validate = (hkid: string, option?: IValidationOption): boolean => {
const shouldCheckPrefix = option?.checkPrefix ?? false;
const regexp = `^([A-Z]{1,2})([0-9]{6})(([0-9A])|\\(([0-9A])\\))$`;
const found = hkid.toUpperCase().match(regexp);
if (!found) {
return false;
}
const prefix = found[1];
const content = found[2];
const checkDigit = found[4] ?? found[5];
if (shouldCheckPrefix === true && checkPrefix(prefix) === false) {
return false;
}
return checkDigit === calculateCheckDigit(`${prefix}${content}`);
};
export const random = (): string => {
const randomIndex = Math.floor(Math.random() * knownPrefixes.length);
const prefix = knownPrefixes[randomIndex];
const digit = Math.random().toString().substr(2, 6);
const candidate = `${prefix}${digit}`;
const checkDigit = calculateCheckDigit(`${candidate}`);
return `${candidate}${checkDigit}`.toUpperCase().trim();
};
/*
* source: https://accessinfo.hk/en/request/you_guan_xiang_gang_shen_fen_zhe_2
*/
const knownPrefixes: string[] = [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"J",
"K",
"L",
"M",
"N",
"P",
"R",
"S",
"T",
"V",
"W",
"Y",
"Z",
"WX",
"XA",
"XB",
"XC",
];
const checkPrefix = (prefix: string): boolean => {
return knownPrefixes.some((value) => value === prefix);
};
const calculateCheckDigit = (candidate: string): string => {
let checkDigit = (candidate.length === 8 ? candidate : ` ${candidate}`)
.split("")
.reduce((previousValue, currentValue, currentIndex) => {
const weight = 9 - currentIndex;
const value = getValue(currentValue);
return previousValue + ((weight * value) % 11);
}, 0);
checkDigit = checkDigit % 11;
if (checkDigit !== 0) {
checkDigit = 11 - checkDigit;
}
return `${checkDigit === 10 ? "A" : checkDigit}`;
};
const A = "A".charCodeAt(0);
const Z = "Z".charCodeAt(0);
const getValue = (char: string): number => {
const charCode = char.charCodeAt(0);
if (char === " ") {
return 36;
} else if (!isNaN(parseInt(char))) {
return parseInt(char);
} else if (charCode >= A && charCode <= Z) {
return charCode - A + 10;
}
throw new Error();
};