|
1 | 1 | import { randomBytes } from 'node:crypto' |
2 | 2 |
|
| 3 | +import { |
| 4 | + RegExpMatcher, |
| 5 | + englishDataset, |
| 6 | + englishRecommendedTransformers |
| 7 | +} from 'obscenity' |
| 8 | + |
| 9 | +/** |
| 10 | + * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed. |
| 11 | + * @param strCodes - array of binary input values |
| 12 | + */ |
| 13 | +export function convertToDecAlpha(strCodes: number[]) { |
| 14 | + const validChars = 'ABCDEFHJKLMNPRSTUVWXYZ23456789' |
| 15 | + const strLen = validChars.length |
| 16 | + const outArray = [] as string[] |
| 17 | + |
| 18 | + strCodes.forEach((code) => { |
| 19 | + const pos = (code / 256) * strLen |
| 20 | + outArray.push(validChars.charAt(pos)) |
| 21 | + }) |
| 22 | + |
| 23 | + return outArray.join('') |
| 24 | +} |
| 25 | + |
3 | 26 | /** |
4 | 27 | * Generates a reference number in the format of `XXX-XXX-XXX`, or `PREFIX-XXX-XXX` if a prefix is provided. |
5 | 28 | * Provides no guarantee on uniqueness. |
| 29 | + * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed |
| 30 | + * (see https://gunkies.org/wiki/DEC_alphabet ) |
6 | 31 | */ |
7 | 32 | export function generateUniqueReference(prefix?: string) { |
8 | 33 | const segmentLength = 3 |
9 | 34 | const segmentCount = prefix ? 2 : 3 |
10 | 35 | prefix = prefix ? `${prefix}-` : '' |
11 | 36 |
|
12 | | - const segments = Array.from( |
13 | | - { length: segmentCount }, |
14 | | - () => randomBytes(segmentLength).toString('hex').slice(0, segmentLength) // 0-9a-f, might be good enough? |
15 | | - ) |
| 37 | + const profanityMatcher = new RegExpMatcher({ |
| 38 | + ...englishDataset.build(), |
| 39 | + ...englishRecommendedTransformers |
| 40 | + }) |
| 41 | + |
| 42 | + let referenceNumber |
| 43 | + |
| 44 | + do { |
| 45 | + const segments = Array.from({ length: segmentCount }, () => |
| 46 | + convertToDecAlpha([...randomBytes(segmentLength)]).slice( |
| 47 | + 0, |
| 48 | + segmentLength * 2 |
| 49 | + ) |
| 50 | + ) |
| 51 | + |
| 52 | + referenceNumber = `${prefix}${segments.join('-')}`.toUpperCase() |
| 53 | + } while (profanityMatcher.hasMatch(referenceNumber.replaceAll('-', ''))) |
16 | 54 |
|
17 | | - return `${prefix}${segments.join('-')}`.toUpperCase() |
| 55 | + return referenceNumber |
18 | 56 | } |
0 commit comments