Files
aliasvault/core/models/scripts/generate-password-defaults.cjs
2026-06-27 22:29:17 +02:00

137 lines
4.3 KiB
JavaScript

#!/usr/bin/env node
/**
* Generates password-generator defaults constants for all cross-platform clients.
*
* Input: core/models/src/defaults/PasswordGeneratorDefaults.ts
* Outputs:
* - core/rust/src/password_generator/defaults.rs (Rust)
* - apps/server/Databases/AliasClientDb/Models/PasswordGeneratorDefaults.cs (C#)
*
* Note: Typescript clients can import the constants directly from the distributed `@/utils/dist/core/models/vault`.
*/
const fs = require('fs');
const path = require('path');
const REPO_ROOT = path.join(__dirname, '../../..');
const TS_SOURCE = path.join(REPO_ROOT, 'core/models/src/defaults/PasswordGeneratorDefaults.ts');
const TS_SOURCE_REL = 'core/models/src/defaults/PasswordGeneratorDefaults.ts';
const RUST_OUTPUT = path.join(REPO_ROOT, 'core/rust/src/password_generator/defaults.rs');
const CS_OUTPUT = path.join(REPO_ROOT, 'apps/server/Databases/AliasClientDb/Models/PasswordGeneratorDefaults.cs');
/**
* Parse the `export const NAME = VALUE;` lines (each with its leading single-line
* JSDoc comment) out of the TypeScript source, preserving order.
*/
function parseConstants(source) {
const constants = [];
const lines = source.split('\n');
let pendingDoc = null;
for (const line of lines) {
const trimmed = line.trim();
// Single-line JSDoc comment: /** ... */
if (trimmed.startsWith('/**') && trimmed.endsWith('*/')) {
pendingDoc = trimmed.slice(3, -2).trim();
continue;
}
const constMatch = line.match(/^\s*export const ([A-Z0-9_]+)\s*=\s*(\d+)\s*;/);
if (constMatch) {
constants.push({
name: constMatch[1],
value: Number(constMatch[2]),
doc: pendingDoc || `${constMatch[1]}.`,
});
pendingDoc = null;
continue;
}
// Any other non-blank line resets a dangling doc comment.
if (line.trim() !== '') {
pendingDoc = null;
}
}
if (constants.length === 0) {
throw new Error(`No 'export const ... = <number>;' constants found in ${TS_SOURCE_REL}`);
}
return constants;
}
/** SCREAMING_SNAKE_CASE -> PascalCase (DEFAULT_PASSWORD_LENGTH -> DefaultPasswordLength). */
function toPascalCase(name) {
return name
.toLowerCase()
.split('_')
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
}
function generateRust(constants) {
const header = `//! Client-facing defaults for the password/passphrase generator.
//!
//! @generated — do NOT edit this file directly. It is generated from
//! ${TS_SOURCE_REL} by core/models/scripts/generate-password-defaults.cjs.
//! Edit the TypeScript source and run 'core/models/build.sh' to regenerate.
`;
const body = constants
.map((c) => `/// ${c.doc}\npub const ${c.name}: u32 = ${c.value};`)
.join('\n\n');
return `${header}\n${body}\n`;
}
function generateCSharp(constants) {
const header = `// <auto-generated />
// This file is auto-generated from ${TS_SOURCE_REL}
// Do not edit this file directly. Run 'core/models/build.sh' (or
// 'node core/models/scripts/generate-password-defaults.cjs') to regenerate.
#nullable enable
namespace AliasClientDb.Models;
/// <summary>
/// Password generator defaults shared across all AliasVault clients.
/// </summary>
public static class PasswordGeneratorDefaults
{`;
const body = constants
.map((c) => {
const csName = toPascalCase(c.name);
return ` /// <summary>\n /// ${c.doc}\n /// </summary>\n public const int ${csName} = ${c.value};`;
})
.join('\n\n');
return `${header}\n${body}\n}\n`;
}
function writeIfChanged(filePath, contents) {
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
if (existing === contents) {
console.log(` ✓ up to date: ${path.relative(REPO_ROOT, filePath)}`);
return;
}
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, contents);
console.log(` ✎ wrote: ${path.relative(REPO_ROOT, filePath)}`);
}
function main() {
const source = fs.readFileSync(TS_SOURCE, 'utf8');
const constants = parseConstants(source);
console.log(`Generating password-generator defaults from ${TS_SOURCE_REL}:`);
console.log(` ${constants.map((c) => `${c.name}=${c.value}`).join(', ')}`);
writeIfChanged(RUST_OUTPUT, generateRust(constants));
writeIfChanged(CS_OUTPUT, generateCSharp(constants));
}
main();