From 260aec34ce67ccb9b70199b5cd797165328ebcef Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Wed, 4 Jun 2025 21:58:45 +0200 Subject: [PATCH] Add shared libraries to AliasVault.Client (#896) --- .../Services/CredentialService.cs | 14 +- .../Services/JsInteropService.cs | 81 +- .../js/shared/identity-generator/README.md | 9 + .../js/shared/identity-generator/index.d.ts | 142 ++ .../js/shared/identity-generator/index.js | 1737 +++++++++++++++++ .../js/shared/identity-generator/index.mjs | 1704 ++++++++++++++++ .../js/shared/password-generator/README.md | 9 + .../js/shared/password-generator/index.d.ts | 119 ++ .../js/shared/password-generator/index.js | 244 +++ .../js/shared/password-generator/index.mjs | 216 ++ 10 files changed, 4266 insertions(+), 9 deletions(-) create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/README.md create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.d.ts create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.js create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.mjs create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/README.md create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.d.ts create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.js create mode 100644 apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.mjs diff --git a/apps/server/AliasVault.Client/Services/CredentialService.cs b/apps/server/AliasVault.Client/Services/CredentialService.cs index 435b414cf..a39923538 100644 --- a/apps/server/AliasVault.Client/Services/CredentialService.cs +++ b/apps/server/AliasVault.Client/Services/CredentialService.cs @@ -14,15 +14,13 @@ using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; using AliasClientDb; -using AliasVault.Generators.Identity.Implementations.Factories; -using AliasVault.Generators.Identity.Models; using AliasVault.Shared.Models.WebApi.Favicon; using Microsoft.EntityFrameworkCore; /// /// Service class for credential operations. /// -public sealed class CredentialService(HttpClient httpClient, DbService dbService, Config config) +public sealed class CredentialService(HttpClient httpClient, DbService dbService, Config config, JsInteropService jsInteropService) { /// /// The default service URL used as placeholder in forms. When this value is set, the URL field is considered empty @@ -73,15 +71,15 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService do { - // Generate a random identity using the IIdentityGenerator implementation - var identity = await IdentityGeneratorFactory.CreateIdentityGenerator(dbService.Settings.DefaultIdentityLanguage).GenerateRandomIdentityAsync(); + // Generate a random identity using the TypeScript library + var identity = await jsInteropService.GenerateRandomIdentityAsync(dbService.Settings.DefaultIdentityLanguage); // Generate random values for the Identity properties credential.Username = identity.NickName; credential.Alias.FirstName = identity.FirstName; credential.Alias.LastName = identity.LastName; credential.Alias.NickName = identity.NickName; - credential.Alias.Gender = identity.Gender == Gender.Male ? "Male" : "Female"; + credential.Alias.Gender = identity.Gender; credential.Alias.BirthDate = identity.BirthDate; // Set the email @@ -105,9 +103,9 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService } while (isEmailTaken && attempts < MaxAttempts); - // Generate password + // Generate password using the TypeScript library var passwordSettings = dbService.Settings.PasswordSettings; - credential.Passwords.First().Value = GenerateRandomPassword(passwordSettings); + credential.Passwords.First().Value = await jsInteropService.GenerateRandomPasswordAsync(passwordSettings); return credential; } diff --git a/apps/server/AliasVault.Client/Services/JsInteropService.cs b/apps/server/AliasVault.Client/Services/JsInteropService.cs index 3deadb579..2bbecfeed 100644 --- a/apps/server/AliasVault.Client/Services/JsInteropService.cs +++ b/apps/server/AliasVault.Client/Services/JsInteropService.cs @@ -9,7 +9,6 @@ namespace AliasVault.Client.Services; using System.Security.Cryptography; using System.Text.Json; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.JSInterop; /// @@ -18,6 +17,19 @@ using Microsoft.JSInterop; /// IJSRuntime. public sealed class JsInteropService(IJSRuntime jsRuntime) { + private IJSObjectReference? _identityGeneratorModule; + private IJSObjectReference? _passwordGeneratorModule; + + /// + /// Initialize the identity generator module. + /// + /// A task representing the asynchronous operation. + public async Task InitializeAsync() + { + _identityGeneratorModule = await jsRuntime.InvokeAsync("import", "./js/shared/identity-generator/index.mjs"); + _passwordGeneratorModule = await jsRuntime.InvokeAsync("import", "./js/shared/password-generator/index.mjs"); + } + /// /// Symmetrically encrypts a string using the provided encryption key. /// @@ -275,6 +287,73 @@ public sealed class JsInteropService(IJSRuntime jsRuntime) return await jsRuntime.InvokeAsync("cryptoInterop.decryptBytes", base64Ciphertext, encryptionKey); } + /// + /// Generates a random identity using the specified language. + /// + /// The language to use for generating the identity (e.g. "en", "nl"). + /// A tuple containing the generated identity information. + public async Task<(string FirstName, string LastName, string NickName, string EmailPrefix, string Gender, DateTime BirthDate)> GenerateRandomIdentityAsync(string language) + { + try + { + if (_identityGeneratorModule == null) + { + await InitializeAsync(); + if (_identityGeneratorModule == null) + { + throw new InvalidOperationException("Failed to initialize identity generator module"); + } + } + + var generatorInstance = await _identityGeneratorModule.InvokeAsync("createGenerator", language); + var result = await generatorInstance.InvokeAsync("generateRandomIdentity"); + + return ( + result.GetProperty("firstName").GetString()!, + result.GetProperty("lastName").GetString()!, + result.GetProperty("nickName").GetString()!, + result.GetProperty("emailPrefix").GetString()!, + result.GetProperty("gender").GetString()!, + result.GetProperty("birthDate").GetDateTime()); + } + catch (JSException ex) + { + await Console.Error.WriteLineAsync($"JavaScript error generating identity: {ex.Message}"); + throw new InvalidOperationException("Failed to generate random identity", ex); + } + } + + /// + /// Generates a random password using the specified settings. + /// + /// The password settings to use. + /// The generated password. + public async Task GenerateRandomPasswordAsync(PasswordSettings settings) + { + try + { + if (_passwordGeneratorModule == null) + { + await InitializeAsync(); + if (_passwordGeneratorModule == null) + { + throw new InvalidOperationException("Failed to initialize password generator module"); + } + } + + var generatorInstance = await _passwordGeneratorModule.InvokeAsync("createPasswordGenerator", settings); + + var result = await generatorInstance.InvokeAsync("generateRandomPassword"); + + return result; + } + catch (JSException ex) + { + await Console.Error.WriteLineAsync($"JavaScript error generating password: {ex.Message}"); + throw new InvalidOperationException("Failed to generate random password", ex); + } + } + /// /// Represents the result of a WebAuthn get credential operation. /// diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/README.md b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/README.md new file mode 100644 index 000000000..b46a10238 --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/README.md @@ -0,0 +1,9 @@ +# ⚠️ Auto-Generated Files + +This folder contains the output of the shared `identity-generator` module from the `/shared` directory in the AliasVault project. + +**Do not edit any of these files manually.** + +To make changes: +1. Update the source files in `packages/identity-generator/src` +2. Run the `build-and-distribute.sh` script at the root of the project to regenerate the outputs and copy them here. diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.d.ts b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.d.ts new file mode 100644 index 000000000..f3d21e979 --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.d.ts @@ -0,0 +1,142 @@ +declare enum Gender { + Male = "Male", + Female = "Female", + Other = "Other" +} + +/** + * Identity. + */ +type Identity = { + firstName: string; + lastName: string; + gender: Gender; + birthDate: Date; + emailPrefix: string; + nickName: string; +}; + +/** + * Generate a username or email prefix. + */ +declare class UsernameEmailGenerator { + private static readonly MIN_LENGTH; + private static readonly MAX_LENGTH; + private readonly symbols; + /** + * Generate a username based on an identity. + */ + generateUsername(identity: Identity): string; + /** + * Generate an email prefix based on an identity. + */ + generateEmailPrefix(identity: Identity): string; + /** + * Sanitize an email prefix. + */ + private sanitizeEmailPrefix; + /** + * Get a random symbol. + */ + private getRandomSymbol; + /** + * Generate a random string. + */ + private generateRandomString; + /** + * Generate a secure random integer between 0 (inclusive) and max (exclusive) + */ + private getSecureRandom; +} + +interface IIdentityGenerator { + generateRandomIdentity(): Identity; +} + +/** + * Base identity generator. + */ +declare abstract class BaseIdentityGenerator implements IIdentityGenerator { + protected firstNamesMale: string[]; + protected firstNamesFemale: string[]; + protected lastNames: string[]; + private readonly random; + /** + * Constructor. + */ + constructor(); + protected abstract getFirstNamesMaleJson(): string[]; + protected abstract getFirstNamesFemaleJson(): string[]; + protected abstract getLastNamesJson(): string[]; + /** + * Generate a random date of birth. + */ + protected generateRandomDateOfBirth(): Date; + /** + * Generate a random identity. + */ + generateRandomIdentity(): Identity; +} + +/** + * Identity generator for English language using English word dictionaries. + */ +declare class IdentityGeneratorEn extends BaseIdentityGenerator { + /** + * Get the male first names. + */ + protected getFirstNamesMaleJson(): string[]; + /** + * Get the female first names. + */ + protected getFirstNamesFemaleJson(): string[]; + /** + * Get the last names. + */ + protected getLastNamesJson(): string[]; +} + +/** + * Identity generator for Dutch language using Dutch word dictionaries. + */ +declare class IdentityGeneratorNl extends BaseIdentityGenerator { + /** + * Get the male first names. + */ + protected getFirstNamesMaleJson(): string[]; + /** + * Get the female first names. + */ + protected getFirstNamesFemaleJson(): string[]; + /** + * Get the last names. + */ + protected getLastNamesJson(): string[]; +} + +/** + * Helper utilities for identity generation that can be used by multiple client applications. + */ +declare class IdentityHelperUtils { + /** + * Normalize a birth date for display. + */ + static normalizeBirthDateForDisplay(birthDate: string | undefined): string; + /** + * Normalize a birth date for database. + */ + static normalizeBirthDateForDb(input: string | undefined): string; + /** + * Check if a birth date is valid. + */ + static isValidBirthDate(input: string | undefined): boolean; +} + +/** + * Creates a new identity generator based on the language. + * @param language - The language to use for generating the identity (e.g. "en", "nl"). + * @returns A new identity generator instance. + */ +declare const createGenerator: (language: string) => IIdentityGenerator; + +export { BaseIdentityGenerator, Gender, type Identity, IdentityGeneratorEn, IdentityGeneratorNl, IdentityHelperUtils, UsernameEmailGenerator, createGenerator }; diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.js b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.js new file mode 100644 index 000000000..d94ec7124 --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.js @@ -0,0 +1,1737 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/index.ts +var index_exports = {}; +__export(index_exports, { + BaseIdentityGenerator: () => BaseIdentityGenerator, + Gender: () => Gender, + IdentityGeneratorEn: () => IdentityGeneratorEn, + IdentityGeneratorNl: () => IdentityGeneratorNl, + IdentityHelperUtils: () => IdentityHelperUtils, + UsernameEmailGenerator: () => UsernameEmailGenerator, + createGenerator: () => createGenerator +}); +module.exports = __toCommonJS(index_exports); + +// src/utils/UsernameEmailGenerator.ts +var _UsernameEmailGenerator = class _UsernameEmailGenerator { + constructor() { + this.symbols = [".", "-"]; + } + /** + * Generate a username based on an identity. + */ + generateUsername(identity) { + let username = this.generateEmailPrefix(identity); + username = username.replace(/[^a-zA-Z0-9]/g, ""); + if (username.length < _UsernameEmailGenerator.MIN_LENGTH) { + username += this.generateRandomString(_UsernameEmailGenerator.MIN_LENGTH - username.length); + } else if (username.length > _UsernameEmailGenerator.MAX_LENGTH) { + username = username.substring(0, _UsernameEmailGenerator.MAX_LENGTH); + } + return username; + } + /** + * Generate an email prefix based on an identity. + */ + generateEmailPrefix(identity) { + const parts = []; + switch (this.getSecureRandom(4)) { + case 0: + parts.push(identity.firstName.substring(0, 1).toLowerCase() + identity.lastName.toLowerCase()); + break; + case 1: + parts.push((identity.firstName + identity.lastName).toLowerCase()); + break; + case 2: + parts.push(identity.firstName.toLowerCase() + identity.lastName.substring(0, 1).toLowerCase()); + break; + case 3: + parts.push(identity.firstName.substring(0, Math.min(3, identity.firstName.length)).toLowerCase() + identity.lastName.toLowerCase()); + break; + } + if (this.getSecureRandom(3) !== 0) { + switch (this.getSecureRandom(2)) { + case 0: + parts.push(identity.birthDate.getFullYear().toString().substring(2)); + break; + case 1: + parts.push(identity.birthDate.getFullYear().toString()); + break; + } + } else if (this.getSecureRandom(2) === 0) { + parts.push((this.getSecureRandom(990) + 10).toString()); + } + let emailPrefix = parts.join(this.getRandomSymbol()); + if (this.getSecureRandom(2) === 0) { + const position = this.getSecureRandom(emailPrefix.length); + emailPrefix = emailPrefix.slice(0, position) + this.getRandomSymbol() + emailPrefix.slice(position); + } + emailPrefix = this.sanitizeEmailPrefix(emailPrefix); + if (emailPrefix.length < _UsernameEmailGenerator.MIN_LENGTH) { + emailPrefix += this.generateRandomString(_UsernameEmailGenerator.MIN_LENGTH - emailPrefix.length); + } else if (emailPrefix.length > _UsernameEmailGenerator.MAX_LENGTH) { + emailPrefix = emailPrefix.substring(0, _UsernameEmailGenerator.MAX_LENGTH); + } + return emailPrefix; + } + /** + * Sanitize an email prefix. + */ + sanitizeEmailPrefix(input) { + let sanitized = input.replace(/[^a-zA-Z0-9._-]/g, ""); + sanitized = sanitized.replace(/[-_.]{2,}/g, (match) => match[0]); + sanitized = sanitized.replace(/^[-._]+/, ""); + sanitized = sanitized.replace(/[-._]*$/, ""); + return sanitized; + } + /** + * Get a random symbol. + */ + getRandomSymbol() { + return this.getSecureRandom(3) === 0 ? this.symbols[this.getSecureRandom(this.symbols.length)] : ""; + } + /** + * Generate a random string. + */ + generateRandomString(length) { + const chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + return Array.from({ length }, () => chars.charAt(this.getSecureRandom(chars.length))).join(""); + } + /** + * Generate a secure random integer between 0 (inclusive) and max (exclusive) + */ + getSecureRandom(max) { + const array = new Uint32Array(1); + crypto.getRandomValues(array); + return array[0] % max; + } +}; +_UsernameEmailGenerator.MIN_LENGTH = 6; +_UsernameEmailGenerator.MAX_LENGTH = 20; +var UsernameEmailGenerator = _UsernameEmailGenerator; + +// src/types/Gender.ts +var Gender = /* @__PURE__ */ ((Gender2) => { + Gender2["Male"] = "Male"; + Gender2["Female"] = "Female"; + Gender2["Other"] = "Other"; + return Gender2; +})(Gender || {}); + +// src/implementations/base/BaseIdentityGenerator.ts +var BaseIdentityGenerator = class { + /** + * Constructor. + */ + constructor() { + this.firstNamesMale = []; + this.firstNamesFemale = []; + this.lastNames = []; + this.random = Math.random; + this.firstNamesMale = this.getFirstNamesMaleJson(); + this.firstNamesFemale = this.getFirstNamesFemaleJson(); + this.lastNames = this.getLastNamesJson(); + } + /** + * Generate a random date of birth. + */ + generateRandomDateOfBirth() { + const today = /* @__PURE__ */ new Date(); + const minAge = 21; + const maxAge = 65; + const minDate = new Date(today.getFullYear() - maxAge, today.getMonth(), today.getDate()); + const maxDate = new Date(today.getFullYear() - minAge, today.getMonth(), today.getDate()); + const timestamp = minDate.getTime() + this.random() * (maxDate.getTime() - minDate.getTime()); + return new Date(timestamp); + } + /** + * Generate a random identity. + */ + generateRandomIdentity() { + const identity = { + firstName: "", + lastName: "", + gender: "Male" /* Male */, + birthDate: /* @__PURE__ */ new Date(), + emailPrefix: "", + nickName: "" + }; + if (this.random() < 0.5) { + identity.firstName = this.firstNamesMale[Math.floor(this.random() * this.firstNamesMale.length)]; + identity.gender = "Male" /* Male */; + } else { + identity.firstName = this.firstNamesFemale[Math.floor(this.random() * this.firstNamesFemale.length)]; + identity.gender = "Female" /* Female */; + } + identity.lastName = this.lastNames[Math.floor(this.random() * this.lastNames.length)]; + identity.birthDate = this.generateRandomDateOfBirth(); + const generator = new UsernameEmailGenerator(); + identity.emailPrefix = generator.generateEmailPrefix(identity); + identity.nickName = generator.generateUsername(identity); + return identity; + } +}; + +// src/dictionaries/en/firstnames_male.ts +var firstnames_male_default = [ + "Michael", + "Christopher", + "Matthew", + "Joshua", + "Daniel", + "David", + "Andrew", + "Joseph", + "James", + "John", + "Robert", + "William", + "Ryan", + "Jason", + "Nicholas", + "Jonathan", + "Jacob", + "Brandon", + "Tyler", + "Zachary", + "Kevin", + "Justin", + "Benjamin", + "Anthony", + "Samuel", + "Thomas", + "Alexander", + "Ethan", + "Noah", + "Dylan", + "Nathan", + "Christian", + "Austin", + "Adam", + "Caleb", + "Cody", + "Jordan", + "Logan", + "Aaron", + "Kyle", + "Jose", + "Brian", + "Gabriel", + "Timothy", + "Luke", + "Jared", + "Connor", + "Sean", + "Evan", + "Isaac", + "Jack", + "Cameron", + "Hunter", + "Jackson", + "Charles", + "Devin", + "Stephen", + "Patrick", + "Steven", + "Elijah", + "Scott", + "Mark", + "Jeffrey", + "Corey", + "Juan", + "Luis", + "Derek", + "Chase", + "Travis", + "Alex", + "Spencer", + "Ian", + "Trevor", + "Bryan", + "Tanner", + "Marcus", + "Jeremy", + "Eric", + "Jaden", + "Garrett", + "Isaiah", + "Dustin", + "Jesse", + "Seth", + "Blake", + "Nathaniel", + "Mason", + "Liam", + "Paul", + "Carlos", + "Mitchell", + "Parker", + "Lucas", + "Richard", + "Cole", + "Adrian", + "Colin", + "Bradley", + "Jesus", + "Peter", + "Kenneth", + "Joel", + "Victor", + "Bryce", + "Casey", + "Vincent", + "Edward", + "Henry", + "Dominic", + "Riley", + "Shane", + "Dalton", + "Grant", + "Shawn", + "Braden", + "Caden", + "Max", + "Hayden", + "Owen", + "Brett", + "Trevor", + "Philip", + "Brendan", + "Wesley", + "Aidan", + "Brady", + "Colton", + "Tristan", + "George", + "Gavin", + "Dawson", + "Miguel", + "Antonio", + "Nolan", + "Dakota", + "Jace", + "Collin", + "Preston", + "Levi", + "Alan", + "Jorge", + "Carson", + "Felix", + "Oliver", + "Theodore", + "Harrison", + "Maxwell", + "Sebastian", + "Xavier", + "Dominick", + "Lincoln", + "Elliott", + "Walter", + "Simon", + "Dean", + "Hugo", + "Malcolm", + "Leon", + "Oscar", + "Calvin", + "Raymond", + "Edgar", + "Franklin", + "Arthur", + "Lawrence", + "Dennis", + "Russell", + "Douglas", + "Leonard", + "Gregory", + "Harold", + "Frederick", + "Martin", + "Curtis", + "Stanley", + "Gilbert", + "Harvey", + "Francis", + "Eugene", + "Ralph", + "Roy", + "Albert", + "Bruce", + "Ronald", + "Keith", + "Craig", + "Roger", + "Randy", + "Gary", + "Dennis", + "Edwin", + "Don", + "Glen", + "Gordon", + "Howard", + "Earl", + "Leo", + "Lloyd", + "Milton", + "Norman", + "Roland", + "Vernon", + "Warren", + "Alfred", + "Bernard", + "Chester", + "Clarence", + "Clifford", + "Clyde", + "Dale", + "Dan", + "Darrell", + "Floyd", + "Herman", + "Jerome", + "Maurice", + "Neil", + "Ray", + "Rodney", + "Roland", + "Stuart", + "Wallace", + "Wayne", + "Wendell", + "Barry", + "Cecil", + "Claude", + "Daryl", + "Edmund", + "Everett", + "Ferdinand", + "Forrest", + "Gerald", + "Hugh", + "Irving", + "Leslie", + "Marvin", + "Morris", + "Nelson", + "Perry", + "Phillip", + "Roderick", + "Ross", + "Terrence", + "Wade", + "Winston", + "Zachariah" +]; + +// src/dictionaries/en/firstnames_female.ts +var firstnames_female_default = [ + "Emily", + "Emma", + "Olivia", + "Ava", + "Sophia", + "Isabella", + "Mia", + "Charlotte", + "Amelia", + "Harper", + "Evelyn", + "Abigail", + "Elizabeth", + "Sofia", + "Avery", + "Ella", + "Madison", + "Scarlett", + "Victoria", + "Aria", + "Grace", + "Chloe", + "Camila", + "Penelope", + "Riley", + "Layla", + "Zoey", + "Nora", + "Lily", + "Eleanor", + "Hannah", + "Lillian", + "Addison", + "Aubrey", + "Ellie", + "Stella", + "Natalie", + "Zoe", + "Leah", + "Hazel", + "Violet", + "Aurora", + "Savannah", + "Audrey", + "Brooklyn", + "Bella", + "Claire", + "Skylar", + "Lucy", + "Paisley", + "Everly", + "Anna", + "Caroline", + "Nova", + "Genesis", + "Emilia", + "Kennedy", + "Samantha", + "Maya", + "Willow", + "Kinsley", + "Naomi", + "Aaliyah", + "Elena", + "Sarah", + "Ariana", + "Allison", + "Gabriella", + "Alice", + "Madelyn", + "Cora", + "Ruby", + "Eva", + "Serenity", + "Autumn", + "Adeline", + "Hailey", + "Gianna", + "Valentina", + "Isla", + "Eliana", + "Quinn", + "Nevaeh", + "Ivy", + "Sadie", + "Piper", + "Lydia", + "Alexa", + "Josephine", + "Emery", + "Julia", + "Delilah", + "Arianna", + "Vivian", + "Kaylee", + "Sophie", + "Brielle", + "Madeline", + "Peyton", + "Rylee", + "Clara", + "Hadley", + "Melanie", + "Mackenzie", + "Reagan", + "Adalyn", + "Liliana", + "Aubree", + "Jade", + "Katherine", + "Isabelle", + "Natalia", + "Raelynn", + "Maria", + "Athena", + "Ximena", + "Arya", + "Leilani", + "Taylor", + "Faith", + "Rose", + "Kylie", + "Alexandra", + "Mary", + "Margaret", + "Lyla", + "Ashley", + "Amaya", + "Eliza", + "Brianna", + "Bailey", + "Andrea", + "Khloe", + "Jasmine", + "Melody", + "Iris", + "Isabel", + "Norah", + "Annabelle", + "Valeria", + "Emerson", + "Adalynn", + "Ryleigh", + "Eden", + "Emersyn", + "Anastasia", + "Kayla", + "Alyssa", + "Anna", + "Juliana", + "Charlie", + "Lucia", + "Stella", + "Adriana", + "Beatrice", + "Bianca", + "Calliope", + "Carmen", + "Celeste", + "Dakota", + "Diana", + "Esther", + "Florence", + "Francesca", + "Georgia", + "Harlow", + "Haven", + "Holly", + "Hope", + "India", + "Indie", + "Iris", + "Juniper", + "Kaia", + "Keira", + "Lara", + "Laura", + "Laurel", + "Luna", + "Magnolia", + "Maeve", + "Marina", + "Marlowe", + "Nina", + "Noelle", + "Octavia", + "Olive", + "Ophelia", + "Phoenix", + "Poppy", + "Primrose", + "Ramona", + "River", + "Rosalie", + "Rosemary", + "Sage", + "Salem", + "Selena", + "Sienna", + "Summer", + "Sylvie", + "Thea", + "Tessa", + "Wren", + "Winter", + "Willa", + "Ada", + "Aspen", + "Blair", + "Brynn", + "Cassidy", + "Cecilia", + "Daisy", + "Dawn", + "Daphne", + "Ember", + "Fiona", + "Flora", + "Freya", + "Gemma", + "Giselle", + "Harmony", + "Heidi", + "Imogen", + "Indie", + "Jessie", + "June", + "Kaia", + "Lena", + "Lola", + "Mabel", + "Maisie", + "Margot", + "Matilda", + "Mira", + "Morgan", + "Nell", + "Nadia", + "Odette", + "Opal", + "Pearl", + "Phoebe", + "Raven", + "Reese", + "Robin", + "Rowan", + "Ruth", + "Sabrina", + "Sasha", + "Sierra", + "Skye", + "Sloane", + "Talia", + "Thora", + "Vera", + "Willa", + "Winnie", + "Yara", + "Zara" +]; + +// src/dictionaries/en/lastnames.ts +var lastnames_default = [ + "Smith", + "Johnson", + "Williams", + "Brown", + "Jones", + "Garcia", + "Miller", + "Davis", + "Rodriguez", + "Martinez", + "Hernandez", + "Lopez", + "Gonzalez", + "Wilson", + "Anderson", + "Thomas", + "Taylor", + "Moore", + "Jackson", + "Martin", + "Lee", + "Perez", + "Thompson", + "White", + "Harris", + "Sanchez", + "Clark", + "Ramirez", + "Lewis", + "Robinson", + "Walker", + "Young", + "Allen", + "King", + "Wright", + "Scott", + "Torres", + "Nguyen", + "Hill", + "Flores", + "Green", + "Adams", + "Nelson", + "Baker", + "Hall", + "Rivera", + "Campbell", + "Mitchell", + "Carter", + "Roberts", + "Gomez", + "Phillips", + "Evans", + "Turner", + "Diaz", + "Parker", + "Cruz", + "Edwards", + "Collins", + "Reyes", + "Stewart", + "Morris", + "Morales", + "Murphy", + "Cook", + "Rogers", + "Gutierrez", + "Ortiz", + "Morgan", + "Cooper", + "Peterson", + "Bailey", + "Reed", + "Kelly", + "Howard", + "Ramos", + "Kim", + "Cox", + "Ward", + "Richardson", + "Watson", + "Brooks", + "Chavez", + "Wood", + "James", + "Bennett", + "Gray", + "Mendoza", + "Ruiz", + "Hughes", + "Price", + "Alvarez", + "Castillo", + "Sanders", + "Patel", + "Myers", + "Long", + "Ross", + "Foster", + "Jimenez", + "Powell", + "Jenkins", + "Perry", + "Russell", + "Sullivan", + "Bell", + "Coleman", + "Butler", + "Henderson", + "Barnes", + "Gonzales", + "Fisher", + "Vasquez", + "Simmons", + "Romero", + "Jordan", + "Patterson", + "Alexander", + "Hamilton", + "Graham", + "Reynolds", + "Griffin", + "Wallace", + "Moreno", + "West", + "Cole", + "Hayes", + "Bryant", + "Herrera", + "Gibson", + "Ellis", + "Tran", + "Medina", + "Aguilar", + "Stevens", + "Murray", + "Ford", + "Castro", + "Marshall", + "Owens", + "Harrison", + "Fernandez", + "McDonald", + "Woods", + "Washington", + "Kennedy", + "Wells", + "Vargas", + "Henry", + "Chen", + "Freeman", + "Webb", + "Tucker", + "Guzman", + "Burns", + "Crawford", + "Olson", + "Simpson", + "Porter", + "Hunter", + "Gordon", + "Mendez", + "Silva", + "Shaw", + "Snyder", + "Mason", + "Dixon", + "Blackwood", + "Shepherd", + "Frost", + "Hawkins", + "Pearson", + "Fleming", + "Dawson", + "Palmer", + "Nash", + "Barker", + "Thornton", + "Fitzgerald", + "Winters", + "Mckenzie", + "Chandler", + "Griffith", + "Cunningham", + "Doyle", + "Fletcher", + "Hicks", + "Walton", + "Briggs", + "Pearce", + "Nichols", + "Blake", + "Hodges", + "Benson", + "Marsh", + "Whitaker", + "Skinner", + "Robbins", + "Goodwin", + "Kirby", + "Savage", + "Hensley", + "Hancock", + "Pratt", + "Gallagher", + "Yates", + "Dennis", + "Swanson", + "Steele", + "Bauer", + "Holt", + "Barber", + "Schultz", + "Foley", + "Fowler", + "Wise", + "Malone", + "Cannon", + "Tate", + "Stark", + "Welch", + "Dyer", + "Booth", + "Payne", + "Shannon", + "Harmon", + "Woodward", + "Morse", + "Jacobson", + "Knowles", + "Blanchard", + "Dillon", + "Stokes", + "Buckley", + "Dickerson", + "Middleton", + "Sellers", + "Cobb", + "Stephenson", + "Roach", + "Moody", + "Beard", + "Mccarthy", + "Garner", + "Mcguire", + "Sloan", + "Ballard", + "Shields", + "Orr", + "Savage", + "Graves", + "Dempsey", + "Weeks", + "Mckay", + "Cooke", + "Riddle", + "Gates", + "Atkins", + "Farrell", + "Lowery", + "Huffman", + "Livingston", + "Davenport", + "Hendricks", + "Kerr", + "Pollard", + "Hoover", + "Wolfe", + "Bowman", + "Underwood", + "Frazier" +]; + +// src/implementations/IdentityGeneratorEn.ts +var IdentityGeneratorEn = class extends BaseIdentityGenerator { + /** + * Get the male first names. + */ + getFirstNamesMaleJson() { + return firstnames_male_default; + } + /** + * Get the female first names. + */ + getFirstNamesFemaleJson() { + return firstnames_female_default; + } + /** + * Get the last names. + */ + getLastNamesJson() { + return lastnames_default; + } +}; + +// src/dictionaries/nl/firstnames_male.ts +var firstnames_male_default2 = [ + "Daan", + "Luuk", + "Sem", + "Finn", + "Milan", + "Levi", + "Noah", + "Lucas", + "Jesse", + "Thijs", + "Jayden", + "Bram", + "Lars", + "Ruben", + "Thomas", + "Tim", + "Sam", + "Liam", + "Julian", + "Mees", + "Ties", + "Sven", + "Max", + "Gijs", + "David", + "Stijn", + "Jasper", + "Niels", + "Jens", + "Timo", + "Cas", + "Joep", + "Roan", + "Tom", + "Tygo", + "Teun", + "Siem", + "Mats", + "Thijmen", + "Rens", + "Niek", + "Tobias", + "Dex", + "Hugo", + "Robin", + "Nick", + "Floris", + "Pepijn", + "Boaz", + "Olivier", + "Luca", + "Jurre", + "Jelle", + "Guus", + "Koen", + "Bart", + "Olaf", + "Wessel", + "Dani\xEBl", + "Job", + "Sander", + "Tijmen", + "Kai", + "Quinten", + "Owen", + "Morris", + "Fedde", + "Joris", + "Jesper", + "Mick", + "Ryan", + "Milo", + "Stan", + "Benjamin", + "Melle", + "Jip", + "Dylan", + "Brent", + "Mick", + "Dean", + "Otis", + "Abel", + "Luc", + "Sepp", + "Vince", + "Rayan", + "Noud", + "Hidde", + "Fabian", + "Jort", + "Damian", + "Boris", + "Sil", + "Moos", + "Aiden", + "Sep", + "Mika", + "Mijs", + "Mika", + "Felix", + "Merlijn", + "Alexander", + "Aron", + "Arthur", + "Axel", + "Bas", + "Bastiaan", + "Berend", + "Bj\xF6rn", + "Casper", + "Cees", + "Chris", + "Christian", + "Christiaan", + "Colin", + "Cornelis", + "Dani", + "Dennis", + "Dirk", + "Dominic", + "Eduard", + "Eelco", + "Erik", + "Erwin", + "Ezra", + "Faas", + "Filip", + "Florian", + "Frank", + "Frederik", + "Freek", + "Gerard", + "Gerrit", + "Giel", + "Gijs", + "Glenn", + "Govert", + "Harm", + "Harold", + "Hendrik", + "Henrik", + "Huub", + "Ian", + "Ivo", + "Jacob", + "Jake", + "Jan", + "Jarno", + "Jason", + "Jeffrey", + "Jeremy", + "Jim", + "Jimmy", + "Johan", + "Johannes", + "Jonas", + "Jonathan", + "Jos", + "Joshua", + "Justin", + "Kay", + "Kevin", + "Kjeld", + "Klaas", + "Lennard", + "Lennart", + "Leon", + "Lex", + "Liam", + "Loek", + "Lorenzo", + "Louis", + "Lowie", + "Maarten", + "Magnus", + "Maikel", + "Marc", + "Marcel", + "Marco", + "Martijn", + "Mathias", + "Matthijs", + "Maurits", + "Menno", + "Michiel", + "Nathan", + "Nico", + "Oscar", + "Pascal", + "Patrick", + "Paul", + "Peter", + "Philip", + "Pieter", + "Pim", + "Quincy", + "Remco", + "Rick", + "Rik", + "Robert", + "Rogier", + "Rowan", + "Ruud", + "Simon", + "Stefan", + "Steven", + "Thom", + "Victor", + "Vincent", + "Willem", + "Wouter", + "Yannick" +]; + +// src/dictionaries/nl/firstnames_female.ts +var firstnames_female_default2 = [ + "Emma", + "Sophie", + "Julia", + "Mila", + "Tess", + "Sara", + "Anna", + "Noor", + "Lotte", + "Liv", + "Eva", + "Nora", + "Zo\xEB", + "Evi", + "Yara", + "Saar", + "Nina", + "Fenna", + "Lieke", + "Fleur", + "Isa", + "Roos", + "Lynn", + "Sofie", + "Sarah", + "Milou", + "Olivia", + "Maud", + "Lisa", + "Vera", + "Luna", + "Lina", + "Noa", + "Feline", + "Lo\xEFs", + "Lena", + "Floor", + "Charlotte", + "Esmee", + "Julie", + "Iris", + "Lara", + "Amber", + "Hailey", + "Mia", + "Lize", + "Isabelle", + "Cato", + "Fenne", + "Sanne", + "Norah", + "Sophia", + "Ella", + "Nova", + "Elin", + "Femke", + "Lizzy", + "Linde", + "Lauren", + "Rosalie", + "Lana", + "Emily", + "Elise", + "Esm\xE9e", + "Anne", + "Isabelle", + "Demi", + "Hannah", + "Liva", + "Suze", + "Fay", + "Isabel", + "Benthe", + "Evi", + "Amy", + "Jasmijn", + "Niene", + "Sterre", + "Fenna", + "Fiene", + "Liz", + "Ise", + "Mara", + "Nienke", + "Indy", + "Romy", + "Lola", + "Puck", + "Nora", + "Merel", + "Bente", + "Eline", + "Lily", + "Leah", + "Naomi", + "Mirthe", + "Valerie", + "Noor", + "Liva", + "Jade", + "Juul", + "Lise", + "Myrthe", + "Veerle", + "Aafke", + "Alicia", + "Amira", + "Aniek", + "Annabel", + "Annelies", + "Anouk", + "Astrid", + "Babette", + "Bianca", + "Britt", + "Carlijn", + "Chantal", + "Claire", + "Dagmar", + "Danique", + "Daphne", + "Denise", + "Dominique", + "Doris", + "Eefje", + "Elena", + "Eline", + "Elisa", + "Elisabeth", + "Ellen", + "Esther", + "Eveline", + "Fabienne", + "Felice", + "Fleur", + "Frederique", + "Gwen", + "Hanna", + "Heleen", + "Helena", + "Ilona", + "Imke", + "Inge", + "Irene", + "Iris", + "Janna", + "Janneke", + "Jasmine", + "Jennifer", + "Jessica", + "Joelle", + "Judith", + "Julia", + "Karin", + "Karlijn", + "Kim", + "Kirsten", + "Kyra", + "Laura", + "Lena", + "Lianne", + "Liesbeth", + "Linda", + "Lisanne", + "Lisette", + "Louise", + "Maartje", + "Manon", + "Margot", + "Marieke", + "Marijke", + "Marlies", + "Marloes", + "Marthe", + "Melissa", + "Michelle", + "Nadine", + "Natalie", + "Nicole", + "Nina", + "Noortje", + "Paulien", + "Petra", + "Rachel", + "Renee", + "Robin", + "Rosa", + "Roxanne", + "Sabine", + "Sandra", + "Saskia", + "Silke", + "Simone", + "Suzanne", + "Sylvie", + "Tamara", + "Tanja", + "Tara", + "Thea", + "Thirza", + "Tina", + "Tineke", + "Ursula", + "Victoria", + "Wendy", + "Wilma", + "Xandra", + "Yasmin", + "Yvette", + "Yvonne", + "Zara" +]; + +// src/dictionaries/nl/lastnames.ts +var lastnames_default2 = [ + "de Jong", + "Jansen", + "de Vries", + "van den Berg", + "van Dijk", + "Bakker", + "Janssen", + "Visser", + "Smit", + "Meijer", + "de Boer", + "Mulder", + "de Groot", + "Bos", + "Vos", + "Peters", + "Hendriks", + "van Leeuwen", + "Dekker", + "Brouwer", + "de Wit", + "Dijkstra", + "Smits", + "de Graaf", + "van der Meer", + "van der Linden", + "Kok", + "Jacobs", + "de Haan", + "Vermeulen", + "van den Heuvel", + "van der Veen", + "van den Broek", + "de Bruijn", + "de Bruin", + "van der Heijden", + "Schouten", + "van Beek", + "Willems", + "van Vliet", + "van de Ven", + "Hoekstra", + "Maas", + "Verhoeven", + "Koster", + "van Dam", + "van der Wal", + "Prins", + "Blom", + "Huisman", + "Peeters", + "Kuipers", + "van Veen", + "van Dongen", + "Veenstra", + "Kramer", + "van den Bosch", + "van der Meulen", + "Mol", + "Zwart", + "van der Laan", + "Martens", + "van de Pol", + "Postma", + "Tromp", + "Borst", + "Boon", + "van Doorn", + "Jonker", + "van der Velden", + "Willemsen", + "van Wijk", + "Groen", + "Gerritsen", + "Bosch", + "van Loon", + "van der Ploeg", + "de Ruiter", + "Molenaar", + "Boer", + "Klein", + "de Koning", + "van de Kamp", + "van der Horst", + "Verbeek", + "Vink", + "Goossens", + "Scholten", + "Hartman", + "van Dalen", + "van Elst", + "Brink", + "Boekel", + "van de Berg", + "Berends", + "van der Hoek", + "Kuiper", + "Kooijman", + "de Lange", + "van der Sluis", + "van Gelder", + "Martens", + "van Asselt", + "Timmermans", + "van Vliet", + "van Rijn", + "van Schaik", + "Bosman", + "Wolters", + "van Hout", + "Hermans", + "van Rooij", + "de Vos", + "van Donselaar", + "Evers", + "van den Brink", + "Verkerk", + "Groeneveld", + "van Duijn", + "Schuurman", + "Hoogendoorn", + "van Zanten", + "Koopman", + "Cornelissen", + "van Driel", + "Teunissen", + "Versteeg", + "van Deursen", + "Schipper", + "van Kempen", + "Bouwman", + "van der Valk", + "Nijhuis", + "van der Werf", + "van den Akker", + "Verhoef", + "Wessels", + "van der Poel", + "Driessen", + "van Oosten", + "Lambrechts", + "van der Vlist", + "Hoogeveen", + "van Gils", + "Rietveld", + "Barendrecht", + "van der Spek", + "Stam", + "van der Linde", + "Boersma", + "van Dijk", + "Schepers", + "van der Kolk", + "Roelofs", + "van der Velden", + "van den Burg", + "Westra", + "van der Steen", + "Pronk", + "van der Veer", + "Rozendaal", + "van den Bos", + "Konings", + "van der Wiel", + "Noordam", + "van der Laan", + "Schut", + "van der Vlugt", + "Witteveen", + "van der Zwan", + "Boogaard", + "van der Waal", + "Stolk", + "van der Windt", + "Rutten", + "van der Zanden", + "Spaans", + "van der Zwaan", + "Roos", + "van der Zijl", + "Schoenmaker", + "van Diepen", + "Romeijn", + "van Doesburg", + "Schippers", + "van Eck", + "Rijken", + "van Egmond", + "Schrama", + "van Eijk", + "Ruijter", + "van Engelen", + "Sanders", + "van Es", + "Schenk", + "van Essen", + "van Gaal", + "van Geenen", + "van Gent", + "van Gestel", + "van Gool", + "van Grinsven", + "van Gurp", + "van Haaften", + "van Haren", + "van Hattem", + "van Hees" +]; + +// src/implementations/IdentityGeneratorNl.ts +var IdentityGeneratorNl = class extends BaseIdentityGenerator { + /** + * Get the male first names. + */ + getFirstNamesMaleJson() { + return firstnames_male_default2; + } + /** + * Get the female first names. + */ + getFirstNamesFemaleJson() { + return firstnames_female_default2; + } + /** + * Get the last names. + */ + getLastNamesJson() { + return lastnames_default2; + } +}; + +// src/utils/IdentityHelperUtils.ts +var IdentityHelperUtils = class { + /** + * Normalize a birth date for display. + */ + static normalizeBirthDateForDisplay(birthDate) { + if (!birthDate || birthDate.startsWith("0001-01-01")) { + return ""; + } + return birthDate.split(/[T ]/)[0]; + } + /** + * Normalize a birth date for database. + */ + static normalizeBirthDateForDb(input) { + if (!input || input.trim() === "") { + return "0001-01-01T00:00:00.000Z"; + } + const trimmed = input.trim().replace(" ", "T"); + const match = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})[T ]?(\d{2}):?(\d{2}):?(\d{2})?$/); + if (match) { + const [_, y, m, d, h = "00", mi = "00", s = "00"] = match; + return `${y}-${m}-${d}T${h}:${mi}:${s}.000Z`; + } + const parsedDate = new Date(trimmed); + if (!isNaN(parsedDate.getTime())) { + return parsedDate.toISOString(); + } + return "0001-01-01T00:00:00.000Z"; + } + /** + * Check if a birth date is valid. + */ + static isValidBirthDate(input) { + if (!input || input.trim() === "") { + return false; + } + if (input.startsWith("0001-01-01")) { + return false; + } + const date = new Date(input); + if (isNaN(date.getTime())) { + return false; + } + const yearValid = date.getFullYear() > 1 && date.getFullYear() < 9999; + return yearValid; + } +}; + +// src/factories/IdentityGeneratorFactory.ts +var createGenerator = (language) => { + switch (language) { + case "en": + return new IdentityGeneratorEn(); + case "nl": + return new IdentityGeneratorNl(); + } + throw new Error(`Unsupported language: ${language}`); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + BaseIdentityGenerator, + Gender, + IdentityGeneratorEn, + IdentityGeneratorNl, + IdentityHelperUtils, + UsernameEmailGenerator, + createGenerator +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.mjs b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.mjs new file mode 100644 index 000000000..5ac1c0697 --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/identity-generator/index.mjs @@ -0,0 +1,1704 @@ +// src/utils/UsernameEmailGenerator.ts +var _UsernameEmailGenerator = class _UsernameEmailGenerator { + constructor() { + this.symbols = [".", "-"]; + } + /** + * Generate a username based on an identity. + */ + generateUsername(identity) { + let username = this.generateEmailPrefix(identity); + username = username.replace(/[^a-zA-Z0-9]/g, ""); + if (username.length < _UsernameEmailGenerator.MIN_LENGTH) { + username += this.generateRandomString(_UsernameEmailGenerator.MIN_LENGTH - username.length); + } else if (username.length > _UsernameEmailGenerator.MAX_LENGTH) { + username = username.substring(0, _UsernameEmailGenerator.MAX_LENGTH); + } + return username; + } + /** + * Generate an email prefix based on an identity. + */ + generateEmailPrefix(identity) { + const parts = []; + switch (this.getSecureRandom(4)) { + case 0: + parts.push(identity.firstName.substring(0, 1).toLowerCase() + identity.lastName.toLowerCase()); + break; + case 1: + parts.push((identity.firstName + identity.lastName).toLowerCase()); + break; + case 2: + parts.push(identity.firstName.toLowerCase() + identity.lastName.substring(0, 1).toLowerCase()); + break; + case 3: + parts.push(identity.firstName.substring(0, Math.min(3, identity.firstName.length)).toLowerCase() + identity.lastName.toLowerCase()); + break; + } + if (this.getSecureRandom(3) !== 0) { + switch (this.getSecureRandom(2)) { + case 0: + parts.push(identity.birthDate.getFullYear().toString().substring(2)); + break; + case 1: + parts.push(identity.birthDate.getFullYear().toString()); + break; + } + } else if (this.getSecureRandom(2) === 0) { + parts.push((this.getSecureRandom(990) + 10).toString()); + } + let emailPrefix = parts.join(this.getRandomSymbol()); + if (this.getSecureRandom(2) === 0) { + const position = this.getSecureRandom(emailPrefix.length); + emailPrefix = emailPrefix.slice(0, position) + this.getRandomSymbol() + emailPrefix.slice(position); + } + emailPrefix = this.sanitizeEmailPrefix(emailPrefix); + if (emailPrefix.length < _UsernameEmailGenerator.MIN_LENGTH) { + emailPrefix += this.generateRandomString(_UsernameEmailGenerator.MIN_LENGTH - emailPrefix.length); + } else if (emailPrefix.length > _UsernameEmailGenerator.MAX_LENGTH) { + emailPrefix = emailPrefix.substring(0, _UsernameEmailGenerator.MAX_LENGTH); + } + return emailPrefix; + } + /** + * Sanitize an email prefix. + */ + sanitizeEmailPrefix(input) { + let sanitized = input.replace(/[^a-zA-Z0-9._-]/g, ""); + sanitized = sanitized.replace(/[-_.]{2,}/g, (match) => match[0]); + sanitized = sanitized.replace(/^[-._]+/, ""); + sanitized = sanitized.replace(/[-._]*$/, ""); + return sanitized; + } + /** + * Get a random symbol. + */ + getRandomSymbol() { + return this.getSecureRandom(3) === 0 ? this.symbols[this.getSecureRandom(this.symbols.length)] : ""; + } + /** + * Generate a random string. + */ + generateRandomString(length) { + const chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + return Array.from({ length }, () => chars.charAt(this.getSecureRandom(chars.length))).join(""); + } + /** + * Generate a secure random integer between 0 (inclusive) and max (exclusive) + */ + getSecureRandom(max) { + const array = new Uint32Array(1); + crypto.getRandomValues(array); + return array[0] % max; + } +}; +_UsernameEmailGenerator.MIN_LENGTH = 6; +_UsernameEmailGenerator.MAX_LENGTH = 20; +var UsernameEmailGenerator = _UsernameEmailGenerator; + +// src/types/Gender.ts +var Gender = /* @__PURE__ */ ((Gender2) => { + Gender2["Male"] = "Male"; + Gender2["Female"] = "Female"; + Gender2["Other"] = "Other"; + return Gender2; +})(Gender || {}); + +// src/implementations/base/BaseIdentityGenerator.ts +var BaseIdentityGenerator = class { + /** + * Constructor. + */ + constructor() { + this.firstNamesMale = []; + this.firstNamesFemale = []; + this.lastNames = []; + this.random = Math.random; + this.firstNamesMale = this.getFirstNamesMaleJson(); + this.firstNamesFemale = this.getFirstNamesFemaleJson(); + this.lastNames = this.getLastNamesJson(); + } + /** + * Generate a random date of birth. + */ + generateRandomDateOfBirth() { + const today = /* @__PURE__ */ new Date(); + const minAge = 21; + const maxAge = 65; + const minDate = new Date(today.getFullYear() - maxAge, today.getMonth(), today.getDate()); + const maxDate = new Date(today.getFullYear() - minAge, today.getMonth(), today.getDate()); + const timestamp = minDate.getTime() + this.random() * (maxDate.getTime() - minDate.getTime()); + return new Date(timestamp); + } + /** + * Generate a random identity. + */ + generateRandomIdentity() { + const identity = { + firstName: "", + lastName: "", + gender: "Male" /* Male */, + birthDate: /* @__PURE__ */ new Date(), + emailPrefix: "", + nickName: "" + }; + if (this.random() < 0.5) { + identity.firstName = this.firstNamesMale[Math.floor(this.random() * this.firstNamesMale.length)]; + identity.gender = "Male" /* Male */; + } else { + identity.firstName = this.firstNamesFemale[Math.floor(this.random() * this.firstNamesFemale.length)]; + identity.gender = "Female" /* Female */; + } + identity.lastName = this.lastNames[Math.floor(this.random() * this.lastNames.length)]; + identity.birthDate = this.generateRandomDateOfBirth(); + const generator = new UsernameEmailGenerator(); + identity.emailPrefix = generator.generateEmailPrefix(identity); + identity.nickName = generator.generateUsername(identity); + return identity; + } +}; + +// src/dictionaries/en/firstnames_male.ts +var firstnames_male_default = [ + "Michael", + "Christopher", + "Matthew", + "Joshua", + "Daniel", + "David", + "Andrew", + "Joseph", + "James", + "John", + "Robert", + "William", + "Ryan", + "Jason", + "Nicholas", + "Jonathan", + "Jacob", + "Brandon", + "Tyler", + "Zachary", + "Kevin", + "Justin", + "Benjamin", + "Anthony", + "Samuel", + "Thomas", + "Alexander", + "Ethan", + "Noah", + "Dylan", + "Nathan", + "Christian", + "Austin", + "Adam", + "Caleb", + "Cody", + "Jordan", + "Logan", + "Aaron", + "Kyle", + "Jose", + "Brian", + "Gabriel", + "Timothy", + "Luke", + "Jared", + "Connor", + "Sean", + "Evan", + "Isaac", + "Jack", + "Cameron", + "Hunter", + "Jackson", + "Charles", + "Devin", + "Stephen", + "Patrick", + "Steven", + "Elijah", + "Scott", + "Mark", + "Jeffrey", + "Corey", + "Juan", + "Luis", + "Derek", + "Chase", + "Travis", + "Alex", + "Spencer", + "Ian", + "Trevor", + "Bryan", + "Tanner", + "Marcus", + "Jeremy", + "Eric", + "Jaden", + "Garrett", + "Isaiah", + "Dustin", + "Jesse", + "Seth", + "Blake", + "Nathaniel", + "Mason", + "Liam", + "Paul", + "Carlos", + "Mitchell", + "Parker", + "Lucas", + "Richard", + "Cole", + "Adrian", + "Colin", + "Bradley", + "Jesus", + "Peter", + "Kenneth", + "Joel", + "Victor", + "Bryce", + "Casey", + "Vincent", + "Edward", + "Henry", + "Dominic", + "Riley", + "Shane", + "Dalton", + "Grant", + "Shawn", + "Braden", + "Caden", + "Max", + "Hayden", + "Owen", + "Brett", + "Trevor", + "Philip", + "Brendan", + "Wesley", + "Aidan", + "Brady", + "Colton", + "Tristan", + "George", + "Gavin", + "Dawson", + "Miguel", + "Antonio", + "Nolan", + "Dakota", + "Jace", + "Collin", + "Preston", + "Levi", + "Alan", + "Jorge", + "Carson", + "Felix", + "Oliver", + "Theodore", + "Harrison", + "Maxwell", + "Sebastian", + "Xavier", + "Dominick", + "Lincoln", + "Elliott", + "Walter", + "Simon", + "Dean", + "Hugo", + "Malcolm", + "Leon", + "Oscar", + "Calvin", + "Raymond", + "Edgar", + "Franklin", + "Arthur", + "Lawrence", + "Dennis", + "Russell", + "Douglas", + "Leonard", + "Gregory", + "Harold", + "Frederick", + "Martin", + "Curtis", + "Stanley", + "Gilbert", + "Harvey", + "Francis", + "Eugene", + "Ralph", + "Roy", + "Albert", + "Bruce", + "Ronald", + "Keith", + "Craig", + "Roger", + "Randy", + "Gary", + "Dennis", + "Edwin", + "Don", + "Glen", + "Gordon", + "Howard", + "Earl", + "Leo", + "Lloyd", + "Milton", + "Norman", + "Roland", + "Vernon", + "Warren", + "Alfred", + "Bernard", + "Chester", + "Clarence", + "Clifford", + "Clyde", + "Dale", + "Dan", + "Darrell", + "Floyd", + "Herman", + "Jerome", + "Maurice", + "Neil", + "Ray", + "Rodney", + "Roland", + "Stuart", + "Wallace", + "Wayne", + "Wendell", + "Barry", + "Cecil", + "Claude", + "Daryl", + "Edmund", + "Everett", + "Ferdinand", + "Forrest", + "Gerald", + "Hugh", + "Irving", + "Leslie", + "Marvin", + "Morris", + "Nelson", + "Perry", + "Phillip", + "Roderick", + "Ross", + "Terrence", + "Wade", + "Winston", + "Zachariah" +]; + +// src/dictionaries/en/firstnames_female.ts +var firstnames_female_default = [ + "Emily", + "Emma", + "Olivia", + "Ava", + "Sophia", + "Isabella", + "Mia", + "Charlotte", + "Amelia", + "Harper", + "Evelyn", + "Abigail", + "Elizabeth", + "Sofia", + "Avery", + "Ella", + "Madison", + "Scarlett", + "Victoria", + "Aria", + "Grace", + "Chloe", + "Camila", + "Penelope", + "Riley", + "Layla", + "Zoey", + "Nora", + "Lily", + "Eleanor", + "Hannah", + "Lillian", + "Addison", + "Aubrey", + "Ellie", + "Stella", + "Natalie", + "Zoe", + "Leah", + "Hazel", + "Violet", + "Aurora", + "Savannah", + "Audrey", + "Brooklyn", + "Bella", + "Claire", + "Skylar", + "Lucy", + "Paisley", + "Everly", + "Anna", + "Caroline", + "Nova", + "Genesis", + "Emilia", + "Kennedy", + "Samantha", + "Maya", + "Willow", + "Kinsley", + "Naomi", + "Aaliyah", + "Elena", + "Sarah", + "Ariana", + "Allison", + "Gabriella", + "Alice", + "Madelyn", + "Cora", + "Ruby", + "Eva", + "Serenity", + "Autumn", + "Adeline", + "Hailey", + "Gianna", + "Valentina", + "Isla", + "Eliana", + "Quinn", + "Nevaeh", + "Ivy", + "Sadie", + "Piper", + "Lydia", + "Alexa", + "Josephine", + "Emery", + "Julia", + "Delilah", + "Arianna", + "Vivian", + "Kaylee", + "Sophie", + "Brielle", + "Madeline", + "Peyton", + "Rylee", + "Clara", + "Hadley", + "Melanie", + "Mackenzie", + "Reagan", + "Adalyn", + "Liliana", + "Aubree", + "Jade", + "Katherine", + "Isabelle", + "Natalia", + "Raelynn", + "Maria", + "Athena", + "Ximena", + "Arya", + "Leilani", + "Taylor", + "Faith", + "Rose", + "Kylie", + "Alexandra", + "Mary", + "Margaret", + "Lyla", + "Ashley", + "Amaya", + "Eliza", + "Brianna", + "Bailey", + "Andrea", + "Khloe", + "Jasmine", + "Melody", + "Iris", + "Isabel", + "Norah", + "Annabelle", + "Valeria", + "Emerson", + "Adalynn", + "Ryleigh", + "Eden", + "Emersyn", + "Anastasia", + "Kayla", + "Alyssa", + "Anna", + "Juliana", + "Charlie", + "Lucia", + "Stella", + "Adriana", + "Beatrice", + "Bianca", + "Calliope", + "Carmen", + "Celeste", + "Dakota", + "Diana", + "Esther", + "Florence", + "Francesca", + "Georgia", + "Harlow", + "Haven", + "Holly", + "Hope", + "India", + "Indie", + "Iris", + "Juniper", + "Kaia", + "Keira", + "Lara", + "Laura", + "Laurel", + "Luna", + "Magnolia", + "Maeve", + "Marina", + "Marlowe", + "Nina", + "Noelle", + "Octavia", + "Olive", + "Ophelia", + "Phoenix", + "Poppy", + "Primrose", + "Ramona", + "River", + "Rosalie", + "Rosemary", + "Sage", + "Salem", + "Selena", + "Sienna", + "Summer", + "Sylvie", + "Thea", + "Tessa", + "Wren", + "Winter", + "Willa", + "Ada", + "Aspen", + "Blair", + "Brynn", + "Cassidy", + "Cecilia", + "Daisy", + "Dawn", + "Daphne", + "Ember", + "Fiona", + "Flora", + "Freya", + "Gemma", + "Giselle", + "Harmony", + "Heidi", + "Imogen", + "Indie", + "Jessie", + "June", + "Kaia", + "Lena", + "Lola", + "Mabel", + "Maisie", + "Margot", + "Matilda", + "Mira", + "Morgan", + "Nell", + "Nadia", + "Odette", + "Opal", + "Pearl", + "Phoebe", + "Raven", + "Reese", + "Robin", + "Rowan", + "Ruth", + "Sabrina", + "Sasha", + "Sierra", + "Skye", + "Sloane", + "Talia", + "Thora", + "Vera", + "Willa", + "Winnie", + "Yara", + "Zara" +]; + +// src/dictionaries/en/lastnames.ts +var lastnames_default = [ + "Smith", + "Johnson", + "Williams", + "Brown", + "Jones", + "Garcia", + "Miller", + "Davis", + "Rodriguez", + "Martinez", + "Hernandez", + "Lopez", + "Gonzalez", + "Wilson", + "Anderson", + "Thomas", + "Taylor", + "Moore", + "Jackson", + "Martin", + "Lee", + "Perez", + "Thompson", + "White", + "Harris", + "Sanchez", + "Clark", + "Ramirez", + "Lewis", + "Robinson", + "Walker", + "Young", + "Allen", + "King", + "Wright", + "Scott", + "Torres", + "Nguyen", + "Hill", + "Flores", + "Green", + "Adams", + "Nelson", + "Baker", + "Hall", + "Rivera", + "Campbell", + "Mitchell", + "Carter", + "Roberts", + "Gomez", + "Phillips", + "Evans", + "Turner", + "Diaz", + "Parker", + "Cruz", + "Edwards", + "Collins", + "Reyes", + "Stewart", + "Morris", + "Morales", + "Murphy", + "Cook", + "Rogers", + "Gutierrez", + "Ortiz", + "Morgan", + "Cooper", + "Peterson", + "Bailey", + "Reed", + "Kelly", + "Howard", + "Ramos", + "Kim", + "Cox", + "Ward", + "Richardson", + "Watson", + "Brooks", + "Chavez", + "Wood", + "James", + "Bennett", + "Gray", + "Mendoza", + "Ruiz", + "Hughes", + "Price", + "Alvarez", + "Castillo", + "Sanders", + "Patel", + "Myers", + "Long", + "Ross", + "Foster", + "Jimenez", + "Powell", + "Jenkins", + "Perry", + "Russell", + "Sullivan", + "Bell", + "Coleman", + "Butler", + "Henderson", + "Barnes", + "Gonzales", + "Fisher", + "Vasquez", + "Simmons", + "Romero", + "Jordan", + "Patterson", + "Alexander", + "Hamilton", + "Graham", + "Reynolds", + "Griffin", + "Wallace", + "Moreno", + "West", + "Cole", + "Hayes", + "Bryant", + "Herrera", + "Gibson", + "Ellis", + "Tran", + "Medina", + "Aguilar", + "Stevens", + "Murray", + "Ford", + "Castro", + "Marshall", + "Owens", + "Harrison", + "Fernandez", + "McDonald", + "Woods", + "Washington", + "Kennedy", + "Wells", + "Vargas", + "Henry", + "Chen", + "Freeman", + "Webb", + "Tucker", + "Guzman", + "Burns", + "Crawford", + "Olson", + "Simpson", + "Porter", + "Hunter", + "Gordon", + "Mendez", + "Silva", + "Shaw", + "Snyder", + "Mason", + "Dixon", + "Blackwood", + "Shepherd", + "Frost", + "Hawkins", + "Pearson", + "Fleming", + "Dawson", + "Palmer", + "Nash", + "Barker", + "Thornton", + "Fitzgerald", + "Winters", + "Mckenzie", + "Chandler", + "Griffith", + "Cunningham", + "Doyle", + "Fletcher", + "Hicks", + "Walton", + "Briggs", + "Pearce", + "Nichols", + "Blake", + "Hodges", + "Benson", + "Marsh", + "Whitaker", + "Skinner", + "Robbins", + "Goodwin", + "Kirby", + "Savage", + "Hensley", + "Hancock", + "Pratt", + "Gallagher", + "Yates", + "Dennis", + "Swanson", + "Steele", + "Bauer", + "Holt", + "Barber", + "Schultz", + "Foley", + "Fowler", + "Wise", + "Malone", + "Cannon", + "Tate", + "Stark", + "Welch", + "Dyer", + "Booth", + "Payne", + "Shannon", + "Harmon", + "Woodward", + "Morse", + "Jacobson", + "Knowles", + "Blanchard", + "Dillon", + "Stokes", + "Buckley", + "Dickerson", + "Middleton", + "Sellers", + "Cobb", + "Stephenson", + "Roach", + "Moody", + "Beard", + "Mccarthy", + "Garner", + "Mcguire", + "Sloan", + "Ballard", + "Shields", + "Orr", + "Savage", + "Graves", + "Dempsey", + "Weeks", + "Mckay", + "Cooke", + "Riddle", + "Gates", + "Atkins", + "Farrell", + "Lowery", + "Huffman", + "Livingston", + "Davenport", + "Hendricks", + "Kerr", + "Pollard", + "Hoover", + "Wolfe", + "Bowman", + "Underwood", + "Frazier" +]; + +// src/implementations/IdentityGeneratorEn.ts +var IdentityGeneratorEn = class extends BaseIdentityGenerator { + /** + * Get the male first names. + */ + getFirstNamesMaleJson() { + return firstnames_male_default; + } + /** + * Get the female first names. + */ + getFirstNamesFemaleJson() { + return firstnames_female_default; + } + /** + * Get the last names. + */ + getLastNamesJson() { + return lastnames_default; + } +}; + +// src/dictionaries/nl/firstnames_male.ts +var firstnames_male_default2 = [ + "Daan", + "Luuk", + "Sem", + "Finn", + "Milan", + "Levi", + "Noah", + "Lucas", + "Jesse", + "Thijs", + "Jayden", + "Bram", + "Lars", + "Ruben", + "Thomas", + "Tim", + "Sam", + "Liam", + "Julian", + "Mees", + "Ties", + "Sven", + "Max", + "Gijs", + "David", + "Stijn", + "Jasper", + "Niels", + "Jens", + "Timo", + "Cas", + "Joep", + "Roan", + "Tom", + "Tygo", + "Teun", + "Siem", + "Mats", + "Thijmen", + "Rens", + "Niek", + "Tobias", + "Dex", + "Hugo", + "Robin", + "Nick", + "Floris", + "Pepijn", + "Boaz", + "Olivier", + "Luca", + "Jurre", + "Jelle", + "Guus", + "Koen", + "Bart", + "Olaf", + "Wessel", + "Dani\xEBl", + "Job", + "Sander", + "Tijmen", + "Kai", + "Quinten", + "Owen", + "Morris", + "Fedde", + "Joris", + "Jesper", + "Mick", + "Ryan", + "Milo", + "Stan", + "Benjamin", + "Melle", + "Jip", + "Dylan", + "Brent", + "Mick", + "Dean", + "Otis", + "Abel", + "Luc", + "Sepp", + "Vince", + "Rayan", + "Noud", + "Hidde", + "Fabian", + "Jort", + "Damian", + "Boris", + "Sil", + "Moos", + "Aiden", + "Sep", + "Mika", + "Mijs", + "Mika", + "Felix", + "Merlijn", + "Alexander", + "Aron", + "Arthur", + "Axel", + "Bas", + "Bastiaan", + "Berend", + "Bj\xF6rn", + "Casper", + "Cees", + "Chris", + "Christian", + "Christiaan", + "Colin", + "Cornelis", + "Dani", + "Dennis", + "Dirk", + "Dominic", + "Eduard", + "Eelco", + "Erik", + "Erwin", + "Ezra", + "Faas", + "Filip", + "Florian", + "Frank", + "Frederik", + "Freek", + "Gerard", + "Gerrit", + "Giel", + "Gijs", + "Glenn", + "Govert", + "Harm", + "Harold", + "Hendrik", + "Henrik", + "Huub", + "Ian", + "Ivo", + "Jacob", + "Jake", + "Jan", + "Jarno", + "Jason", + "Jeffrey", + "Jeremy", + "Jim", + "Jimmy", + "Johan", + "Johannes", + "Jonas", + "Jonathan", + "Jos", + "Joshua", + "Justin", + "Kay", + "Kevin", + "Kjeld", + "Klaas", + "Lennard", + "Lennart", + "Leon", + "Lex", + "Liam", + "Loek", + "Lorenzo", + "Louis", + "Lowie", + "Maarten", + "Magnus", + "Maikel", + "Marc", + "Marcel", + "Marco", + "Martijn", + "Mathias", + "Matthijs", + "Maurits", + "Menno", + "Michiel", + "Nathan", + "Nico", + "Oscar", + "Pascal", + "Patrick", + "Paul", + "Peter", + "Philip", + "Pieter", + "Pim", + "Quincy", + "Remco", + "Rick", + "Rik", + "Robert", + "Rogier", + "Rowan", + "Ruud", + "Simon", + "Stefan", + "Steven", + "Thom", + "Victor", + "Vincent", + "Willem", + "Wouter", + "Yannick" +]; + +// src/dictionaries/nl/firstnames_female.ts +var firstnames_female_default2 = [ + "Emma", + "Sophie", + "Julia", + "Mila", + "Tess", + "Sara", + "Anna", + "Noor", + "Lotte", + "Liv", + "Eva", + "Nora", + "Zo\xEB", + "Evi", + "Yara", + "Saar", + "Nina", + "Fenna", + "Lieke", + "Fleur", + "Isa", + "Roos", + "Lynn", + "Sofie", + "Sarah", + "Milou", + "Olivia", + "Maud", + "Lisa", + "Vera", + "Luna", + "Lina", + "Noa", + "Feline", + "Lo\xEFs", + "Lena", + "Floor", + "Charlotte", + "Esmee", + "Julie", + "Iris", + "Lara", + "Amber", + "Hailey", + "Mia", + "Lize", + "Isabelle", + "Cato", + "Fenne", + "Sanne", + "Norah", + "Sophia", + "Ella", + "Nova", + "Elin", + "Femke", + "Lizzy", + "Linde", + "Lauren", + "Rosalie", + "Lana", + "Emily", + "Elise", + "Esm\xE9e", + "Anne", + "Isabelle", + "Demi", + "Hannah", + "Liva", + "Suze", + "Fay", + "Isabel", + "Benthe", + "Evi", + "Amy", + "Jasmijn", + "Niene", + "Sterre", + "Fenna", + "Fiene", + "Liz", + "Ise", + "Mara", + "Nienke", + "Indy", + "Romy", + "Lola", + "Puck", + "Nora", + "Merel", + "Bente", + "Eline", + "Lily", + "Leah", + "Naomi", + "Mirthe", + "Valerie", + "Noor", + "Liva", + "Jade", + "Juul", + "Lise", + "Myrthe", + "Veerle", + "Aafke", + "Alicia", + "Amira", + "Aniek", + "Annabel", + "Annelies", + "Anouk", + "Astrid", + "Babette", + "Bianca", + "Britt", + "Carlijn", + "Chantal", + "Claire", + "Dagmar", + "Danique", + "Daphne", + "Denise", + "Dominique", + "Doris", + "Eefje", + "Elena", + "Eline", + "Elisa", + "Elisabeth", + "Ellen", + "Esther", + "Eveline", + "Fabienne", + "Felice", + "Fleur", + "Frederique", + "Gwen", + "Hanna", + "Heleen", + "Helena", + "Ilona", + "Imke", + "Inge", + "Irene", + "Iris", + "Janna", + "Janneke", + "Jasmine", + "Jennifer", + "Jessica", + "Joelle", + "Judith", + "Julia", + "Karin", + "Karlijn", + "Kim", + "Kirsten", + "Kyra", + "Laura", + "Lena", + "Lianne", + "Liesbeth", + "Linda", + "Lisanne", + "Lisette", + "Louise", + "Maartje", + "Manon", + "Margot", + "Marieke", + "Marijke", + "Marlies", + "Marloes", + "Marthe", + "Melissa", + "Michelle", + "Nadine", + "Natalie", + "Nicole", + "Nina", + "Noortje", + "Paulien", + "Petra", + "Rachel", + "Renee", + "Robin", + "Rosa", + "Roxanne", + "Sabine", + "Sandra", + "Saskia", + "Silke", + "Simone", + "Suzanne", + "Sylvie", + "Tamara", + "Tanja", + "Tara", + "Thea", + "Thirza", + "Tina", + "Tineke", + "Ursula", + "Victoria", + "Wendy", + "Wilma", + "Xandra", + "Yasmin", + "Yvette", + "Yvonne", + "Zara" +]; + +// src/dictionaries/nl/lastnames.ts +var lastnames_default2 = [ + "de Jong", + "Jansen", + "de Vries", + "van den Berg", + "van Dijk", + "Bakker", + "Janssen", + "Visser", + "Smit", + "Meijer", + "de Boer", + "Mulder", + "de Groot", + "Bos", + "Vos", + "Peters", + "Hendriks", + "van Leeuwen", + "Dekker", + "Brouwer", + "de Wit", + "Dijkstra", + "Smits", + "de Graaf", + "van der Meer", + "van der Linden", + "Kok", + "Jacobs", + "de Haan", + "Vermeulen", + "van den Heuvel", + "van der Veen", + "van den Broek", + "de Bruijn", + "de Bruin", + "van der Heijden", + "Schouten", + "van Beek", + "Willems", + "van Vliet", + "van de Ven", + "Hoekstra", + "Maas", + "Verhoeven", + "Koster", + "van Dam", + "van der Wal", + "Prins", + "Blom", + "Huisman", + "Peeters", + "Kuipers", + "van Veen", + "van Dongen", + "Veenstra", + "Kramer", + "van den Bosch", + "van der Meulen", + "Mol", + "Zwart", + "van der Laan", + "Martens", + "van de Pol", + "Postma", + "Tromp", + "Borst", + "Boon", + "van Doorn", + "Jonker", + "van der Velden", + "Willemsen", + "van Wijk", + "Groen", + "Gerritsen", + "Bosch", + "van Loon", + "van der Ploeg", + "de Ruiter", + "Molenaar", + "Boer", + "Klein", + "de Koning", + "van de Kamp", + "van der Horst", + "Verbeek", + "Vink", + "Goossens", + "Scholten", + "Hartman", + "van Dalen", + "van Elst", + "Brink", + "Boekel", + "van de Berg", + "Berends", + "van der Hoek", + "Kuiper", + "Kooijman", + "de Lange", + "van der Sluis", + "van Gelder", + "Martens", + "van Asselt", + "Timmermans", + "van Vliet", + "van Rijn", + "van Schaik", + "Bosman", + "Wolters", + "van Hout", + "Hermans", + "van Rooij", + "de Vos", + "van Donselaar", + "Evers", + "van den Brink", + "Verkerk", + "Groeneveld", + "van Duijn", + "Schuurman", + "Hoogendoorn", + "van Zanten", + "Koopman", + "Cornelissen", + "van Driel", + "Teunissen", + "Versteeg", + "van Deursen", + "Schipper", + "van Kempen", + "Bouwman", + "van der Valk", + "Nijhuis", + "van der Werf", + "van den Akker", + "Verhoef", + "Wessels", + "van der Poel", + "Driessen", + "van Oosten", + "Lambrechts", + "van der Vlist", + "Hoogeveen", + "van Gils", + "Rietveld", + "Barendrecht", + "van der Spek", + "Stam", + "van der Linde", + "Boersma", + "van Dijk", + "Schepers", + "van der Kolk", + "Roelofs", + "van der Velden", + "van den Burg", + "Westra", + "van der Steen", + "Pronk", + "van der Veer", + "Rozendaal", + "van den Bos", + "Konings", + "van der Wiel", + "Noordam", + "van der Laan", + "Schut", + "van der Vlugt", + "Witteveen", + "van der Zwan", + "Boogaard", + "van der Waal", + "Stolk", + "van der Windt", + "Rutten", + "van der Zanden", + "Spaans", + "van der Zwaan", + "Roos", + "van der Zijl", + "Schoenmaker", + "van Diepen", + "Romeijn", + "van Doesburg", + "Schippers", + "van Eck", + "Rijken", + "van Egmond", + "Schrama", + "van Eijk", + "Ruijter", + "van Engelen", + "Sanders", + "van Es", + "Schenk", + "van Essen", + "van Gaal", + "van Geenen", + "van Gent", + "van Gestel", + "van Gool", + "van Grinsven", + "van Gurp", + "van Haaften", + "van Haren", + "van Hattem", + "van Hees" +]; + +// src/implementations/IdentityGeneratorNl.ts +var IdentityGeneratorNl = class extends BaseIdentityGenerator { + /** + * Get the male first names. + */ + getFirstNamesMaleJson() { + return firstnames_male_default2; + } + /** + * Get the female first names. + */ + getFirstNamesFemaleJson() { + return firstnames_female_default2; + } + /** + * Get the last names. + */ + getLastNamesJson() { + return lastnames_default2; + } +}; + +// src/utils/IdentityHelperUtils.ts +var IdentityHelperUtils = class { + /** + * Normalize a birth date for display. + */ + static normalizeBirthDateForDisplay(birthDate) { + if (!birthDate || birthDate.startsWith("0001-01-01")) { + return ""; + } + return birthDate.split(/[T ]/)[0]; + } + /** + * Normalize a birth date for database. + */ + static normalizeBirthDateForDb(input) { + if (!input || input.trim() === "") { + return "0001-01-01T00:00:00.000Z"; + } + const trimmed = input.trim().replace(" ", "T"); + const match = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})[T ]?(\d{2}):?(\d{2}):?(\d{2})?$/); + if (match) { + const [_, y, m, d, h = "00", mi = "00", s = "00"] = match; + return `${y}-${m}-${d}T${h}:${mi}:${s}.000Z`; + } + const parsedDate = new Date(trimmed); + if (!isNaN(parsedDate.getTime())) { + return parsedDate.toISOString(); + } + return "0001-01-01T00:00:00.000Z"; + } + /** + * Check if a birth date is valid. + */ + static isValidBirthDate(input) { + if (!input || input.trim() === "") { + return false; + } + if (input.startsWith("0001-01-01")) { + return false; + } + const date = new Date(input); + if (isNaN(date.getTime())) { + return false; + } + const yearValid = date.getFullYear() > 1 && date.getFullYear() < 9999; + return yearValid; + } +}; + +// src/factories/IdentityGeneratorFactory.ts +var createGenerator = (language) => { + switch (language) { + case "en": + return new IdentityGeneratorEn(); + case "nl": + return new IdentityGeneratorNl(); + } + throw new Error(`Unsupported language: ${language}`); +}; +export { + BaseIdentityGenerator, + Gender, + IdentityGeneratorEn, + IdentityGeneratorNl, + IdentityHelperUtils, + UsernameEmailGenerator, + createGenerator +}; +//# sourceMappingURL=index.mjs.map \ No newline at end of file diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/README.md b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/README.md new file mode 100644 index 000000000..1124b2255 --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/README.md @@ -0,0 +1,9 @@ +# ⚠️ Auto-Generated Files + +This folder contains the output of the shared `password-generator` module from the `/shared` directory in the AliasVault project. + +**Do not edit any of these files manually.** + +To make changes: +1. Update the source files in `packages/password-generator/src` +2. Run the `build-and-distribute.sh` script at the root of the project to regenerate the outputs and copy them here. diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.d.ts b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.d.ts new file mode 100644 index 000000000..373c840bf --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.d.ts @@ -0,0 +1,119 @@ +/** + * Settings for password generation stored in SQLite database settings table as string. + */ +type PasswordSettings = { + /** + * The length of the password. + */ + Length: number; + /** + * Whether to use lowercase letters. + */ + UseLowercase: boolean; + /** + * Whether to use uppercase letters. + */ + UseUppercase: boolean; + /** + * Whether to use numbers. + */ + UseNumbers: boolean; + /** + * Whether to use special characters. + */ + UseSpecialChars: boolean; + /** + * Whether to use non-ambiguous characters. + */ + UseNonAmbiguousChars: boolean; +}; + +/** + * Generate a random password. + */ +declare class PasswordGenerator { + private readonly lowercaseChars; + private readonly uppercaseChars; + private readonly numberChars; + private readonly specialChars; + private readonly ambiguousChars; + private length; + private useLowercase; + private useUppercase; + private useNumbers; + private useSpecial; + private useNonAmbiguous; + /** + * Create a new instance of PasswordGenerator. + * @param settings Optional password settings to initialize with. + */ + constructor(settings?: PasswordSettings); + /** + * Apply password settings to this generator. + */ + applySettings(settings: PasswordSettings): this; + /** + * Set the length of the password. + */ + setLength(length: number): this; + /** + * Set if lowercase letters should be used. + */ + useLowercaseLetters(use: boolean): this; + /** + * Set if uppercase letters should be used. + */ + useUppercaseLetters(use: boolean): this; + /** + * Set if numeric characters should be used. + */ + useNumericCharacters(use: boolean): this; + /** + * Set if special characters should be used. + */ + useSpecialCharacters(use: boolean): this; + /** + * Set if only non-ambiguous characters should be used. + */ + useNonAmbiguousCharacters(use: boolean): this; + /** + * Get a random index from the crypto module. + */ + private getUnbiasedRandomIndex; + /** + * Generate a random password. + */ + generateRandomPassword(): string; + /** + * Build character set based on selected options. + */ + private buildCharacterSet; + /** + * Remove ambiguous characters from a character set. + */ + private removeAmbiguousCharacters; + /** + * Generate initial random password. + */ + private generateInitialPassword; + /** + * Ensure the generated password meets all specified requirements. + */ + private ensureRequirements; + /** + * Get a character set with ambiguous characters removed if needed. + */ + private getSafeCharacterSet; + /** + * Add a character from the given set at a random position in the password. + */ + private addCharacterFromSet; +} + +/** + * Creates a new password generator. + * @returns A new password generator instance. + */ +declare const createPasswordGenerator: () => PasswordGenerator; + +export { PasswordGenerator, type PasswordSettings, createPasswordGenerator }; diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.js b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.js new file mode 100644 index 000000000..f155250ad --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.js @@ -0,0 +1,244 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/index.ts +var index_exports = {}; +__export(index_exports, { + PasswordGenerator: () => PasswordGenerator, + createPasswordGenerator: () => createPasswordGenerator +}); +module.exports = __toCommonJS(index_exports); + +// src/utils/PasswordGenerator.ts +var PasswordGenerator = class { + /** + * Create a new instance of PasswordGenerator. + * @param settings Optional password settings to initialize with. + */ + constructor(settings) { + this.lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; + this.uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.numberChars = "0123456789"; + this.specialChars = "!@#$%^&*()_+-=[]{}|;:,.<>?"; + this.ambiguousChars = "Il1O0"; + this.length = 18; + this.useLowercase = true; + this.useUppercase = true; + this.useNumbers = true; + this.useSpecial = true; + this.useNonAmbiguous = false; + if (settings) { + this.applySettings(settings); + } + } + /** + * Apply password settings to this generator. + */ + applySettings(settings) { + this.length = settings.Length; + this.useLowercase = settings.UseLowercase; + this.useUppercase = settings.UseUppercase; + this.useNumbers = settings.UseNumbers; + this.useSpecial = settings.UseSpecialChars; + this.useNonAmbiguous = settings.UseNonAmbiguousChars; + return this; + } + /** + * Set the length of the password. + */ + setLength(length) { + this.length = length; + return this; + } + /** + * Set if lowercase letters should be used. + */ + useLowercaseLetters(use) { + this.useLowercase = use; + return this; + } + /** + * Set if uppercase letters should be used. + */ + useUppercaseLetters(use) { + this.useUppercase = use; + return this; + } + /** + * Set if numeric characters should be used. + */ + useNumericCharacters(use) { + this.useNumbers = use; + return this; + } + /** + * Set if special characters should be used. + */ + useSpecialCharacters(use) { + this.useSpecial = use; + return this; + } + /** + * Set if only non-ambiguous characters should be used. + */ + useNonAmbiguousCharacters(use) { + this.useNonAmbiguous = use; + return this; + } + /** + * Get a random index from the crypto module. + */ + getUnbiasedRandomIndex(max) { + const limit = Math.floor(2 ** 32 / max) * max; + while (true) { + const array = new Uint32Array(1); + crypto.getRandomValues(array); + const value = array[0]; + if (value < limit) { + return value % max; + } + } + } + /** + * Generate a random password. + */ + generateRandomPassword() { + const chars = this.buildCharacterSet(); + let password = this.generateInitialPassword(chars); + password = this.ensureRequirements(password); + return password; + } + /** + * Build character set based on selected options. + */ + buildCharacterSet() { + let chars = ""; + if (this.useLowercase) { + chars += this.lowercaseChars; + } + if (this.useUppercase) { + chars += this.uppercaseChars; + } + if (this.useNumbers) { + chars += this.numberChars; + } + if (this.useSpecial) { + chars += this.specialChars; + } + if (chars.length === 0) { + chars = this.lowercaseChars; + } + if (this.useNonAmbiguous) { + chars = this.removeAmbiguousCharacters(chars); + } + return chars; + } + /** + * Remove ambiguous characters from a character set. + */ + removeAmbiguousCharacters(chars) { + for (const ambChar of this.ambiguousChars) { + chars = chars.replace(ambChar, ""); + } + return chars; + } + /** + * Generate initial random password. + */ + generateInitialPassword(chars) { + let password = ""; + for (let i = 0; i < this.length; i++) { + password += chars[this.getUnbiasedRandomIndex(chars.length)]; + } + return password; + } + /** + * Ensure the generated password meets all specified requirements. + */ + ensureRequirements(password) { + if (this.useLowercase && !/[a-z]/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.getSafeCharacterSet(this.lowercaseChars, true) + ); + } + if (this.useUppercase && !/[A-Z]/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.getSafeCharacterSet(this.uppercaseChars, true) + ); + } + if (this.useNumbers && !/\d/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.getSafeCharacterSet(this.numberChars, false) + ); + } + if (this.useSpecial && !/[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.specialChars + ); + } + return password; + } + /** + * Get a character set with ambiguous characters removed if needed. + */ + getSafeCharacterSet(charSet, isAlpha) { + if (!this.useNonAmbiguous) { + return charSet; + } + let safeSet = charSet; + for (const ambChar of this.ambiguousChars) { + if (!isAlpha && !/\d/.test(ambChar)) { + continue; + } + let charToRemove = ambChar; + if (isAlpha) { + if (charSet === this.lowercaseChars) { + charToRemove = ambChar.toLowerCase(); + } else { + charToRemove = ambChar.toUpperCase(); + } + } + safeSet = safeSet.replace(charToRemove, ""); + } + return safeSet; + } + /** + * Add a character from the given set at a random position in the password. + */ + addCharacterFromSet(password, charSet) { + const pos = this.getUnbiasedRandomIndex(this.length); + const char = charSet[this.getUnbiasedRandomIndex(charSet.length)]; + return password.substring(0, pos) + char + password.substring(pos + 1); + } +}; + +// src/factories/PasswordGeneratorFactory.ts +var createPasswordGenerator = () => { + return new PasswordGenerator(); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + PasswordGenerator, + createPasswordGenerator +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.mjs b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.mjs new file mode 100644 index 000000000..8d845caa4 --- /dev/null +++ b/apps/server/AliasVault.Client/wwwroot/js/shared/password-generator/index.mjs @@ -0,0 +1,216 @@ +// src/utils/PasswordGenerator.ts +var PasswordGenerator = class { + /** + * Create a new instance of PasswordGenerator. + * @param settings Optional password settings to initialize with. + */ + constructor(settings) { + this.lowercaseChars = "abcdefghijklmnopqrstuvwxyz"; + this.uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.numberChars = "0123456789"; + this.specialChars = "!@#$%^&*()_+-=[]{}|;:,.<>?"; + this.ambiguousChars = "Il1O0"; + this.length = 18; + this.useLowercase = true; + this.useUppercase = true; + this.useNumbers = true; + this.useSpecial = true; + this.useNonAmbiguous = false; + if (settings) { + this.applySettings(settings); + } + } + /** + * Apply password settings to this generator. + */ + applySettings(settings) { + this.length = settings.Length; + this.useLowercase = settings.UseLowercase; + this.useUppercase = settings.UseUppercase; + this.useNumbers = settings.UseNumbers; + this.useSpecial = settings.UseSpecialChars; + this.useNonAmbiguous = settings.UseNonAmbiguousChars; + return this; + } + /** + * Set the length of the password. + */ + setLength(length) { + this.length = length; + return this; + } + /** + * Set if lowercase letters should be used. + */ + useLowercaseLetters(use) { + this.useLowercase = use; + return this; + } + /** + * Set if uppercase letters should be used. + */ + useUppercaseLetters(use) { + this.useUppercase = use; + return this; + } + /** + * Set if numeric characters should be used. + */ + useNumericCharacters(use) { + this.useNumbers = use; + return this; + } + /** + * Set if special characters should be used. + */ + useSpecialCharacters(use) { + this.useSpecial = use; + return this; + } + /** + * Set if only non-ambiguous characters should be used. + */ + useNonAmbiguousCharacters(use) { + this.useNonAmbiguous = use; + return this; + } + /** + * Get a random index from the crypto module. + */ + getUnbiasedRandomIndex(max) { + const limit = Math.floor(2 ** 32 / max) * max; + while (true) { + const array = new Uint32Array(1); + crypto.getRandomValues(array); + const value = array[0]; + if (value < limit) { + return value % max; + } + } + } + /** + * Generate a random password. + */ + generateRandomPassword() { + const chars = this.buildCharacterSet(); + let password = this.generateInitialPassword(chars); + password = this.ensureRequirements(password); + return password; + } + /** + * Build character set based on selected options. + */ + buildCharacterSet() { + let chars = ""; + if (this.useLowercase) { + chars += this.lowercaseChars; + } + if (this.useUppercase) { + chars += this.uppercaseChars; + } + if (this.useNumbers) { + chars += this.numberChars; + } + if (this.useSpecial) { + chars += this.specialChars; + } + if (chars.length === 0) { + chars = this.lowercaseChars; + } + if (this.useNonAmbiguous) { + chars = this.removeAmbiguousCharacters(chars); + } + return chars; + } + /** + * Remove ambiguous characters from a character set. + */ + removeAmbiguousCharacters(chars) { + for (const ambChar of this.ambiguousChars) { + chars = chars.replace(ambChar, ""); + } + return chars; + } + /** + * Generate initial random password. + */ + generateInitialPassword(chars) { + let password = ""; + for (let i = 0; i < this.length; i++) { + password += chars[this.getUnbiasedRandomIndex(chars.length)]; + } + return password; + } + /** + * Ensure the generated password meets all specified requirements. + */ + ensureRequirements(password) { + if (this.useLowercase && !/[a-z]/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.getSafeCharacterSet(this.lowercaseChars, true) + ); + } + if (this.useUppercase && !/[A-Z]/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.getSafeCharacterSet(this.uppercaseChars, true) + ); + } + if (this.useNumbers && !/\d/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.getSafeCharacterSet(this.numberChars, false) + ); + } + if (this.useSpecial && !/[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/.exec(password)) { + password = this.addCharacterFromSet( + password, + this.specialChars + ); + } + return password; + } + /** + * Get a character set with ambiguous characters removed if needed. + */ + getSafeCharacterSet(charSet, isAlpha) { + if (!this.useNonAmbiguous) { + return charSet; + } + let safeSet = charSet; + for (const ambChar of this.ambiguousChars) { + if (!isAlpha && !/\d/.test(ambChar)) { + continue; + } + let charToRemove = ambChar; + if (isAlpha) { + if (charSet === this.lowercaseChars) { + charToRemove = ambChar.toLowerCase(); + } else { + charToRemove = ambChar.toUpperCase(); + } + } + safeSet = safeSet.replace(charToRemove, ""); + } + return safeSet; + } + /** + * Add a character from the given set at a random position in the password. + */ + addCharacterFromSet(password, charSet) { + const pos = this.getUnbiasedRandomIndex(this.length); + const char = charSet[this.getUnbiasedRandomIndex(charSet.length)]; + return password.substring(0, pos) + char + password.substring(pos + 1); + } +}; + +// src/factories/PasswordGeneratorFactory.ts +var createPasswordGenerator = () => { + return new PasswordGenerator(); +}; +export { + PasswordGenerator, + createPasswordGenerator +}; +//# sourceMappingURL=index.mjs.map \ No newline at end of file