import { ulid } from 'ulid';
/**
* Character sets for base conversion
* BASE85 provides optimal compression while avoiding problematic serialization characters
* Removed: \ > [ ] | ^ < (7 most dangerous characters from BASE91)
* Added: ` (backtick) as safe replacement to maintain BASE85
*/
const BASE85_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+,-./:;=?@_{}~`';
/**
* Generates ultra-compact time-sortable identifier for Neo4j
* Uses BASE85 encoding to achieve ~31% shorter IDs than standard ULID
* Maintains time-sortable property crucial for Neo4j performance
*
* @returns {string} 18-character identifier (vs 26 chars for standard ULID)
*/
export function generateCompactId(): string {
const ulidResult = ulid();
const base85Result = encodeBase85FromUlid(ulidResult);
return base85Result;
}
/**
* Converts ULID directly to BASE85 for optimal compression
* More efficient than going through Buffer as in original implementation
*/
function encodeBase85FromUlid(ulid: string): string {
// ULID uses Crockford Base32
const base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
// Convert ULID to BigInt for precise arithmetic
let value = 0n;
for (const char of ulid) {
const index = base32Chars.indexOf(char);
if (index === -1) throw new Error(`Invalid ULID character: ${char}`);
value = value * 32n + BigInt(index);
}
// Convert to BASE85
if (value === 0n) return BASE85_CHARS[0];
let result = '';
while (value > 0n) {
result = BASE85_CHARS[Number(value % 85n)] + result;
value = value / 85n;
}
// Result is typically 20 chars, we can safely truncate to 18 for Neo4j
// This still provides sufficient entropy (85^18 ≈ 2.7 * 10^34 combinations)
return result.substring(0, 18);
}