@ibiliaze/stringman 3.8.0 → 3.9.0
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.
- package/dist/index.d.ts +5 -8
- package/dist/ticket.d.ts +16 -8
- package/dist/ticket.js +83 -46
- package/package.json +2 -2
- package/src/ticket.ts +80 -49
package/dist/index.d.ts
CHANGED
|
@@ -143,11 +143,7 @@ export declare const invalidPw: (password: string, passwordLength?: number) => s
|
|
|
143
143
|
export declare const b36: (n: number) => string;
|
|
144
144
|
export declare const luhn36: (s: string) => string;
|
|
145
145
|
export declare const ticket: {
|
|
146
|
-
getTicketId:
|
|
147
|
-
fixtureId: string;
|
|
148
|
-
sectionName: string;
|
|
149
|
-
seatName: string;
|
|
150
|
-
}) => string;
|
|
146
|
+
getTicketId: any;
|
|
151
147
|
buildTicketCode: (args: {
|
|
152
148
|
venueCode: string;
|
|
153
149
|
fixtureLocalIso: string;
|
|
@@ -155,9 +151,10 @@ export declare const ticket: {
|
|
|
155
151
|
row: number;
|
|
156
152
|
col: number;
|
|
157
153
|
rndLen?: number;
|
|
154
|
+
dashed?: boolean;
|
|
158
155
|
}) => string;
|
|
159
156
|
validateTicketCode: (code: string) => boolean;
|
|
160
|
-
rndBlock:
|
|
161
|
-
fixtureBlock:
|
|
162
|
-
seatBlock:
|
|
157
|
+
rndBlock: any;
|
|
158
|
+
fixtureBlock: any;
|
|
159
|
+
seatBlock: any;
|
|
163
160
|
};
|
package/dist/ticket.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
1
|
+
export declare const luhn10: (digits: string) => string;
|
|
2
|
+
export declare const venueBlockNum: (venueCode: string, len?: number) => string;
|
|
3
|
+
export declare const seatBlockNum: (sectionId: string, row: number, col: number, len?: number) => string;
|
|
4
|
+
export declare const fixtureBlockNum: (fixtureLocalIso: string, len?: number) => string;
|
|
5
|
+
export declare const rndBlockNum: (len?: number) => string;
|
|
6
|
+
export declare const buildTicketCodeNumeric: (args: {
|
|
7
|
+
venueCode: string;
|
|
8
|
+
fixtureLocalIso: string;
|
|
9
|
+
sectionId: string;
|
|
10
|
+
row: number;
|
|
11
|
+
col: number;
|
|
12
|
+
rndLen?: number;
|
|
13
|
+
dashed?: boolean;
|
|
14
|
+
}) => string;
|
|
15
|
+
export declare const validateTicketCodeNumeric: (code: string) => boolean;
|
|
4
16
|
export declare const buildTicketCode: (args: {
|
|
5
17
|
venueCode: string;
|
|
6
18
|
fixtureLocalIso: string;
|
|
@@ -8,10 +20,6 @@ export declare const buildTicketCode: (args: {
|
|
|
8
20
|
row: number;
|
|
9
21
|
col: number;
|
|
10
22
|
rndLen?: number;
|
|
23
|
+
dashed?: boolean;
|
|
11
24
|
}) => string;
|
|
12
25
|
export declare const validateTicketCode: (code: string) => boolean;
|
|
13
|
-
export declare const getTicketId: ({ fixtureId, sectionName, seatName, }: {
|
|
14
|
-
fixtureId: string;
|
|
15
|
-
sectionName: string;
|
|
16
|
-
seatName: string;
|
|
17
|
-
}) => string;
|
package/dist/ticket.js
CHANGED
|
@@ -1,78 +1,115 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.validateTicketCode = exports.buildTicketCode = exports.validateTicketCodeNumeric = exports.buildTicketCodeNumeric = exports.rndBlockNum = exports.fixtureBlockNum = exports.seatBlockNum = exports.venueBlockNum = exports.luhn10 = void 0;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
|
|
6
|
-
const
|
|
5
|
+
// ---------- helpers ----------
|
|
6
|
+
const pad = (n, len) => n.toString().padStart(len, '0');
|
|
7
|
+
// Luhn mod-10 for digits
|
|
8
|
+
const luhn10 = (digits) => {
|
|
9
|
+
let sum = 0;
|
|
10
|
+
let dbl = false;
|
|
11
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
12
|
+
let d = digits.charCodeAt(i) - 48; // '0' -> 48
|
|
13
|
+
if (dbl) {
|
|
14
|
+
d *= 2;
|
|
15
|
+
if (d > 9)
|
|
16
|
+
d -= 9;
|
|
17
|
+
}
|
|
18
|
+
sum += d;
|
|
19
|
+
dbl = !dbl;
|
|
20
|
+
}
|
|
21
|
+
const check = (10 - (sum % 10)) % 10;
|
|
22
|
+
return String(check);
|
|
23
|
+
};
|
|
24
|
+
exports.luhn10 = luhn10;
|
|
25
|
+
// ---------- numeric blocks ----------
|
|
26
|
+
const venueBlockNum = (venueCode, len = 2) => {
|
|
7
27
|
try {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const n = (h[0] << 8) | h[1];
|
|
11
|
-
|
|
12
|
-
return code.slice(-len);
|
|
28
|
+
// Small, stable hash of the code -> 0..(10^len - 1)
|
|
29
|
+
const h = (0, node_crypto_1.createHash)('sha1').update(venueCode.toUpperCase()).digest(); // 20 bytes
|
|
30
|
+
const n = ((h[0] << 8) | h[1]) % 10 ** len;
|
|
31
|
+
return pad(n, len);
|
|
13
32
|
}
|
|
14
|
-
catch
|
|
15
|
-
return '';
|
|
33
|
+
catch {
|
|
34
|
+
return ''.padStart(len, '0');
|
|
16
35
|
}
|
|
17
36
|
};
|
|
18
|
-
exports.
|
|
19
|
-
|
|
20
|
-
const fixtureBlock = (fixtureLocalIso) => {
|
|
37
|
+
exports.venueBlockNum = venueBlockNum;
|
|
38
|
+
const seatBlockNum = (sectionId, row, col, len = 3) => {
|
|
21
39
|
try {
|
|
22
|
-
//
|
|
40
|
+
// Hash -> 16-bit -> modulo 10^len
|
|
41
|
+
const h = (0, node_crypto_1.createHash)('sha1').update(`${sectionId}:${row}:${col}`).digest();
|
|
42
|
+
const n16 = (h[0] << 8) | h[1];
|
|
43
|
+
const n = n16 % 10 ** len;
|
|
44
|
+
return pad(n, len);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return ''.padStart(len, '0');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.seatBlockNum = seatBlockNum;
|
|
51
|
+
const fixtureBlockNum = (fixtureLocalIso, len = 6) => {
|
|
52
|
+
try {
|
|
53
|
+
// Expect "YYYY-MM-DDThh:mm" (local, no Z).
|
|
23
54
|
const m = fixtureLocalIso.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/);
|
|
24
55
|
if (!m)
|
|
25
56
|
throw new Error('Bad fixtureLocalIso');
|
|
26
57
|
const [_, y, M, d, h, mnt] = m;
|
|
27
|
-
|
|
28
|
-
|
|
58
|
+
// YYMMDDhhmm is 10 digits; compress to len digits via mod 10^len.
|
|
59
|
+
const compact = Number(`${y.slice(2)}${M}${d}${h}${mnt}`); // e.g. 2510312000
|
|
60
|
+
const n = compact % 10 ** len;
|
|
61
|
+
return pad(n, len);
|
|
29
62
|
}
|
|
30
|
-
catch
|
|
31
|
-
return '';
|
|
63
|
+
catch {
|
|
64
|
+
return ''.padStart(len, '0');
|
|
32
65
|
}
|
|
33
66
|
};
|
|
34
|
-
exports.
|
|
35
|
-
|
|
36
|
-
const rndBlock = (len = 3) => {
|
|
67
|
+
exports.fixtureBlockNum = fixtureBlockNum;
|
|
68
|
+
const rndBlockNum = (len = 3) => {
|
|
37
69
|
try {
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
return (0,
|
|
70
|
+
// Uniform cryptographic integer in [0, 10^len)
|
|
71
|
+
const max = 10 ** len;
|
|
72
|
+
return pad((0, node_crypto_1.randomInt)(0, max), len);
|
|
41
73
|
}
|
|
42
|
-
catch
|
|
43
|
-
return '';
|
|
74
|
+
catch {
|
|
75
|
+
return ''.padStart(len, '0');
|
|
44
76
|
}
|
|
45
77
|
};
|
|
46
|
-
exports.
|
|
47
|
-
|
|
78
|
+
exports.rndBlockNum = rndBlockNum;
|
|
79
|
+
// ---------- builder & validator (numeric-only) ----------
|
|
80
|
+
const buildTicketCodeNumeric = (args) => {
|
|
48
81
|
try {
|
|
49
|
-
const VV = args.venueCode
|
|
50
|
-
const E = (0, exports.
|
|
51
|
-
const S = (0, exports.
|
|
52
|
-
const R = (0, exports.
|
|
53
|
-
const body = `${VV}${E}${S}${R}`;
|
|
54
|
-
const C = (0,
|
|
55
|
-
|
|
82
|
+
const VV = (0, exports.venueBlockNum)(args.venueCode, 2); // 2 digits
|
|
83
|
+
const E = (0, exports.fixtureBlockNum)(args.fixtureLocalIso, 6); // 6 digits
|
|
84
|
+
const S = (0, exports.seatBlockNum)(args.sectionId, args.row, args.col); // 3 digits
|
|
85
|
+
const R = (0, exports.rndBlockNum)(args.rndLen ?? 3); // 3 digits
|
|
86
|
+
const body = `${VV}${E}${S}${R}`; // 14 digits
|
|
87
|
+
const C = (0, exports.luhn10)(body); // 1 digit
|
|
88
|
+
const numeric = `${body}${C}`; // 15 digits total
|
|
89
|
+
if (args.dashed === false)
|
|
90
|
+
return numeric;
|
|
91
|
+
// Grouping 2-6-3-3-1 for readability
|
|
56
92
|
return `${VV}-${E}-${S}-${R}-${C}`;
|
|
57
93
|
}
|
|
58
|
-
catch
|
|
94
|
+
catch {
|
|
59
95
|
return '';
|
|
60
96
|
}
|
|
61
97
|
};
|
|
62
|
-
exports.
|
|
63
|
-
const
|
|
98
|
+
exports.buildTicketCodeNumeric = buildTicketCodeNumeric;
|
|
99
|
+
const validateTicketCodeNumeric = (code) => {
|
|
64
100
|
try {
|
|
65
|
-
const clean = code.replace(/-/g, '')
|
|
66
|
-
if (
|
|
67
|
-
return false;
|
|
101
|
+
const clean = code.replace(/-/g, '');
|
|
102
|
+
if (!/^\d{15}$/.test(clean))
|
|
103
|
+
return false; // 2+6+3+3+1
|
|
68
104
|
const body = clean.slice(0, -1);
|
|
69
105
|
const check = clean.slice(-1);
|
|
70
|
-
return (0,
|
|
106
|
+
return (0, exports.luhn10)(body) === check;
|
|
71
107
|
}
|
|
72
|
-
catch
|
|
108
|
+
catch {
|
|
73
109
|
return false;
|
|
74
110
|
}
|
|
75
111
|
};
|
|
76
|
-
exports.
|
|
77
|
-
|
|
78
|
-
exports.
|
|
112
|
+
exports.validateTicketCodeNumeric = validateTicketCodeNumeric;
|
|
113
|
+
// ---------- compatibility shim (if you want same names as before) ----------
|
|
114
|
+
exports.buildTicketCode = exports.buildTicketCodeNumeric;
|
|
115
|
+
exports.validateTicketCode = exports.validateTicketCodeNumeric;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ibiliaze/stringman",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"pub": "npm publish --access public",
|
|
10
|
-
"git": "git add .; git commit -m 'changes'; git tag -a v3.
|
|
10
|
+
"git": "git add .; git commit -m 'changes'; git tag -a v3.9.0 -m 'v3.9.0'; git push origin v3.9.0; git push",
|
|
11
11
|
"push": "npm run build; npm run git; npm run pub"
|
|
12
12
|
},
|
|
13
13
|
"author": "Ibi Hasanli",
|
package/src/ticket.ts
CHANGED
|
@@ -1,83 +1,114 @@
|
|
|
1
|
-
import { createHash,
|
|
2
|
-
import { b36, luhn36 } from '.';
|
|
1
|
+
import { createHash, randomInt } from 'node:crypto';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
// ---------- helpers ----------
|
|
4
|
+
const pad = (n: number, len: number) => n.toString().padStart(len, '0');
|
|
5
|
+
|
|
6
|
+
// Luhn mod-10 for digits
|
|
7
|
+
export const luhn10 = (digits: string): string => {
|
|
8
|
+
let sum = 0;
|
|
9
|
+
let dbl = false;
|
|
10
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
11
|
+
let d = digits.charCodeAt(i) - 48; // '0' -> 48
|
|
12
|
+
if (dbl) {
|
|
13
|
+
d *= 2;
|
|
14
|
+
if (d > 9) d -= 9;
|
|
15
|
+
}
|
|
16
|
+
sum += d;
|
|
17
|
+
dbl = !dbl;
|
|
18
|
+
}
|
|
19
|
+
const check = (10 - (sum % 10)) % 10;
|
|
20
|
+
return String(check);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// ---------- numeric blocks ----------
|
|
24
|
+
export const venueBlockNum = (venueCode: string, len = 2) => {
|
|
5
25
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const n = (h[0] << 8) | h[1];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return '';
|
|
26
|
+
// Small, stable hash of the code -> 0..(10^len - 1)
|
|
27
|
+
const h = createHash('sha1').update(venueCode.toUpperCase()).digest(); // 20 bytes
|
|
28
|
+
const n = ((h[0] << 8) | h[1]) % 10 ** len;
|
|
29
|
+
return pad(n, len);
|
|
30
|
+
} catch {
|
|
31
|
+
return ''.padStart(len, '0');
|
|
13
32
|
}
|
|
14
33
|
};
|
|
15
34
|
|
|
16
|
-
|
|
17
|
-
export const fixtureBlock = (fixtureLocalIso: string) => {
|
|
35
|
+
export const seatBlockNum = (sectionId: string, row: number, col: number, len = 3) => {
|
|
18
36
|
try {
|
|
19
|
-
//
|
|
37
|
+
// Hash -> 16-bit -> modulo 10^len
|
|
38
|
+
const h = createHash('sha1').update(`${sectionId}:${row}:${col}`).digest();
|
|
39
|
+
const n16 = (h[0] << 8) | h[1];
|
|
40
|
+
const n = n16 % 10 ** len;
|
|
41
|
+
return pad(n, len);
|
|
42
|
+
} catch {
|
|
43
|
+
return ''.padStart(len, '0');
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const fixtureBlockNum = (fixtureLocalIso: string, len = 6) => {
|
|
48
|
+
try {
|
|
49
|
+
// Expect "YYYY-MM-DDThh:mm" (local, no Z).
|
|
20
50
|
const m = fixtureLocalIso.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/);
|
|
21
51
|
if (!m) throw new Error('Bad fixtureLocalIso');
|
|
22
52
|
const [_, y, M, d, h, mnt] = m;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
53
|
+
// YYMMDDhhmm is 10 digits; compress to len digits via mod 10^len.
|
|
54
|
+
const compact = Number(`${y.slice(2)}${M}${d}${h}${mnt}`); // e.g. 2510312000
|
|
55
|
+
const n = compact % 10 ** len;
|
|
56
|
+
return pad(n, len);
|
|
57
|
+
} catch {
|
|
58
|
+
return ''.padStart(len, '0');
|
|
27
59
|
}
|
|
28
60
|
};
|
|
29
61
|
|
|
30
|
-
|
|
31
|
-
export const rndBlock = (len = 3) => {
|
|
62
|
+
export const rndBlockNum = (len = 3) => {
|
|
32
63
|
try {
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
return
|
|
36
|
-
} catch
|
|
37
|
-
return '';
|
|
64
|
+
// Uniform cryptographic integer in [0, 10^len)
|
|
65
|
+
const max = 10 ** len;
|
|
66
|
+
return pad(randomInt(0, max), len);
|
|
67
|
+
} catch {
|
|
68
|
+
return ''.padStart(len, '0');
|
|
38
69
|
}
|
|
39
70
|
};
|
|
40
71
|
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
// ---------- builder & validator (numeric-only) ----------
|
|
73
|
+
export const buildTicketCodeNumeric = (args: {
|
|
74
|
+
venueCode: string; // e.g. "SB"
|
|
43
75
|
fixtureLocalIso: string; // "2025-10-31T20:00" in venue TZ
|
|
44
76
|
sectionId: string;
|
|
45
77
|
row: number;
|
|
46
78
|
col: number;
|
|
47
|
-
rndLen?: number; // 3
|
|
79
|
+
rndLen?: number; // default 3 (use 4 if you want more entropy)
|
|
80
|
+
dashed?: boolean; // default true (format with dashes)
|
|
48
81
|
}) => {
|
|
49
82
|
try {
|
|
50
|
-
const VV = args.venueCode
|
|
51
|
-
const E =
|
|
52
|
-
const S =
|
|
53
|
-
const R =
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
83
|
+
const VV = venueBlockNum(args.venueCode, 2); // 2 digits
|
|
84
|
+
const E = fixtureBlockNum(args.fixtureLocalIso, 6); // 6 digits
|
|
85
|
+
const S = seatBlockNum(args.sectionId, args.row, args.col); // 3 digits
|
|
86
|
+
const R = rndBlockNum(args.rndLen ?? 3); // 3 digits
|
|
87
|
+
|
|
88
|
+
const body = `${VV}${E}${S}${R}`; // 14 digits
|
|
89
|
+
const C = luhn10(body); // 1 digit
|
|
90
|
+
const numeric = `${body}${C}`; // 15 digits total
|
|
91
|
+
|
|
92
|
+
if (args.dashed === false) return numeric;
|
|
93
|
+
// Grouping 2-6-3-3-1 for readability
|
|
57
94
|
return `${VV}-${E}-${S}-${R}-${C}`;
|
|
58
|
-
} catch
|
|
95
|
+
} catch {
|
|
59
96
|
return '';
|
|
60
97
|
}
|
|
61
98
|
};
|
|
62
99
|
|
|
63
|
-
export const
|
|
100
|
+
export const validateTicketCodeNumeric = (code: string) => {
|
|
64
101
|
try {
|
|
65
|
-
const clean = code.replace(/-/g, '')
|
|
66
|
-
if (
|
|
102
|
+
const clean = code.replace(/-/g, '');
|
|
103
|
+
if (!/^\d{15}$/.test(clean)) return false; // 2+6+3+3+1
|
|
67
104
|
const body = clean.slice(0, -1);
|
|
68
105
|
const check = clean.slice(-1);
|
|
69
|
-
return
|
|
70
|
-
} catch
|
|
106
|
+
return luhn10(body) === check;
|
|
107
|
+
} catch {
|
|
71
108
|
return false;
|
|
72
109
|
}
|
|
73
110
|
};
|
|
74
111
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
seatName,
|
|
79
|
-
}: {
|
|
80
|
-
fixtureId: string;
|
|
81
|
-
sectionName: string;
|
|
82
|
-
seatName: string;
|
|
83
|
-
}) => `${sectionName}-${seatName}-${fixtureId}`;
|
|
112
|
+
// ---------- compatibility shim (if you want same names as before) ----------
|
|
113
|
+
export const buildTicketCode = buildTicketCodeNumeric;
|
|
114
|
+
export const validateTicketCode = validateTicketCodeNumeric;
|