use @paralleldrive/cuid2

This commit is contained in:
Anas Syed 2026-01-22 20:17:10 +00:00
parent 322feb8b67
commit a0401bc38c
3 changed files with 42 additions and 23 deletions

View File

@ -9,6 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@paralleldrive/cuid2": "^3.0.6",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-checkbox": "^1.3.2",
@ -1485,6 +1486,17 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@noble/hashes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz",
"integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==",
"engines": {
"node": ">= 20.19.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1523,6 +1535,19 @@
"node": ">= 8"
}
},
"node_modules/@paralleldrive/cuid2": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-3.0.6.tgz",
"integrity": "sha512-ujtxTTvr4fwPrzuQT7o6VLKs5BzdWetR9+/zRQ0SyK9hVIwZQllEccxgcHYXN6I3Z429y1yg3F6+uiVxMDPrLQ==",
"dependencies": {
"@noble/hashes": "^2.0.1",
"bignumber.js": "^9.3.1",
"error-causes": "^3.0.2"
},
"bin": {
"cuid2": "bin/cuid2.js"
}
},
"node_modules/@petamoriken/float16": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.3.tgz",
@ -3780,6 +3805,14 @@
"prebuild-install": "^7.1.1"
}
},
"node_modules/bignumber.js": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
"integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@ -4708,6 +4741,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/error-causes": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/error-causes/-/error-causes-3.0.2.tgz",
"integrity": "sha512-i0B8zq1dHL6mM85FGoxaJnVtx6LD5nL2v0hlpGdntg5FOSyzQ46c9lmz5qx0xRS2+PWHGOHcYxGIBC5Le2dRMw=="
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",

View File

@ -21,6 +21,7 @@
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@paralleldrive/cuid2": "^3.0.6",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-checkbox": "^1.3.2",

View File

@ -8,7 +8,7 @@ import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { readFile, writeFile, mkdir, access, unlink } from 'fs/promises';
import { existsSync } from 'fs';
import crypto from 'node:crypto';
import { createId } from '@paralleldrive/cuid2';
import { getSetting } from '../repositories/settings.js';
import { pickProjectIdsForJob } from './projectSelection.js';
@ -20,26 +20,6 @@ import { resumeDataSchema } from '../../shared/rxresume-schema.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* Generate a CUID2-compatible ID for RXResume.
* CUID2 format: starts with a letter, lowercase alphanumeric, ~24 chars
*/
function generateCuid2(): string {
const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789';
const letters = 'abcdefghijklmnopqrstuvwxyz';
const bytes = crypto.randomBytes(24);
// First char must be a letter
let result = letters[bytes[0] % letters.length];
// Rest can be alphanumeric
for (let i = 1; i < 24; i++) {
result += alphabet[bytes[i] % alphabet.length];
}
return result;
}
// Paths - can be overridden via env for Docker
const RESUME_GEN_DIR = process.env.RESUME_GEN_DIR || join(__dirname, '../../../../resume-generator');
const OUTPUT_DIR = join(getDataDir(), 'pdfs');
@ -92,7 +72,7 @@ export async function generatePdf(
if (baseResume.sections?.skills?.items && Array.isArray(baseResume.sections.skills.items)) {
baseResume.sections.skills.items = baseResume.sections.skills.items.map((skill: any) => ({
...skill,
id: skill.id || generateCuid2(),
id: skill.id || createId(),
visible: skill.visible ?? true,
// Zod schema requires string, default to empty string if missing
description: skill.description ?? '',
@ -135,7 +115,7 @@ export async function generatePdf(
const existing = existingSkills.find((s: any) => s.name === newSkill.name);
return {
id: newSkill.id || existing?.id || generateCuid2(),
id: newSkill.id || existing?.id || createId(),
visible: newSkill.visible !== undefined ? newSkill.visible : (existing?.visible ?? true),
name: newSkill.name || existing?.name || '',
description: newSkill.description !== undefined ? newSkill.description : (existing?.description || ''),