@ibiliaze/stringman 3.14.0 → 3.15.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 CHANGED
@@ -154,6 +154,7 @@ export declare const ticket: {
154
154
  sectionId: string;
155
155
  row: number;
156
156
  col: number;
157
+ fixtureId?: string;
157
158
  rndLen?: number;
158
159
  dashed?: boolean;
159
160
  }) => string;
package/dist/seat.d.ts CHANGED
@@ -11,5 +11,5 @@ export declare const getFromItemId: (itemId: string) => {
11
11
  fixtureId: string;
12
12
  sectionId: string;
13
13
  seatId: string;
14
- categoryId: String;
14
+ categoryId: string;
15
15
  };
package/dist/ticket.d.ts CHANGED
@@ -1,30 +1,90 @@
1
+ /**
2
+ * Compute a Luhn mod-10 check digit for a numeric string.
3
+ * @param digits Numeric string (no separators).
4
+ * @returns Single check digit as a string.
5
+ */
1
6
  export declare const luhn10: (digits: string) => string;
7
+ /**
8
+ * Compact, stable 2-digit number for a venue or any short code.
9
+ * Uses a small SHA-1 based hash and modulo to fit the requested width.
10
+ */
2
11
  export declare const venueBlockNum: (venueCode: string, len?: number) => string;
12
+ /**
13
+ * Compact, stable 2-digit number for a Fixture/Match ID string.
14
+ * If you pass your DB fixtureId here, you’ll get a short numeric block.
15
+ */
16
+ export declare const fixtureIdBlockNum: (fixtureId: string, len?: number) => string;
17
+ /**
18
+ * Compact, stable 3-digit number for a specific seat (section + row + col).
19
+ */
3
20
  export declare const seatBlockNum: (sectionId: string, row: number, col: number, len?: number) => string;
21
+ /**
22
+ * 6-digit date/time block derived from local start time.
23
+ * Input format expected: "YYYY-MM-DDThh:mm" (local, no 'Z').
24
+ * Encodes YYMMDDhhmm then reduces modulo 10^len to keep width stable.
25
+ */
4
26
  export declare const fixtureBlockNum: (fixtureLocalIso: string, len?: number) => string;
27
+ /**
28
+ * Cryptographically strong random numeric block of the given length.
29
+ */
5
30
  export declare const rndBlockNum: (len?: number) => string;
31
+ /**
32
+ * Build a numeric ticket code with blocks ordered to match the picture *in reverse*:
33
+ * Random (3) + Sector/Block/Col (3) + Date/Time (6) + Fixture/Match ID (2) + Luhn(1)
34
+ *
35
+ * Default dashed display groups: 3-3-6-2-1
36
+ *
37
+ * Lengths are chosen to keep the overall size at 15 digits (incl. Luhn),
38
+ * which plays nicely with scanners and manual entry.
39
+ */
6
40
  export declare const buildTicketCodeNumeric: (args: {
41
+ /** Human/ops venue short code (e.g. "SB"). Used only if fixtureId is not provided. */
7
42
  venueCode: string;
43
+ /** Local fixture start time, "YYYY-MM-DDThh:mm" (no 'Z'). */
8
44
  fixtureLocalIso: string;
45
+ /** Section identifier as used in your DB. */
9
46
  sectionId: string;
47
+ /** Seat row number. */
10
48
  row: number;
49
+ /** Seat column/number. */
11
50
  col: number;
51
+ /** Optional: your DB fixtureId. If present, becomes the 2-digit “Fixture/Match ID” block. */
52
+ fixtureId?: string;
53
+ /** Random block length (default 3). */
12
54
  rndLen?: number;
55
+ /** Show dashes for readability (default true). */
13
56
  dashed?: boolean;
14
57
  }) => string;
58
+ /**
59
+ * Validate a code built by buildTicketCodeNumeric (dashes optional).
60
+ * Checks: numeric, correct total length (15), and Luhn checksum.
61
+ */
15
62
  export declare const validateTicketCodeNumeric: (code: string) => boolean;
63
+ /**
64
+ * A stable, human-readable internal ID for a seat within a fixture.
65
+ * (Not used in the printed/scanned code — just handy elsewhere.)
66
+ */
16
67
  export declare const getTicketId: ({ fixtureId, sectionName, seatName, }: {
17
68
  fixtureId: string;
18
69
  sectionName: string;
19
70
  seatName: string;
20
71
  }) => string;
21
72
  export declare const buildTicketCode: (args: {
73
+ /** Human/ops venue short code (e.g. "SB"). Used only if fixtureId is not provided. */
22
74
  venueCode: string;
75
+ /** Local fixture start time, "YYYY-MM-DDThh:mm" (no 'Z'). */
23
76
  fixtureLocalIso: string;
77
+ /** Section identifier as used in your DB. */
24
78
  sectionId: string;
79
+ /** Seat row number. */
25
80
  row: number;
81
+ /** Seat column/number. */
26
82
  col: number;
83
+ /** Optional: your DB fixtureId. If present, becomes the 2-digit “Fixture/Match ID” block. */
84
+ fixtureId?: string;
85
+ /** Random block length (default 3). */
27
86
  rndLen?: number;
87
+ /** Show dashes for readability (default true). */
28
88
  dashed?: boolean;
29
89
  }) => string;
30
90
  export declare const validateTicketCode: (code: string) => boolean;
package/dist/ticket.js CHANGED
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateTicketCode = exports.buildTicketCode = exports.getTicketId = exports.validateTicketCodeNumeric = exports.buildTicketCodeNumeric = exports.rndBlockNum = exports.fixtureBlockNum = exports.seatBlockNum = exports.venueBlockNum = exports.luhn10 = void 0;
3
+ exports.validateTicketCode = exports.buildTicketCode = exports.getTicketId = exports.validateTicketCodeNumeric = exports.buildTicketCodeNumeric = exports.rndBlockNum = exports.fixtureBlockNum = exports.seatBlockNum = exports.fixtureIdBlockNum = exports.venueBlockNum = exports.luhn10 = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
- // ---------- helpers ----------
5
+ /**
6
+ * Left-pad a positive integer with zeros to a fixed width.
7
+ */
6
8
  const pad = (n, len) => n.toString().padStart(len, '0');
7
- // Luhn mod-10 for digits
9
+ /**
10
+ * Compute a Luhn mod-10 check digit for a numeric string.
11
+ * @param digits Numeric string (no separators).
12
+ * @returns Single check digit as a string.
13
+ */
8
14
  const luhn10 = (digits) => {
9
15
  let sum = 0;
10
16
  let dbl = false;
@@ -22,11 +28,13 @@ const luhn10 = (digits) => {
22
28
  return String(check);
23
29
  };
24
30
  exports.luhn10 = luhn10;
25
- // ---------- numeric blocks ----------
31
+ /**
32
+ * Compact, stable 2-digit number for a venue or any short code.
33
+ * Uses a small SHA-1 based hash and modulo to fit the requested width.
34
+ */
26
35
  const venueBlockNum = (venueCode, len = 2) => {
27
36
  try {
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
37
+ const h = (0, node_crypto_1.createHash)('sha1').update(venueCode.toUpperCase()).digest();
30
38
  const n = ((h[0] << 8) | h[1]) % 10 ** len;
31
39
  return pad(n, len);
32
40
  }
@@ -35,9 +43,26 @@ const venueBlockNum = (venueCode, len = 2) => {
35
43
  }
36
44
  };
37
45
  exports.venueBlockNum = venueBlockNum;
46
+ /**
47
+ * Compact, stable 2-digit number for a Fixture/Match ID string.
48
+ * If you pass your DB fixtureId here, you’ll get a short numeric block.
49
+ */
50
+ const fixtureIdBlockNum = (fixtureId, len = 2) => {
51
+ try {
52
+ const h = (0, node_crypto_1.createHash)('sha1').update(fixtureId).digest();
53
+ const n = ((h[0] << 8) | h[1]) % 10 ** len;
54
+ return pad(n, len);
55
+ }
56
+ catch {
57
+ return ''.padStart(len, '0');
58
+ }
59
+ };
60
+ exports.fixtureIdBlockNum = fixtureIdBlockNum;
61
+ /**
62
+ * Compact, stable 3-digit number for a specific seat (section + row + col).
63
+ */
38
64
  const seatBlockNum = (sectionId, row, col, len = 3) => {
39
65
  try {
40
- // Hash -> 16-bit -> modulo 10^len
41
66
  const h = (0, node_crypto_1.createHash)('sha1').update(`${sectionId}:${row}:${col}`).digest();
42
67
  const n16 = (h[0] << 8) | h[1];
43
68
  const n = n16 % 10 ** len;
@@ -48,14 +73,17 @@ const seatBlockNum = (sectionId, row, col, len = 3) => {
48
73
  }
49
74
  };
50
75
  exports.seatBlockNum = seatBlockNum;
76
+ /**
77
+ * 6-digit date/time block derived from local start time.
78
+ * Input format expected: "YYYY-MM-DDThh:mm" (local, no 'Z').
79
+ * Encodes YYMMDDhhmm then reduces modulo 10^len to keep width stable.
80
+ */
51
81
  const fixtureBlockNum = (fixtureLocalIso, len = 6) => {
52
82
  try {
53
- // Expect "YYYY-MM-DDThh:mm" (local, no Z).
54
83
  const m = fixtureLocalIso.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/);
55
84
  if (!m)
56
85
  throw new Error('Bad fixtureLocalIso');
57
86
  const [_, y, M, d, h, mnt] = m;
58
- // YYMMDDhhmm is 10 digits; compress to len digits via mod 10^len.
59
87
  const compact = Number(`${y.slice(2)}${M}${d}${h}${mnt}`); // e.g. 2510312000
60
88
  const n = compact % 10 ** len;
61
89
  return pad(n, len);
@@ -65,9 +93,11 @@ const fixtureBlockNum = (fixtureLocalIso, len = 6) => {
65
93
  }
66
94
  };
67
95
  exports.fixtureBlockNum = fixtureBlockNum;
96
+ /**
97
+ * Cryptographically strong random numeric block of the given length.
98
+ */
68
99
  const rndBlockNum = (len = 3) => {
69
100
  try {
70
- // Uniform cryptographic integer in [0, 10^len)
71
101
  const max = 10 ** len;
72
102
  return pad((0, node_crypto_1.randomInt)(0, max), len);
73
103
  }
@@ -76,31 +106,46 @@ const rndBlockNum = (len = 3) => {
76
106
  }
77
107
  };
78
108
  exports.rndBlockNum = rndBlockNum;
79
- // ---------- builder & validator (numeric-only) ----------
109
+ /**
110
+ * Build a numeric ticket code with blocks ordered to match the picture *in reverse*:
111
+ * Random (3) + Sector/Block/Col (3) + Date/Time (6) + Fixture/Match ID (2) + Luhn(1)
112
+ *
113
+ * Default dashed display groups: 3-3-6-2-1
114
+ *
115
+ * Lengths are chosen to keep the overall size at 15 digits (incl. Luhn),
116
+ * which plays nicely with scanners and manual entry.
117
+ */
80
118
  const buildTicketCodeNumeric = (args) => {
81
119
  try {
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
120
+ // --- individual blocks ---
121
+ const R = (0, exports.rndBlockNum)(args.rndLen ?? 3); // Random (3)
122
+ const S = (0, exports.seatBlockNum)(args.sectionId, args.row, args.col); // Seat (3)
123
+ const D = (0, exports.fixtureBlockNum)(args.fixtureLocalIso, 6); // Date/Time (6)
124
+ // Fixture/Match ID (2): prefer fixtureId; fallback to venue code hash
125
+ const F = args.fixtureId ? (0, exports.fixtureIdBlockNum)(args.fixtureId, 2) : (0, exports.venueBlockNum)(args.venueCode, 2);
126
+ // --- assemble (REVERSED order vs the picture’s left-to-right) ---
127
+ const body = `${R}${S}${D}${F}`; // 3+3+6+2 = 14
87
128
  const C = (0, exports.luhn10)(body); // 1 digit
88
- const numeric = `${body}${C}`; // 15 digits total
129
+ const numeric = `${body}${C}`; // total 15 digits
89
130
  if (args.dashed === false)
90
131
  return numeric;
91
- // Grouping 2-6-3-3-1 for readability
92
- return `${VV}-${E}-${S}-${R}-${C}`;
132
+ // Readability grouping for scanners/ushers: 3-3-6-2-1
133
+ return `${R}-${S}-${D}-${F}-${C}`;
93
134
  }
94
135
  catch {
95
136
  return '';
96
137
  }
97
138
  };
98
139
  exports.buildTicketCodeNumeric = buildTicketCodeNumeric;
140
+ /**
141
+ * Validate a code built by buildTicketCodeNumeric (dashes optional).
142
+ * Checks: numeric, correct total length (15), and Luhn checksum.
143
+ */
99
144
  const validateTicketCodeNumeric = (code) => {
100
145
  try {
101
146
  const clean = code.replace(/-/g, '');
102
147
  if (!/^\d{15}$/.test(clean))
103
- return false; // 2+6+3+3+1
148
+ return false; // 3+3+6+2+1
104
149
  const body = clean.slice(0, -1);
105
150
  const check = clean.slice(-1);
106
151
  return (0, exports.luhn10)(body) === check;
@@ -110,8 +155,12 @@ const validateTicketCodeNumeric = (code) => {
110
155
  }
111
156
  };
112
157
  exports.validateTicketCodeNumeric = validateTicketCodeNumeric;
158
+ /**
159
+ * A stable, human-readable internal ID for a seat within a fixture.
160
+ * (Not used in the printed/scanned code — just handy elsewhere.)
161
+ */
113
162
  const getTicketId = ({ fixtureId, sectionName, seatName, }) => `${sectionName}-${seatName}-${fixtureId}`;
114
163
  exports.getTicketId = getTicketId;
115
- // ---------- compatibility shim (if you want same names as before) ----------
164
+ // Compatibility aliases (if other code imports these names already)
116
165
  exports.buildTicketCode = exports.buildTicketCodeNumeric;
117
166
  exports.validateTicketCode = exports.validateTicketCodeNumeric;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@ibiliaze/stringman",
3
- "version": "3.14.0",
3
+ "version": "3.15.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.14.0 -m 'v3.14.0'; git push origin v3.14.0; git push",
10
+ "git": "git add .; git commit -m 'changes'; git tag -a v3.14.1 -m 'v3.14.1'; git push origin v3.14.1; git push",
11
11
  "push": "npm run build; npm run git; npm run pub"
12
12
  },
13
13
  "author": "Ibi Hasanli",
package/src/seat.ts CHANGED
@@ -17,4 +17,4 @@ export const getItemId = ({
17
17
 
18
18
  export const getFromItemId = (
19
19
  itemId: string
20
- ): { fixtureId: string; sectionId: string; seatId: string; categoryId: String } => JSON.parse(itemId);
20
+ ): { fixtureId: string; sectionId: string; seatId: string; categoryId: string } => JSON.parse(itemId);
package/src/ticket.ts CHANGED
@@ -1,9 +1,15 @@
1
1
  import { createHash, randomInt } from 'node:crypto';
2
2
 
3
- // ---------- helpers ----------
3
+ /**
4
+ * Left-pad a positive integer with zeros to a fixed width.
5
+ */
4
6
  const pad = (n: number, len: number) => n.toString().padStart(len, '0');
5
7
 
6
- // Luhn mod-10 for digits
8
+ /**
9
+ * Compute a Luhn mod-10 check digit for a numeric string.
10
+ * @param digits Numeric string (no separators).
11
+ * @returns Single check digit as a string.
12
+ */
7
13
  export const luhn10 = (digits: string): string => {
8
14
  let sum = 0;
9
15
  let dbl = false;
@@ -20,11 +26,13 @@ export const luhn10 = (digits: string): string => {
20
26
  return String(check);
21
27
  };
22
28
 
23
- // ---------- numeric blocks ----------
29
+ /**
30
+ * Compact, stable 2-digit number for a venue or any short code.
31
+ * Uses a small SHA-1 based hash and modulo to fit the requested width.
32
+ */
24
33
  export const venueBlockNum = (venueCode: string, len = 2) => {
25
34
  try {
26
- // Small, stable hash of the code -> 0..(10^len - 1)
27
- const h = createHash('sha1').update(venueCode.toUpperCase()).digest(); // 20 bytes
35
+ const h = createHash('sha1').update(venueCode.toUpperCase()).digest();
28
36
  const n = ((h[0] << 8) | h[1]) % 10 ** len;
29
37
  return pad(n, len);
30
38
  } catch {
@@ -32,9 +40,25 @@ export const venueBlockNum = (venueCode: string, len = 2) => {
32
40
  }
33
41
  };
34
42
 
43
+ /**
44
+ * Compact, stable 2-digit number for a Fixture/Match ID string.
45
+ * If you pass your DB fixtureId here, you’ll get a short numeric block.
46
+ */
47
+ export const fixtureIdBlockNum = (fixtureId: string, len = 2) => {
48
+ try {
49
+ const h = createHash('sha1').update(fixtureId).digest();
50
+ const n = ((h[0] << 8) | h[1]) % 10 ** len;
51
+ return pad(n, len);
52
+ } catch {
53
+ return ''.padStart(len, '0');
54
+ }
55
+ };
56
+
57
+ /**
58
+ * Compact, stable 3-digit number for a specific seat (section + row + col).
59
+ */
35
60
  export const seatBlockNum = (sectionId: string, row: number, col: number, len = 3) => {
36
61
  try {
37
- // Hash -> 16-bit -> modulo 10^len
38
62
  const h = createHash('sha1').update(`${sectionId}:${row}:${col}`).digest();
39
63
  const n16 = (h[0] << 8) | h[1];
40
64
  const n = n16 % 10 ** len;
@@ -44,13 +68,16 @@ export const seatBlockNum = (sectionId: string, row: number, col: number, len =
44
68
  }
45
69
  };
46
70
 
71
+ /**
72
+ * 6-digit date/time block derived from local start time.
73
+ * Input format expected: "YYYY-MM-DDThh:mm" (local, no 'Z').
74
+ * Encodes YYMMDDhhmm then reduces modulo 10^len to keep width stable.
75
+ */
47
76
  export const fixtureBlockNum = (fixtureLocalIso: string, len = 6) => {
48
77
  try {
49
- // Expect "YYYY-MM-DDThh:mm" (local, no Z).
50
78
  const m = fixtureLocalIso.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/);
51
79
  if (!m) throw new Error('Bad fixtureLocalIso');
52
80
  const [_, y, M, d, h, mnt] = m;
53
- // YYMMDDhhmm is 10 digits; compress to len digits via mod 10^len.
54
81
  const compact = Number(`${y.slice(2)}${M}${d}${h}${mnt}`); // e.g. 2510312000
55
82
  const n = compact % 10 ** len;
56
83
  return pad(n, len);
@@ -59,9 +86,11 @@ export const fixtureBlockNum = (fixtureLocalIso: string, len = 6) => {
59
86
  }
60
87
  };
61
88
 
89
+ /**
90
+ * Cryptographically strong random numeric block of the given length.
91
+ */
62
92
  export const rndBlockNum = (len = 3) => {
63
93
  try {
64
- // Uniform cryptographic integer in [0, 10^len)
65
94
  const max = 10 ** len;
66
95
  return pad(randomInt(0, max), len);
67
96
  } catch {
@@ -69,38 +98,63 @@ export const rndBlockNum = (len = 3) => {
69
98
  }
70
99
  };
71
100
 
72
- // ---------- builder & validator (numeric-only) ----------
101
+ /**
102
+ * Build a numeric ticket code with blocks ordered to match the picture *in reverse*:
103
+ * Random (3) + Sector/Block/Col (3) + Date/Time (6) + Fixture/Match ID (2) + Luhn(1)
104
+ *
105
+ * Default dashed display groups: 3-3-6-2-1
106
+ *
107
+ * Lengths are chosen to keep the overall size at 15 digits (incl. Luhn),
108
+ * which plays nicely with scanners and manual entry.
109
+ */
73
110
  export const buildTicketCodeNumeric = (args: {
74
- venueCode: string; // e.g. "SB"
75
- fixtureLocalIso: string; // "2025-10-31T20:00" in venue TZ
111
+ /** Human/ops venue short code (e.g. "SB"). Used only if fixtureId is not provided. */
112
+ venueCode: string;
113
+ /** Local fixture start time, "YYYY-MM-DDThh:mm" (no 'Z'). */
114
+ fixtureLocalIso: string;
115
+ /** Section identifier as used in your DB. */
76
116
  sectionId: string;
117
+ /** Seat row number. */
77
118
  row: number;
119
+ /** Seat column/number. */
78
120
  col: number;
79
- rndLen?: number; // default 3 (use 4 if you want more entropy)
80
- dashed?: boolean; // default true (format with dashes)
121
+ /** Optional: your DB fixtureId. If present, becomes the 2-digit “Fixture/Match ID” block. */
122
+ fixtureId?: string;
123
+ /** Random block length (default 3). */
124
+ rndLen?: number;
125
+ /** Show dashes for readability (default true). */
126
+ dashed?: boolean;
81
127
  }) => {
82
128
  try {
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
129
+ // --- individual blocks ---
130
+ const R = rndBlockNum(args.rndLen ?? 3); // Random (3)
131
+ const S = seatBlockNum(args.sectionId, args.row, args.col); // Seat (3)
132
+ const D = fixtureBlockNum(args.fixtureLocalIso, 6); // Date/Time (6)
133
+ // Fixture/Match ID (2): prefer fixtureId; fallback to venue code hash
134
+ const F = args.fixtureId ? fixtureIdBlockNum(args.fixtureId, 2) : venueBlockNum(args.venueCode, 2);
87
135
 
88
- const body = `${VV}${E}${S}${R}`; // 14 digits
136
+ // --- assemble (REVERSED order vs the picture’s left-to-right) ---
137
+ const body = `${R}${S}${D}${F}`; // 3+3+6+2 = 14
89
138
  const C = luhn10(body); // 1 digit
90
- const numeric = `${body}${C}`; // 15 digits total
139
+ const numeric = `${body}${C}`; // total 15 digits
91
140
 
92
141
  if (args.dashed === false) return numeric;
93
- // Grouping 2-6-3-3-1 for readability
94
- return `${VV}-${E}-${S}-${R}-${C}`;
142
+
143
+ // Readability grouping for scanners/ushers: 3-3-6-2-1
144
+ return `${R}-${S}-${D}-${F}-${C}`;
95
145
  } catch {
96
146
  return '';
97
147
  }
98
148
  };
99
149
 
150
+ /**
151
+ * Validate a code built by buildTicketCodeNumeric (dashes optional).
152
+ * Checks: numeric, correct total length (15), and Luhn checksum.
153
+ */
100
154
  export const validateTicketCodeNumeric = (code: string) => {
101
155
  try {
102
156
  const clean = code.replace(/-/g, '');
103
- if (!/^\d{15}$/.test(clean)) return false; // 2+6+3+3+1
157
+ if (!/^\d{15}$/.test(clean)) return false; // 3+3+6+2+1
104
158
  const body = clean.slice(0, -1);
105
159
  const check = clean.slice(-1);
106
160
  return luhn10(body) === check;
@@ -109,6 +163,10 @@ export const validateTicketCodeNumeric = (code: string) => {
109
163
  }
110
164
  };
111
165
 
166
+ /**
167
+ * A stable, human-readable internal ID for a seat within a fixture.
168
+ * (Not used in the printed/scanned code — just handy elsewhere.)
169
+ */
112
170
  export const getTicketId = ({
113
171
  fixtureId,
114
172
  sectionName,
@@ -119,6 +177,6 @@ export const getTicketId = ({
119
177
  seatName: string;
120
178
  }) => `${sectionName}-${seatName}-${fixtureId}`;
121
179
 
122
- // ---------- compatibility shim (if you want same names as before) ----------
180
+ // Compatibility aliases (if other code imports these names already)
123
181
  export const buildTicketCode = buildTicketCodeNumeric;
124
182
  export const validateTicketCode = validateTicketCodeNumeric;