loredata 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +33 -5
  2. package/data/breaking-bad/characters.json +36 -7
  3. package/data/breaking-bad/meta.json +3 -1
  4. package/data/fast-and-furious/characters.json +26 -7
  5. package/data/fast-and-furious/meta.json +3 -1
  6. package/data/friends/characters.json +128 -22
  7. package/data/friends/meta.json +6 -1
  8. package/data/game-of-thrones/characters.json +45 -15
  9. package/data/game-of-thrones/meta.json +3 -1
  10. package/data/harry-potter/characters.json +30 -7
  11. package/data/harry-potter/meta.json +3 -1
  12. package/data/house-md/characters.json +58 -15
  13. package/data/house-md/meta.json +6 -1
  14. package/data/lost/characters.json +120 -24
  15. package/data/lost/meta.json +6 -1
  16. package/data/matrix/characters.json +112 -24
  17. package/data/matrix/meta.json +6 -1
  18. package/data/peaky-blinders/characters.json +33 -11
  19. package/data/peaky-blinders/meta.json +6 -1
  20. package/data/sherlock/characters.json +99 -19
  21. package/data/sherlock/meta.json +6 -1
  22. package/data/the-office/characters.json +55 -15
  23. package/data/the-office/meta.json +3 -1
  24. package/dist/browser.cjs +107 -8
  25. package/dist/browser.cjs.map +1 -1
  26. package/dist/browser.d.cts +7 -3
  27. package/dist/browser.d.ts +7 -3
  28. package/dist/browser.js +103 -8
  29. package/dist/browser.js.map +1 -1
  30. package/dist/cli/cli.cjs +109 -16
  31. package/dist/cli/cli.cjs.map +1 -1
  32. package/dist/cli/cli.js +110 -17
  33. package/dist/cli/cli.js.map +1 -1
  34. package/dist/index.cjs +110 -15
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +14 -4
  37. package/dist/index.d.ts +14 -4
  38. package/dist/index.js +110 -16
  39. package/dist/index.js.map +1 -1
  40. package/dist/{universe-store-DJrm7bP7.d.cts → universe-store-Del1UHgn.d.cts} +11 -9
  41. package/dist/{universe-store-DJrm7bP7.d.ts → universe-store-Del1UHgn.d.ts} +11 -9
  42. package/package.json +9 -4
package/dist/cli/cli.cjs CHANGED
@@ -6,18 +6,23 @@ var import_commander = require("commander");
6
6
 
7
7
  // src/universes/loader.ts
8
8
  var import_fs = require("fs");
9
+ var import_module = require("module");
9
10
  var import_path = require("path");
10
11
  var import_meta = {};
11
- var dataDir = (0, import_path.join)(import_meta.dirname, "../data");
12
+ var require2 = (0, import_module.createRequire)(import_meta.url);
13
+ var packageRoot = (0, import_path.dirname)(require2.resolve("loredata/package.json"));
14
+ var dataDir = (0, import_path.join)(packageRoot, "data");
12
15
  var UniverseLoader = class {
13
16
  static cache = /* @__PURE__ */ new Map();
14
17
  static register(data) {
15
18
  this.cache.set(data.id, data);
16
19
  }
17
20
  static load(universeId) {
18
- const cached = this.cache.get(universeId);
19
- if (cached) {
20
- return cached;
21
+ if (process.env.NODE_ENV !== "development") {
22
+ const cached = this.cache.get(universeId);
23
+ if (cached) {
24
+ return cached;
25
+ }
21
26
  }
22
27
  const universeDir = (0, import_path.join)(dataDir, universeId);
23
28
  const meta = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(universeDir, "meta.json"), "utf-8"));
@@ -27,6 +32,8 @@ var UniverseLoader = class {
27
32
  const universeData = {
28
33
  id: meta.id,
29
34
  name: meta.name,
35
+ genre: meta.genre,
36
+ description: meta.description,
30
37
  characters,
31
38
  addresses,
32
39
  domains
@@ -34,6 +41,49 @@ var UniverseLoader = class {
34
41
  this.cache.set(universeId, universeData);
35
42
  return universeData;
36
43
  }
44
+ static getManifest() {
45
+ const ids = this.listAvailable();
46
+ const result = ids.map((id) => {
47
+ const universeDir = (0, import_path.join)(dataDir, id);
48
+ const meta = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(universeDir, "meta.json"), "utf-8"));
49
+ const entry = {
50
+ id: meta.id,
51
+ name: meta.name,
52
+ genre: meta.genre,
53
+ description: meta.description
54
+ };
55
+ return entry;
56
+ });
57
+ return result;
58
+ }
59
+ static getAllInterests() {
60
+ const ids = this.listAvailable();
61
+ const interestSet = /* @__PURE__ */ new Set();
62
+ for (const id of ids) {
63
+ const universe = this.load(id);
64
+ for (const character of universe.characters) {
65
+ for (const interest of character.interests) {
66
+ interestSet.add(interest);
67
+ }
68
+ }
69
+ }
70
+ const result = Array.from(interestSet).sort();
71
+ return result;
72
+ }
73
+ static getAllLocations() {
74
+ const ids = this.listAvailable();
75
+ const citySet = /* @__PURE__ */ new Set();
76
+ for (const id of ids) {
77
+ const universe = this.load(id);
78
+ for (const address of universe.addresses) {
79
+ if (address.city) {
80
+ citySet.add(address.city);
81
+ }
82
+ }
83
+ }
84
+ const result = Array.from(citySet).sort();
85
+ return result;
86
+ }
37
87
  static listAvailable() {
38
88
  const entries = (0, import_fs.readdirSync)(dataDir, { withFileTypes: true });
39
89
  const ids = entries.filter((entry) => {
@@ -136,6 +186,45 @@ var PersonFactory = class {
136
186
  const rng = createRng(seed);
137
187
  return this.buildFromCharacter(character, universe, rng);
138
188
  }
189
+ static buildCanonical(characterId, universe) {
190
+ const character = universe.characters.find((c) => c.id === characterId);
191
+ if (!character) {
192
+ throw new Error(`Character "${characterId}" not found in universe "${universe.id}"`);
193
+ }
194
+ const address = character.address ?? universe.addresses[0];
195
+ const username = character.usernames[0];
196
+ const domains = character.emailDomain ? { ...universe.domains, emailDomains: [character.emailDomain] } : universe.domains;
197
+ const rng = createRng(0);
198
+ const email = EmailGenerator.generateFromUsername(username, domains, rng);
199
+ const phone = PhoneGenerator.generate(universe.domains, rng);
200
+ const password = PasswordGenerator.generate(universe.domains, rng);
201
+ const quote = character.quotes[0];
202
+ const person = {
203
+ id: crypto.randomUUID(),
204
+ characterId: character.id,
205
+ firstName: character.firstName,
206
+ lastName: character.lastName,
207
+ username,
208
+ email,
209
+ password,
210
+ phone,
211
+ address: {
212
+ street: address.street,
213
+ city: address.city,
214
+ state: address.state ?? "",
215
+ zip: address.zip ?? "",
216
+ country: address.country
217
+ },
218
+ profession: character.profession,
219
+ interests: character.interests,
220
+ quote,
221
+ universe: universe.id,
222
+ universeName: universe.name,
223
+ ...character.symbol ? { symbol: character.symbol } : {},
224
+ ...character.color ? { color: character.color } : {}
225
+ };
226
+ return person;
227
+ }
139
228
  static buildFromCharacter(character, universe, rng) {
140
229
  const address = character.address ?? pickRandom(universe.addresses, rng);
141
230
  const username = pickRandom(character.usernames, rng);
@@ -144,8 +233,6 @@ var PersonFactory = class {
144
233
  const phone = PhoneGenerator.generate(universe.domains, rng);
145
234
  const password = PasswordGenerator.generate(universe.domains, rng);
146
235
  const quote = pickRandom(character.quotes, rng);
147
- const avatarName = encodeURIComponent(`${character.firstName} ${character.lastName}`);
148
- const avatar = `https://ui-avatars.com/api/?name=${avatarName}&background=random`;
149
236
  const person = {
150
237
  id: crypto.randomUUID(),
151
238
  characterId: character.id,
@@ -167,7 +254,8 @@ var PersonFactory = class {
167
254
  quote,
168
255
  universe: universe.id,
169
256
  universeName: universe.name,
170
- avatar
257
+ ...character.symbol ? { symbol: character.symbol } : {},
258
+ ...character.color ? { color: character.color } : {}
171
259
  };
172
260
  return person;
173
261
  }
@@ -194,12 +282,13 @@ var PersonaBuilder = class {
194
282
  const ids = UniverseLoader.listAvailable();
195
283
  const result = ids.map((id) => {
196
284
  const data = UniverseLoader.load(id);
197
- const info = {
285
+ const meta = {
198
286
  id,
199
287
  name: data.name,
200
- characterCount: data.characters.length
288
+ genre: data.genre,
289
+ description: data.description
201
290
  };
202
- return info;
291
+ return meta;
203
292
  });
204
293
  return result;
205
294
  }
@@ -228,11 +317,15 @@ var UniverseStore = class {
228
317
  return result;
229
318
  }
230
319
  getUniverses() {
231
- const result = this.universes.map((u) => ({
232
- id: u.id,
233
- name: u.name,
234
- characterCount: u.characters.length
235
- }));
320
+ const result = this.universes.map((u) => {
321
+ const meta = {
322
+ id: u.id,
323
+ name: u.name,
324
+ genre: u.genre,
325
+ description: u.description
326
+ };
327
+ return meta;
328
+ });
236
329
  return result;
237
330
  }
238
331
  findCharacters(query) {
@@ -383,7 +476,7 @@ program.command("universes").description("List available universes").option("-f,
383
476
  return;
384
477
  }
385
478
  for (const u of list) {
386
- console.log(` ${u.id.padEnd(20)} ${u.name} (${u.characterCount} characters)`);
479
+ console.log(` ${u.id.padEnd(20)} ${u.name}`);
387
480
  }
388
481
  });
389
482
  program.parse();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/cli.ts","../../src/universes/loader.ts","../../src/generators/random.ts","../../src/generators/email.ts","../../src/generators/phone.ts","../../src/generators/password.ts","../../src/engine/person-factory.ts","../../src/engine/persona-builder.ts","../../src/engine/universe-store.ts","../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\n\nimport { universes, UniverseLoader, UniverseStore } from '@/index';\n\nimport type { CharacterQuery } from '@/types';\n\nconst program = new Command();\n\nprogram.name('loredata').description('Pop culture fake data generator').version('0.1.0');\n\nprogram\n\t.command('person')\n\t.description('Generate a single persona')\n\t.option('-u, --universe <id>', 'Universe ID (e.g. breaking-bad). Omit for all universes.')\n\t.option('-s, --seed <n>', 'Seed for deterministic generation')\n\t.option('-f, --format <fmt>', 'Output format: pretty | json', 'pretty')\n\t.option('--interests <list>', 'Comma-separated interests filter (e.g. chemistry,cooking)')\n\t.option('--interests-mode <mode>', 'Match mode: and | or (default: or)', 'or')\n\t.option('--name <query>', 'Search by character name')\n\t.action(\n\t\t(options: { universe?: string; seed?: string; format: string; interests?: string; interestsMode: string; name?: string }) => {\n\t\t\tconst seed = options.seed ? parseInt(options.seed, 10) : undefined;\n\t\t\tconst universeIds = options.universe ? [options.universe] : UniverseLoader.listAvailable();\n\t\t\tconst universesData = universeIds.map((id) => UniverseLoader.load(id));\n\t\t\tconst storeInstance = new UniverseStore(universesData);\n\n\t\t\tconst query: CharacterQuery = {\n\t\t\t\tuniverses: options.universe ? [options.universe] : [],\n\t\t\t\tinterests: options.interests ? options.interests.split(',').map((s) => s.trim()) : [],\n\t\t\t\tinterestsMode: options.interestsMode === 'and' ? 'and' : 'or',\n\t\t\t\tname: options.name\n\t\t\t};\n\n\t\t\tconst result = storeInstance.generatePersonas(query, 1, seed)[0];\n\n\t\t\tif (!result) {\n\t\t\t\tconsole.error('No characters match the given filters.');\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tif (options.format === 'json') {\n\t\t\t\tconsole.log(JSON.stringify(result, null, 2));\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconsole.log(\n\t\t\t\t`\nName: ${result.firstName} ${result.lastName} (@${result.username})\nEmail: ${result.email}\nPhone: ${result.phone}\nAddress: ${[result.address.street, result.address.city, result.address.country].filter(Boolean).join(', ')}\nProfession: ${result.profession}\nInterests: ${result.interests.join(', ')}\nQuote: ${result.quote}\nUniverse: ${result.universe}\n `.trim()\n\t\t\t);\n\t\t}\n\t);\n\nprogram\n\t.command('group')\n\t.description('Generate multiple personas')\n\t.option('-u, --universe <id>', 'Universe ID (e.g. got). Omit for all universes.')\n\t.option('-n, --size <n>', 'Number of personas', '3')\n\t.option('-s, --seed <n>', 'Seed for deterministic generation')\n\t.option('-f, --format <fmt>', 'Output format: pretty | json', 'pretty')\n\t.option('--interests <list>', 'Comma-separated interests filter (e.g. chemistry,cooking)')\n\t.option('--interests-mode <mode>', 'Match mode: and | or (default: or)', 'or')\n\t.option('--name <query>', 'Search by character name')\n\t.action(\n\t\t(options: {\n\t\t\tuniverse?: string;\n\t\t\tsize: string;\n\t\t\tseed?: string;\n\t\t\tformat: string;\n\t\t\tinterests?: string;\n\t\t\tinterestsMode: string;\n\t\t\tname?: string;\n\t\t}) => {\n\t\t\tconst size = parseInt(options.size, 10);\n\t\t\tconst seed = options.seed ? parseInt(options.seed, 10) : undefined;\n\t\t\tconst universeIds = options.universe ? [options.universe] : UniverseLoader.listAvailable();\n\t\t\tconst universesData = universeIds.map((id) => UniverseLoader.load(id));\n\t\t\tconst storeInstance = new UniverseStore(universesData);\n\n\t\t\tconst query: CharacterQuery = {\n\t\t\t\tuniverses: options.universe ? [options.universe] : [],\n\t\t\t\tinterests: options.interests ? options.interests.split(',').map((s) => s.trim()) : [],\n\t\t\t\tinterestsMode: options.interestsMode === 'and' ? 'and' : 'or',\n\t\t\t\tname: options.name\n\t\t\t};\n\n\t\t\tconst result = storeInstance.generatePersonas(query, size, seed);\n\n\t\t\tif (result.length === 0) {\n\t\t\t\tconsole.error('No characters match the given filters.');\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tif (options.format === 'json') {\n\t\t\t\tconsole.log(JSON.stringify(result, null, 2));\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const p of result) {\n\t\t\t\tconsole.log(`${p.firstName} ${p.lastName} <${p.email}> — ${p.profession}`);\n\t\t\t}\n\t\t}\n\t);\n\nprogram\n\t.command('universes')\n\t.description('List available universes')\n\t.option('-f, --format <fmt>', 'Output format: pretty | json', 'pretty')\n\t.action((options: { format: string }) => {\n\t\tconst list = universes();\n\n\t\tif (options.format === 'json') {\n\t\t\tconsole.log(JSON.stringify(list, null, 2));\n\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const u of list) {\n\t\t\tconsole.log(` ${u.id.padEnd(20)} ${u.name} (${u.characterCount} characters)`);\n\t\t}\n\t});\n\nprogram.parse();\n","import { readFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\n\nimport type { UniverseData, CharacterData, AddressData, DomainsData } from '@/types';\n\nconst dataDir = join(import.meta.dirname, '../data');\n\nexport class UniverseLoader {\n\tprivate static cache = new Map<string, UniverseData>();\n\n\tstatic register(data: UniverseData): void {\n\t\tthis.cache.set(data.id, data);\n\t}\n\n\tstatic load(universeId: string): UniverseData {\n\t\tconst cached = this.cache.get(universeId);\n\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst universeDir = join(dataDir, universeId);\n\t\tconst meta = JSON.parse(readFileSync(join(universeDir, 'meta.json'), 'utf-8')) as {\n\t\t\tid: string;\n\t\t\tname: string;\n\t\t};\n\n\t\tconst characters = JSON.parse(readFileSync(join(universeDir, 'characters.json'), 'utf-8')) as CharacterData[];\n\n\t\tconst addresses = JSON.parse(readFileSync(join(universeDir, 'addresses.json'), 'utf-8')) as AddressData[];\n\n\t\tconst domains = JSON.parse(readFileSync(join(universeDir, 'domains.json'), 'utf-8')) as DomainsData;\n\n\t\tconst universeData: UniverseData = {\n\t\t\tid: meta.id,\n\t\t\tname: meta.name,\n\t\t\tcharacters,\n\t\t\taddresses,\n\t\t\tdomains\n\t\t};\n\n\t\tthis.cache.set(universeId, universeData);\n\n\t\treturn universeData;\n\t}\n\n\tstatic listAvailable(): string[] {\n\t\tconst entries = readdirSync(dataDir, { withFileTypes: true });\n\n\t\tconst ids = entries\n\t\t\t.filter((entry) => {\n\t\t\t\tif (!entry.isDirectory()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treadFileSync(join(dataDir, entry.name, 'meta.json'), 'utf-8');\n\n\t\t\t\t\treturn true;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.map((entry) => entry.name);\n\n\t\treturn ids;\n\t}\n}\n","/**\n * Seeded pseudo-random number generator (mulberry32).\n * Returns a function that produces values in [0, 1).\n */\nexport function createRng(seed?: number): () => number {\n\tif (seed === undefined) {\n\t\treturn Math.random;\n\t}\n\n\tlet s = seed;\n\n\treturn function rng(): number {\n\t\ts += 0x6d2b79f5;\n\t\tlet z = s;\n\t\tz = Math.imul(z ^ (z >>> 15), z | 1);\n\t\tz ^= z + Math.imul(z ^ (z >>> 7), z | 61);\n\n\t\treturn ((z ^ (z >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\nexport function pickRandom<T>(array: T[], rng: () => number): T {\n\tconst index = Math.floor(rng() * array.length);\n\n\treturn array[index];\n}\n","import { pickRandom } from './random';\n\nimport type { DomainsData } from '@/types';\n\nexport class EmailGenerator {\n\tstatic generate(firstName: string, lastName: string, domains: DomainsData, rng: () => number): string {\n\t\tconst domain = pickRandom(domains.emailDomains, rng);\n\t\tconst first = firstName.toLowerCase();\n\t\tconst last = lastName.toLowerCase();\n\n\t\treturn `${first}.${last}@${domain}`;\n\t}\n\n\tstatic generateFromUsername(username: string, domains: DomainsData, rng: () => number): string {\n\t\tconst domain = pickRandom(domains.emailDomains, rng);\n\n\t\treturn `${username}@${domain}`;\n\t}\n}\n","import { pickRandom } from './random';\n\nimport type { DomainsData } from '@/types';\n\nexport class PhoneGenerator {\n\tstatic generate(domains: DomainsData, rng: () => number): string {\n\t\tif (domains.phoneEasterEggs && domains.phoneEasterEggs.length > 0 && rng() < 0.2) {\n\t\t\treturn pickRandom(domains.phoneEasterEggs, rng);\n\t\t}\n\n\t\tconst prefix = pickRandom(domains.phonePrefixes, rng);\n\t\tconst suffix = this.randomDigits(7, rng);\n\n\t\treturn `${prefix}-${suffix.slice(0, 3)}-${suffix.slice(3)}`;\n\t}\n\n\tprivate static randomDigits(count: number, rng: () => number): string {\n\t\tlet result = '';\n\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tresult += Math.floor(rng() * 10).toString();\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","import { pickRandom } from './random';\n\nimport type { DomainsData } from '@/types';\n\nexport class PasswordGenerator {\n\tstatic generate(domains: DomainsData, rng: () => number): string {\n\t\tif (domains.passwordEasterEggs && domains.passwordEasterEggs.length > 0 && rng() < 0.3) {\n\t\t\treturn pickRandom(domains.passwordEasterEggs, rng);\n\t\t}\n\n\t\treturn this.randomPassword(12, rng);\n\t}\n\n\tprivate static randomPassword(length: number, rng: () => number): string {\n\t\tconst chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$';\n\t\tlet result = '';\n\n\t\tfor (let i = 0; i < length; i++) {\n\t\t\tresult += chars[Math.floor(rng() * chars.length)];\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","import { createRng, pickRandom, EmailGenerator, PhoneGenerator, PasswordGenerator } from '@/generators';\n\nimport type { Person, UniverseData, CharacterData } from '@/types';\n\nexport class PersonFactory {\n\tstatic build(universe: UniverseData, seed?: number): Person {\n\t\tconst rng = createRng(seed);\n\n\t\tconst character = pickRandom(universe.characters, rng);\n\n\t\treturn this.buildFromCharacter(character, universe, rng);\n\t}\n\n\tstatic buildByCharacterId(characterId: string, universe: UniverseData, seed?: number): Person {\n\t\tconst character = universe.characters.find((c) => c.id === characterId);\n\n\t\tif (!character) {\n\t\t\tthrow new Error(`Character \"${characterId}\" not found in universe \"${universe.id}\"`);\n\t\t}\n\n\t\tconst rng = createRng(seed);\n\n\t\treturn this.buildFromCharacter(character, universe, rng);\n\t}\n\n\tprivate static buildFromCharacter(character: CharacterData, universe: UniverseData, rng: () => number): Person {\n\t\tconst address = character.address ?? pickRandom(universe.addresses, rng);\n\n\t\tconst username = pickRandom(character.usernames, rng);\n\n\t\tconst domains = character.emailDomain ? { ...universe.domains, emailDomains: [character.emailDomain] } : universe.domains;\n\n\t\tconst email = EmailGenerator.generateFromUsername(username, domains, rng);\n\t\tconst phone = PhoneGenerator.generate(universe.domains, rng);\n\t\tconst password = PasswordGenerator.generate(universe.domains, rng);\n\t\tconst quote = pickRandom(character.quotes, rng);\n\t\tconst avatarName = encodeURIComponent(`${character.firstName} ${character.lastName}`);\n\t\tconst avatar = `https://ui-avatars.com/api/?name=${avatarName}&background=random`;\n\n\t\tconst person: Person = {\n\t\t\tid: crypto.randomUUID(),\n\t\t\tcharacterId: character.id,\n\t\t\tfirstName: character.firstName,\n\t\t\tlastName: character.lastName,\n\t\t\tusername,\n\t\t\temail,\n\t\t\tpassword,\n\t\t\tphone,\n\t\t\taddress: {\n\t\t\t\tstreet: address.street,\n\t\t\t\tcity: address.city,\n\t\t\t\tstate: address.state ?? '',\n\t\t\t\tzip: address.zip ?? '',\n\t\t\t\tcountry: address.country\n\t\t\t},\n\t\t\tprofession: character.profession,\n\t\t\tinterests: character.interests,\n\t\t\tquote,\n\t\t\tuniverse: universe.id,\n\t\t\tuniverseName: universe.name,\n\t\t\tavatar\n\t\t};\n\n\t\treturn person;\n\t}\n}\n","import { UniverseLoader } from '@/universes';\n\nimport { PersonFactory } from './person-factory';\n\nimport type { Person, PersonOptions, GroupOptions, UniverseInfo, UniverseData } from '@/types';\n\nexport class PersonaBuilder {\n\tstatic buildPersonFromData(universe: UniverseData, options: Omit<PersonOptions, 'universe'>): Person {\n\t\treturn PersonFactory.build(universe, options.seed);\n\t}\n\n\tstatic buildPerson(options: PersonOptions): Person {\n\t\tconst universe = UniverseLoader.load(options.universe);\n\n\t\treturn PersonFactory.build(universe, options.seed);\n\t}\n\n\tstatic buildGroup(options: GroupOptions): Person[] {\n\t\tconst persons: Person[] = [];\n\n\t\tfor (let i = 0; i < options.size; i++) {\n\t\t\tconst seed = options.seed !== undefined ? options.seed + i : undefined;\n\n\t\t\tpersons.push(this.buildPerson({ universe: options.universe, seed }));\n\t\t}\n\n\t\treturn persons;\n\t}\n\n\tstatic listUniverses(): UniverseInfo[] {\n\t\tconst ids = UniverseLoader.listAvailable();\n\n\t\tconst result: UniverseInfo[] = ids.map((id) => {\n\t\t\tconst data = UniverseLoader.load(id);\n\n\t\t\tconst info: UniverseInfo = {\n\t\t\t\tid,\n\t\t\t\tname: data.name,\n\t\t\t\tcharacterCount: data.characters.length\n\t\t\t};\n\n\t\t\treturn info;\n\t\t});\n\n\t\treturn result;\n\t}\n}\n","import { createRng } from '@/generators';\n\nimport { PersonFactory } from './person-factory';\n\nimport type { UniverseData, Person, CharacterEntry, CharacterQuery } from '@/types';\n\nexport class UniverseStore {\n\tprivate universes: UniverseData[];\n\tprivate characterIndex: CharacterEntry[];\n\n\tconstructor(universes: UniverseData[]) {\n\t\tthis.universes = universes;\n\t\tthis.characterIndex = this.buildIndex(universes);\n\t}\n\n\tget index(): CharacterEntry[] {\n\t\treturn this.characterIndex;\n\t}\n\n\tgetInterests(universeIds?: string[]): string[] {\n\t\tconst entries =\n\t\t\tuniverseIds && universeIds.length > 0\n\t\t\t\t? this.characterIndex.filter((e) => universeIds.includes(e.universeId))\n\t\t\t\t: this.characterIndex;\n\n\t\tconst interestSet = new Set<string>();\n\n\t\tfor (const entry of entries) {\n\t\t\tfor (const interest of entry.interests) {\n\t\t\t\tinterestSet.add(interest);\n\t\t\t}\n\t\t}\n\n\t\tconst result = Array.from(interestSet).sort();\n\n\t\treturn result;\n\t}\n\n\tgetUniverses(): { id: string; name: string; characterCount: number }[] {\n\t\tconst result = this.universes.map((u) => ({\n\t\t\tid: u.id,\n\t\t\tname: u.name,\n\t\t\tcharacterCount: u.characters.length\n\t\t}));\n\n\t\treturn result;\n\t}\n\n\tfindCharacters(query: CharacterQuery): CharacterEntry[] {\n\t\tlet entries = this.characterIndex;\n\n\t\tif (query.universes && query.universes.length > 0) {\n\t\t\tentries = entries.filter((e) => query.universes!.includes(e.universeId));\n\t\t}\n\n\t\tif (query.name && query.name.trim().length > 0) {\n\t\t\tconst needle = query.name.toLowerCase();\n\n\t\t\tentries = entries.filter((e) => {\n\t\t\t\tconst fullName = `${e.firstName} ${e.lastName}`.toLowerCase();\n\n\t\t\t\treturn fullName.includes(needle);\n\t\t\t});\n\t\t}\n\n\t\tif (query.interests && query.interests.length > 0) {\n\t\t\tconst mode = query.interestsMode ?? 'or';\n\n\t\t\tif (mode === 'and') {\n\t\t\t\tentries = entries.filter((e) => query.interests!.every((interest) => e.interests.includes(interest)));\n\t\t\t} else {\n\t\t\t\tentries = entries.filter((e) => query.interests!.some((interest) => e.interests.includes(interest)));\n\t\t\t}\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\tpersonByCharacterId(characterId: string, seed?: number): Person {\n\t\tconst entry = this.characterIndex.find((e) => e.characterId === characterId);\n\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Character \"${characterId}\" not found in store`);\n\t\t}\n\n\t\tconst universe = this.universes.find((u) => u.id === entry.universeId);\n\n\t\tif (!universe) {\n\t\t\tthrow new Error(`Universe \"${entry.universeId}\" not found in store`);\n\t\t}\n\n\t\treturn PersonFactory.buildByCharacterId(characterId, universe, seed);\n\t}\n\n\tgeneratePersonas(query: CharacterQuery, count: number, seed?: number): Person[] {\n\t\tconst matching = this.findCharacters(query);\n\n\t\tif (matching.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst rng = createRng(seed);\n\t\tconst actualCount = Math.min(count, matching.length);\n\n\t\tconst shuffled = [...matching].sort(() => rng() - 0.5);\n\t\tconst selected = shuffled.slice(0, actualCount);\n\t\tconst personas: Person[] = [];\n\n\t\tfor (let i = 0; i < selected.length; i++) {\n\t\t\tconst entry = selected[i];\n\t\t\tconst universe = this.universes.find((u) => u.id === entry.universeId)!;\n\t\t\tconst personSeed = seed !== undefined ? seed + i : undefined;\n\n\t\t\tpersonas.push(PersonFactory.buildByCharacterId(entry.characterId, universe, personSeed));\n\t\t}\n\n\t\treturn personas;\n\t}\n\n\tprivate buildIndex(universes: UniverseData[]): CharacterEntry[] {\n\t\tconst index: CharacterEntry[] = [];\n\n\t\tfor (const universe of universes) {\n\t\t\tfor (const character of universe.characters) {\n\t\t\t\tconst entry: CharacterEntry = {\n\t\t\t\t\tcharacterId: character.id,\n\t\t\t\t\tuniverseId: universe.id,\n\t\t\t\t\tuniverseName: universe.name,\n\t\t\t\t\tfirstName: character.firstName,\n\t\t\t\t\tlastName: character.lastName,\n\t\t\t\t\tinterests: character.interests,\n\t\t\t\t\tprofession: character.profession\n\t\t\t\t};\n\n\t\t\t\tindex.push(entry);\n\t\t\t}\n\t\t}\n\n\t\treturn index;\n\t}\n}\n","import { PersonaBuilder } from '@/engine';\n\nimport type { Person, UniverseInfo, PersonOptions, GroupOptions, UniverseData } from '@/types';\n\nexport function person(options: PersonOptions): Person {\n\treturn PersonaBuilder.buildPerson(options);\n}\n\nexport function personFromData(universe: UniverseData, seed?: number): Person {\n\treturn PersonaBuilder.buildPersonFromData(universe, { seed });\n}\n\nexport function group(options: GroupOptions): Person[] {\n\treturn PersonaBuilder.buildGroup(options);\n}\n\nexport function universes(): UniverseInfo[] {\n\treturn PersonaBuilder.listUniverses();\n}\n\nexport { UniverseLoader } from '@/universes';\nexport { UniverseStore } from '@/engine';\n\nexport type { Person, UniverseInfo, PersonOptions, GroupOptions } from '@/types';\nexport type { Address, UniverseData, CharacterData, AddressData, DomainsData } from '@/types';\nexport type { CharacterEntry, CharacterQuery } from '@/types';\n"],"mappings":";;;;AACA,uBAAwB;;;ACDxB,gBAA0C;AAC1C,kBAAqB;AADrB;AAKA,IAAM,cAAU,kBAAK,YAAY,SAAS,SAAS;AAE5C,IAAM,iBAAN,MAAqB;AAAA,EAC3B,OAAe,QAAQ,oBAAI,IAA0B;AAAA,EAErD,OAAO,SAAS,MAA0B;AACzC,SAAK,MAAM,IAAI,KAAK,IAAI,IAAI;AAAA,EAC7B;AAAA,EAEA,OAAO,KAAK,YAAkC;AAC7C,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AAExC,QAAI,QAAQ;AACX,aAAO;AAAA,IACR;AAEA,UAAM,kBAAc,kBAAK,SAAS,UAAU;AAC5C,UAAM,OAAO,KAAK,UAAM,4BAAa,kBAAK,aAAa,WAAW,GAAG,OAAO,CAAC;AAK7E,UAAM,aAAa,KAAK,UAAM,4BAAa,kBAAK,aAAa,iBAAiB,GAAG,OAAO,CAAC;AAEzF,UAAM,YAAY,KAAK,UAAM,4BAAa,kBAAK,aAAa,gBAAgB,GAAG,OAAO,CAAC;AAEvF,UAAM,UAAU,KAAK,UAAM,4BAAa,kBAAK,aAAa,cAAc,GAAG,OAAO,CAAC;AAEnF,UAAM,eAA6B;AAAA,MAClC,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,SAAK,MAAM,IAAI,YAAY,YAAY;AAEvC,WAAO;AAAA,EACR;AAAA,EAEA,OAAO,gBAA0B;AAChC,UAAM,cAAU,uBAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE5D,UAAM,MAAM,QACV,OAAO,CAAC,UAAU;AAClB,UAAI,CAAC,MAAM,YAAY,GAAG;AACzB,eAAO;AAAA,MACR;AAEA,UAAI;AACH,wCAAa,kBAAK,SAAS,MAAM,MAAM,WAAW,GAAG,OAAO;AAE5D,eAAO;AAAA,MACR,QAAQ;AACP,eAAO;AAAA,MACR;AAAA,IACD,CAAC,EACA,IAAI,CAAC,UAAU,MAAM,IAAI;AAE3B,WAAO;AAAA,EACR;AACD;;;AC/DO,SAAS,UAAU,MAA6B;AACtD,MAAI,SAAS,QAAW;AACvB,WAAO,KAAK;AAAA,EACb;AAEA,MAAI,IAAI;AAER,SAAO,SAAS,MAAc;AAC7B,SAAK;AACL,QAAI,IAAI;AACR,QAAI,KAAK,KAAK,IAAK,MAAM,IAAK,IAAI,CAAC;AACnC,SAAK,IAAI,KAAK,KAAK,IAAK,MAAM,GAAI,IAAI,EAAE;AAExC,aAAS,IAAK,MAAM,QAAS,KAAK;AAAA,EACnC;AACD;AAEO,SAAS,WAAc,OAAY,KAAsB;AAC/D,QAAM,QAAQ,KAAK,MAAM,IAAI,IAAI,MAAM,MAAM;AAE7C,SAAO,MAAM,KAAK;AACnB;;;ACrBO,IAAM,iBAAN,MAAqB;AAAA,EAC3B,OAAO,SAAS,WAAmB,UAAkB,SAAsB,KAA2B;AACrG,UAAM,SAAS,WAAW,QAAQ,cAAc,GAAG;AACnD,UAAM,QAAQ,UAAU,YAAY;AACpC,UAAM,OAAO,SAAS,YAAY;AAElC,WAAO,GAAG,KAAK,IAAI,IAAI,IAAI,MAAM;AAAA,EAClC;AAAA,EAEA,OAAO,qBAAqB,UAAkB,SAAsB,KAA2B;AAC9F,UAAM,SAAS,WAAW,QAAQ,cAAc,GAAG;AAEnD,WAAO,GAAG,QAAQ,IAAI,MAAM;AAAA,EAC7B;AACD;;;ACdO,IAAM,iBAAN,MAAqB;AAAA,EAC3B,OAAO,SAAS,SAAsB,KAA2B;AAChE,QAAI,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,KAAK,IAAI,IAAI,KAAK;AACjF,aAAO,WAAW,QAAQ,iBAAiB,GAAG;AAAA,IAC/C;AAEA,UAAM,SAAS,WAAW,QAAQ,eAAe,GAAG;AACpD,UAAM,SAAS,KAAK,aAAa,GAAG,GAAG;AAEvC,WAAO,GAAG,MAAM,IAAI,OAAO,MAAM,GAAG,CAAC,CAAC,IAAI,OAAO,MAAM,CAAC,CAAC;AAAA,EAC1D;AAAA,EAEA,OAAe,aAAa,OAAe,KAA2B;AACrE,QAAI,SAAS;AAEb,aAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC/B,gBAAU,KAAK,MAAM,IAAI,IAAI,EAAE,EAAE,SAAS;AAAA,IAC3C;AAEA,WAAO;AAAA,EACR;AACD;;;ACrBO,IAAM,oBAAN,MAAwB;AAAA,EAC9B,OAAO,SAAS,SAAsB,KAA2B;AAChE,QAAI,QAAQ,sBAAsB,QAAQ,mBAAmB,SAAS,KAAK,IAAI,IAAI,KAAK;AACvF,aAAO,WAAW,QAAQ,oBAAoB,GAAG;AAAA,IAClD;AAEA,WAAO,KAAK,eAAe,IAAI,GAAG;AAAA,EACnC;AAAA,EAEA,OAAe,eAAe,QAAgB,KAA2B;AACxE,UAAM,QAAQ;AACd,QAAI,SAAS;AAEb,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAChC,gBAAU,MAAM,KAAK,MAAM,IAAI,IAAI,MAAM,MAAM,CAAC;AAAA,IACjD;AAEA,WAAO;AAAA,EACR;AACD;;;ACnBO,IAAM,gBAAN,MAAoB;AAAA,EAC1B,OAAO,MAAM,UAAwB,MAAuB;AAC3D,UAAM,MAAM,UAAU,IAAI;AAE1B,UAAM,YAAY,WAAW,SAAS,YAAY,GAAG;AAErD,WAAO,KAAK,mBAAmB,WAAW,UAAU,GAAG;AAAA,EACxD;AAAA,EAEA,OAAO,mBAAmB,aAAqB,UAAwB,MAAuB;AAC7F,UAAM,YAAY,SAAS,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW;AAEtE,QAAI,CAAC,WAAW;AACf,YAAM,IAAI,MAAM,cAAc,WAAW,4BAA4B,SAAS,EAAE,GAAG;AAAA,IACpF;AAEA,UAAM,MAAM,UAAU,IAAI;AAE1B,WAAO,KAAK,mBAAmB,WAAW,UAAU,GAAG;AAAA,EACxD;AAAA,EAEA,OAAe,mBAAmB,WAA0B,UAAwB,KAA2B;AAC9G,UAAM,UAAU,UAAU,WAAW,WAAW,SAAS,WAAW,GAAG;AAEvE,UAAM,WAAW,WAAW,UAAU,WAAW,GAAG;AAEpD,UAAM,UAAU,UAAU,cAAc,EAAE,GAAG,SAAS,SAAS,cAAc,CAAC,UAAU,WAAW,EAAE,IAAI,SAAS;AAElH,UAAM,QAAQ,eAAe,qBAAqB,UAAU,SAAS,GAAG;AACxE,UAAM,QAAQ,eAAe,SAAS,SAAS,SAAS,GAAG;AAC3D,UAAM,WAAW,kBAAkB,SAAS,SAAS,SAAS,GAAG;AACjE,UAAM,QAAQ,WAAW,UAAU,QAAQ,GAAG;AAC9C,UAAM,aAAa,mBAAmB,GAAG,UAAU,SAAS,IAAI,UAAU,QAAQ,EAAE;AACpF,UAAM,SAAS,oCAAoC,UAAU;AAE7D,UAAM,SAAiB;AAAA,MACtB,IAAI,OAAO,WAAW;AAAA,MACtB,aAAa,UAAU;AAAA,MACvB,WAAW,UAAU;AAAA,MACrB,UAAU,UAAU;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACR,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,QACd,OAAO,QAAQ,SAAS;AAAA,QACxB,KAAK,QAAQ,OAAO;AAAA,QACpB,SAAS,QAAQ;AAAA,MAClB;AAAA,MACA,YAAY,UAAU;AAAA,MACtB,WAAW,UAAU;AAAA,MACrB;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,cAAc,SAAS;AAAA,MACvB;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AACD;;;AC3DO,IAAM,iBAAN,MAAqB;AAAA,EAC3B,OAAO,oBAAoB,UAAwB,SAAkD;AACpG,WAAO,cAAc,MAAM,UAAU,QAAQ,IAAI;AAAA,EAClD;AAAA,EAEA,OAAO,YAAY,SAAgC;AAClD,UAAM,WAAW,eAAe,KAAK,QAAQ,QAAQ;AAErD,WAAO,cAAc,MAAM,UAAU,QAAQ,IAAI;AAAA,EAClD;AAAA,EAEA,OAAO,WAAW,SAAiC;AAClD,UAAM,UAAoB,CAAC;AAE3B,aAAS,IAAI,GAAG,IAAI,QAAQ,MAAM,KAAK;AACtC,YAAM,OAAO,QAAQ,SAAS,SAAY,QAAQ,OAAO,IAAI;AAE7D,cAAQ,KAAK,KAAK,YAAY,EAAE,UAAU,QAAQ,UAAU,KAAK,CAAC,CAAC;AAAA,IACpE;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,OAAO,gBAAgC;AACtC,UAAM,MAAM,eAAe,cAAc;AAEzC,UAAM,SAAyB,IAAI,IAAI,CAAC,OAAO;AAC9C,YAAM,OAAO,eAAe,KAAK,EAAE;AAEnC,YAAM,OAAqB;AAAA,QAC1B;AAAA,QACA,MAAM,KAAK;AAAA,QACX,gBAAgB,KAAK,WAAW;AAAA,MACjC;AAEA,aAAO;AAAA,IACR,CAAC;AAED,WAAO;AAAA,EACR;AACD;;;ACxCO,IAAM,gBAAN,MAAoB;AAAA,EAClB;AAAA,EACA;AAAA,EAER,YAAYA,YAA2B;AACtC,SAAK,YAAYA;AACjB,SAAK,iBAAiB,KAAK,WAAWA,UAAS;AAAA,EAChD;AAAA,EAEA,IAAI,QAA0B;AAC7B,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,aAAa,aAAkC;AAC9C,UAAM,UACL,eAAe,YAAY,SAAS,IACjC,KAAK,eAAe,OAAO,CAAC,MAAM,YAAY,SAAS,EAAE,UAAU,CAAC,IACpE,KAAK;AAET,UAAM,cAAc,oBAAI,IAAY;AAEpC,eAAW,SAAS,SAAS;AAC5B,iBAAW,YAAY,MAAM,WAAW;AACvC,oBAAY,IAAI,QAAQ;AAAA,MACzB;AAAA,IACD;AAEA,UAAM,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK;AAE5C,WAAO;AAAA,EACR;AAAA,EAEA,eAAuE;AACtE,UAAM,SAAS,KAAK,UAAU,IAAI,CAAC,OAAO;AAAA,MACzC,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,gBAAgB,EAAE,WAAW;AAAA,IAC9B,EAAE;AAEF,WAAO;AAAA,EACR;AAAA,EAEA,eAAe,OAAyC;AACvD,QAAI,UAAU,KAAK;AAEnB,QAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AAClD,gBAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,UAAW,SAAS,EAAE,UAAU,CAAC;AAAA,IACxE;AAEA,QAAI,MAAM,QAAQ,MAAM,KAAK,KAAK,EAAE,SAAS,GAAG;AAC/C,YAAM,SAAS,MAAM,KAAK,YAAY;AAEtC,gBAAU,QAAQ,OAAO,CAAC,MAAM;AAC/B,cAAM,WAAW,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,GAAG,YAAY;AAE5D,eAAO,SAAS,SAAS,MAAM;AAAA,MAChC,CAAC;AAAA,IACF;AAEA,QAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AAClD,YAAM,OAAO,MAAM,iBAAiB;AAEpC,UAAI,SAAS,OAAO;AACnB,kBAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,UAAW,MAAM,CAAC,aAAa,EAAE,UAAU,SAAS,QAAQ,CAAC,CAAC;AAAA,MACrG,OAAO;AACN,kBAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,UAAW,KAAK,CAAC,aAAa,EAAE,UAAU,SAAS,QAAQ,CAAC,CAAC;AAAA,MACpG;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,oBAAoB,aAAqB,MAAuB;AAC/D,UAAM,QAAQ,KAAK,eAAe,KAAK,CAAC,MAAM,EAAE,gBAAgB,WAAW;AAE3E,QAAI,CAAC,OAAO;AACX,YAAM,IAAI,MAAM,cAAc,WAAW,sBAAsB;AAAA,IAChE;AAEA,UAAM,WAAW,KAAK,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,UAAU;AAErE,QAAI,CAAC,UAAU;AACd,YAAM,IAAI,MAAM,aAAa,MAAM,UAAU,sBAAsB;AAAA,IACpE;AAEA,WAAO,cAAc,mBAAmB,aAAa,UAAU,IAAI;AAAA,EACpE;AAAA,EAEA,iBAAiB,OAAuB,OAAe,MAAyB;AAC/E,UAAM,WAAW,KAAK,eAAe,KAAK;AAE1C,QAAI,SAAS,WAAW,GAAG;AAC1B,aAAO,CAAC;AAAA,IACT;AAEA,UAAM,MAAM,UAAU,IAAI;AAC1B,UAAM,cAAc,KAAK,IAAI,OAAO,SAAS,MAAM;AAEnD,UAAM,WAAW,CAAC,GAAG,QAAQ,EAAE,KAAK,MAAM,IAAI,IAAI,GAAG;AACrD,UAAM,WAAW,SAAS,MAAM,GAAG,WAAW;AAC9C,UAAM,WAAqB,CAAC;AAE5B,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,YAAM,QAAQ,SAAS,CAAC;AACxB,YAAM,WAAW,KAAK,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,UAAU;AACrE,YAAM,aAAa,SAAS,SAAY,OAAO,IAAI;AAEnD,eAAS,KAAK,cAAc,mBAAmB,MAAM,aAAa,UAAU,UAAU,CAAC;AAAA,IACxF;AAEA,WAAO;AAAA,EACR;AAAA,EAEQ,WAAWA,YAA6C;AAC/D,UAAM,QAA0B,CAAC;AAEjC,eAAW,YAAYA,YAAW;AACjC,iBAAW,aAAa,SAAS,YAAY;AAC5C,cAAM,QAAwB;AAAA,UAC7B,aAAa,UAAU;AAAA,UACvB,YAAY,SAAS;AAAA,UACrB,cAAc,SAAS;AAAA,UACvB,WAAW,UAAU;AAAA,UACrB,UAAU,UAAU;AAAA,UACpB,WAAW,UAAU;AAAA,UACrB,YAAY,UAAU;AAAA,QACvB;AAEA,cAAM,KAAK,KAAK;AAAA,MACjB;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AACD;;;AC5HO,SAAS,YAA4B;AAC3C,SAAO,eAAe,cAAc;AACrC;;;ATXA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QAAQ,KAAK,UAAU,EAAE,YAAY,iCAAiC,EAAE,QAAQ,OAAO;AAEvF,QACE,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,OAAO,uBAAuB,0DAA0D,EACxF,OAAO,kBAAkB,mCAAmC,EAC5D,OAAO,sBAAsB,gCAAgC,QAAQ,EACrE,OAAO,sBAAsB,2DAA2D,EACxF,OAAO,2BAA2B,sCAAsC,IAAI,EAC5E,OAAO,kBAAkB,0BAA0B,EACnD;AAAA,EACA,CAAC,YAA4H;AAC5H,UAAM,OAAO,QAAQ,OAAO,SAAS,QAAQ,MAAM,EAAE,IAAI;AACzD,UAAM,cAAc,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,eAAe,cAAc;AACzF,UAAM,gBAAgB,YAAY,IAAI,CAAC,OAAO,eAAe,KAAK,EAAE,CAAC;AACrE,UAAM,gBAAgB,IAAI,cAAc,aAAa;AAErD,UAAM,QAAwB;AAAA,MAC7B,WAAW,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAAA,MACpD,WAAW,QAAQ,YAAY,QAAQ,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;AAAA,MACpF,eAAe,QAAQ,kBAAkB,QAAQ,QAAQ;AAAA,MACzD,MAAM,QAAQ;AAAA,IACf;AAEA,UAAM,SAAS,cAAc,iBAAiB,OAAO,GAAG,IAAI,EAAE,CAAC;AAE/D,QAAI,CAAC,QAAQ;AACZ,cAAQ,MAAM,wCAAwC;AACtD,cAAQ,KAAK,CAAC;AAAA,IACf;AAEA,QAAI,QAAQ,WAAW,QAAQ;AAC9B,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAE3C;AAAA,IACD;AAEA,YAAQ;AAAA,MACP;AAAA,cACU,OAAO,SAAS,IAAI,OAAO,QAAQ,MAAM,OAAO,QAAQ;AAAA,cACxD,OAAO,KAAK;AAAA,cACZ,OAAO,KAAK;AAAA,cACZ,CAAC,OAAO,QAAQ,QAAQ,OAAO,QAAQ,MAAM,OAAO,QAAQ,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,cAC/F,OAAO,UAAU;AAAA,cACjB,OAAO,UAAU,KAAK,IAAI,CAAC;AAAA,cAC3B,OAAO,KAAK;AAAA,cACZ,OAAO,QAAQ;AAAA,MACvB,KAAK;AAAA,IACR;AAAA,EACD;AACD;AAED,QACE,QAAQ,OAAO,EACf,YAAY,4BAA4B,EACxC,OAAO,uBAAuB,iDAAiD,EAC/E,OAAO,kBAAkB,sBAAsB,GAAG,EAClD,OAAO,kBAAkB,mCAAmC,EAC5D,OAAO,sBAAsB,gCAAgC,QAAQ,EACrE,OAAO,sBAAsB,2DAA2D,EACxF,OAAO,2BAA2B,sCAAsC,IAAI,EAC5E,OAAO,kBAAkB,0BAA0B,EACnD;AAAA,EACA,CAAC,YAQK;AACL,UAAM,OAAO,SAAS,QAAQ,MAAM,EAAE;AACtC,UAAM,OAAO,QAAQ,OAAO,SAAS,QAAQ,MAAM,EAAE,IAAI;AACzD,UAAM,cAAc,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,eAAe,cAAc;AACzF,UAAM,gBAAgB,YAAY,IAAI,CAAC,OAAO,eAAe,KAAK,EAAE,CAAC;AACrE,UAAM,gBAAgB,IAAI,cAAc,aAAa;AAErD,UAAM,QAAwB;AAAA,MAC7B,WAAW,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAAA,MACpD,WAAW,QAAQ,YAAY,QAAQ,UAAU,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;AAAA,MACpF,eAAe,QAAQ,kBAAkB,QAAQ,QAAQ;AAAA,MACzD,MAAM,QAAQ;AAAA,IACf;AAEA,UAAM,SAAS,cAAc,iBAAiB,OAAO,MAAM,IAAI;AAE/D,QAAI,OAAO,WAAW,GAAG;AACxB,cAAQ,MAAM,wCAAwC;AACtD,cAAQ,KAAK,CAAC;AAAA,IACf;AAEA,QAAI,QAAQ,WAAW,QAAQ;AAC9B,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAE3C;AAAA,IACD;AAEA,eAAW,KAAK,QAAQ;AACvB,cAAQ,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,KAAK,EAAE,KAAK,YAAO,EAAE,UAAU,EAAE;AAAA,IAC1E;AAAA,EACD;AACD;AAED,QACE,QAAQ,WAAW,EACnB,YAAY,0BAA0B,EACtC,OAAO,sBAAsB,gCAAgC,QAAQ,EACrE,OAAO,CAAC,YAAgC;AACxC,QAAM,OAAO,UAAU;AAEvB,MAAI,QAAQ,WAAW,QAAQ;AAC9B,YAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAEzC;AAAA,EACD;AAEA,aAAW,KAAK,MAAM;AACrB,YAAQ,IAAI,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,EAAE,cAAc,cAAc;AAAA,EAC9E;AACD,CAAC;AAEF,QAAQ,MAAM;","names":["universes"]}
1
+ {"version":3,"sources":["../../src/cli/cli.ts","../../src/universes/loader.ts","../../src/generators/random.ts","../../src/generators/email.ts","../../src/generators/phone.ts","../../src/generators/password.ts","../../src/engine/person-factory.ts","../../src/engine/persona-builder.ts","../../src/engine/universe-store.ts","../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\n\nimport { universes, UniverseLoader, UniverseStore } from '@/index';\n\nimport type { CharacterQuery } from '@/types';\n\nconst program = new Command();\n\nprogram.name('loredata').description('Pop culture fake data generator').version('0.1.0');\n\nprogram\n\t.command('person')\n\t.description('Generate a single persona')\n\t.option('-u, --universe <id>', 'Universe ID (e.g. breaking-bad). Omit for all universes.')\n\t.option('-s, --seed <n>', 'Seed for deterministic generation')\n\t.option('-f, --format <fmt>', 'Output format: pretty | json', 'pretty')\n\t.option('--interests <list>', 'Comma-separated interests filter (e.g. chemistry,cooking)')\n\t.option('--interests-mode <mode>', 'Match mode: and | or (default: or)', 'or')\n\t.option('--name <query>', 'Search by character name')\n\t.action(\n\t\t(options: { universe?: string; seed?: string; format: string; interests?: string; interestsMode: string; name?: string }) => {\n\t\t\tconst seed = options.seed ? parseInt(options.seed, 10) : undefined;\n\t\t\tconst universeIds = options.universe ? [options.universe] : UniverseLoader.listAvailable();\n\t\t\tconst universesData = universeIds.map((id) => UniverseLoader.load(id));\n\t\t\tconst storeInstance = new UniverseStore(universesData);\n\n\t\t\tconst query: CharacterQuery = {\n\t\t\t\tuniverses: options.universe ? [options.universe] : [],\n\t\t\t\tinterests: options.interests ? options.interests.split(',').map((s) => s.trim()) : [],\n\t\t\t\tinterestsMode: options.interestsMode === 'and' ? 'and' : 'or',\n\t\t\t\tname: options.name\n\t\t\t};\n\n\t\t\tconst result = storeInstance.generatePersonas(query, 1, seed)[0];\n\n\t\t\tif (!result) {\n\t\t\t\tconsole.error('No characters match the given filters.');\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tif (options.format === 'json') {\n\t\t\t\tconsole.log(JSON.stringify(result, null, 2));\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconsole.log(\n\t\t\t\t`\nName: ${result.firstName} ${result.lastName} (@${result.username})\nEmail: ${result.email}\nPhone: ${result.phone}\nAddress: ${[result.address.street, result.address.city, result.address.country].filter(Boolean).join(', ')}\nProfession: ${result.profession}\nInterests: ${result.interests.join(', ')}\nQuote: ${result.quote}\nUniverse: ${result.universe}\n `.trim()\n\t\t\t);\n\t\t}\n\t);\n\nprogram\n\t.command('group')\n\t.description('Generate multiple personas')\n\t.option('-u, --universe <id>', 'Universe ID (e.g. got). Omit for all universes.')\n\t.option('-n, --size <n>', 'Number of personas', '3')\n\t.option('-s, --seed <n>', 'Seed for deterministic generation')\n\t.option('-f, --format <fmt>', 'Output format: pretty | json', 'pretty')\n\t.option('--interests <list>', 'Comma-separated interests filter (e.g. chemistry,cooking)')\n\t.option('--interests-mode <mode>', 'Match mode: and | or (default: or)', 'or')\n\t.option('--name <query>', 'Search by character name')\n\t.action(\n\t\t(options: {\n\t\t\tuniverse?: string;\n\t\t\tsize: string;\n\t\t\tseed?: string;\n\t\t\tformat: string;\n\t\t\tinterests?: string;\n\t\t\tinterestsMode: string;\n\t\t\tname?: string;\n\t\t}) => {\n\t\t\tconst size = parseInt(options.size, 10);\n\t\t\tconst seed = options.seed ? parseInt(options.seed, 10) : undefined;\n\t\t\tconst universeIds = options.universe ? [options.universe] : UniverseLoader.listAvailable();\n\t\t\tconst universesData = universeIds.map((id) => UniverseLoader.load(id));\n\t\t\tconst storeInstance = new UniverseStore(universesData);\n\n\t\t\tconst query: CharacterQuery = {\n\t\t\t\tuniverses: options.universe ? [options.universe] : [],\n\t\t\t\tinterests: options.interests ? options.interests.split(',').map((s) => s.trim()) : [],\n\t\t\t\tinterestsMode: options.interestsMode === 'and' ? 'and' : 'or',\n\t\t\t\tname: options.name\n\t\t\t};\n\n\t\t\tconst result = storeInstance.generatePersonas(query, size, seed);\n\n\t\t\tif (result.length === 0) {\n\t\t\t\tconsole.error('No characters match the given filters.');\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\tif (options.format === 'json') {\n\t\t\t\tconsole.log(JSON.stringify(result, null, 2));\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const p of result) {\n\t\t\t\tconsole.log(`${p.firstName} ${p.lastName} <${p.email}> — ${p.profession}`);\n\t\t\t}\n\t\t}\n\t);\n\nprogram\n\t.command('universes')\n\t.description('List available universes')\n\t.option('-f, --format <fmt>', 'Output format: pretty | json', 'pretty')\n\t.action((options: { format: string }) => {\n\t\tconst list = universes();\n\n\t\tif (options.format === 'json') {\n\t\t\tconsole.log(JSON.stringify(list, null, 2));\n\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const u of list) {\n\t\t\tconsole.log(` ${u.id.padEnd(20)} ${u.name}`);\n\t\t}\n\t});\n\nprogram.parse();\n","import { readFileSync, readdirSync } from 'fs';\nimport { createRequire } from 'module';\nimport { dirname, join } from 'path';\n\nimport type { UniverseData, UniverseMeta, CharacterData, AddressData, DomainsData } from '@/types';\n\nconst require = createRequire(import.meta.url);\nconst packageRoot = dirname(require.resolve('loredata/package.json'));\nconst dataDir = join(packageRoot, 'data');\n\nexport class UniverseLoader {\n\tprivate static cache = new Map<string, UniverseData>();\n\n\tstatic register(data: UniverseData): void {\n\t\tthis.cache.set(data.id, data);\n\t}\n\n\tstatic load(universeId: string): UniverseData {\n\t\tif (process.env.NODE_ENV !== 'development') {\n\t\t\tconst cached = this.cache.get(universeId);\n\n\t\t\tif (cached) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t}\n\n\t\tconst universeDir = join(dataDir, universeId);\n\t\tconst meta = JSON.parse(readFileSync(join(universeDir, 'meta.json'), 'utf-8')) as {\n\t\t\tid: string;\n\t\t\tname: string;\n\t\t\tgenre: string[];\n\t\t\tdescription: string;\n\t\t};\n\n\t\tconst characters = JSON.parse(readFileSync(join(universeDir, 'characters.json'), 'utf-8')) as CharacterData[];\n\n\t\tconst addresses = JSON.parse(readFileSync(join(universeDir, 'addresses.json'), 'utf-8')) as AddressData[];\n\n\t\tconst domains = JSON.parse(readFileSync(join(universeDir, 'domains.json'), 'utf-8')) as DomainsData;\n\n\t\tconst universeData: UniverseData = {\n\t\t\tid: meta.id,\n\t\t\tname: meta.name,\n\t\t\tgenre: meta.genre,\n\t\t\tdescription: meta.description,\n\t\t\tcharacters,\n\t\t\taddresses,\n\t\t\tdomains\n\t\t};\n\n\t\tthis.cache.set(universeId, universeData);\n\n\t\treturn universeData;\n\t}\n\n\tstatic getManifest(): UniverseMeta[] {\n\t\tconst ids = this.listAvailable();\n\n\t\tconst result = ids.map((id) => {\n\t\t\tconst universeDir = join(dataDir, id);\n\t\t\tconst meta = JSON.parse(readFileSync(join(universeDir, 'meta.json'), 'utf-8')) as UniverseMeta;\n\n\t\t\tconst entry: UniverseMeta = {\n\t\t\t\tid: meta.id,\n\t\t\t\tname: meta.name,\n\t\t\t\tgenre: meta.genre,\n\t\t\t\tdescription: meta.description\n\t\t\t};\n\n\t\t\treturn entry;\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tstatic getAllInterests(): string[] {\n\t\tconst ids = this.listAvailable();\n\t\tconst interestSet = new Set<string>();\n\n\t\tfor (const id of ids) {\n\t\t\tconst universe = this.load(id);\n\n\t\t\tfor (const character of universe.characters) {\n\t\t\t\tfor (const interest of character.interests) {\n\t\t\t\t\tinterestSet.add(interest);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst result = Array.from(interestSet).sort();\n\n\t\treturn result;\n\t}\n\n\tstatic getAllLocations(): string[] {\n\t\tconst ids = this.listAvailable();\n\t\tconst citySet = new Set<string>();\n\n\t\tfor (const id of ids) {\n\t\t\tconst universe = this.load(id);\n\n\t\t\tfor (const address of universe.addresses) {\n\t\t\t\tif (address.city) {\n\t\t\t\t\tcitySet.add(address.city);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst result = Array.from(citySet).sort();\n\n\t\treturn result;\n\t}\n\n\tstatic listAvailable(): string[] {\n\t\tconst entries = readdirSync(dataDir, { withFileTypes: true });\n\n\t\tconst ids = entries\n\t\t\t.filter((entry) => {\n\t\t\t\tif (!entry.isDirectory()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treadFileSync(join(dataDir, entry.name, 'meta.json'), 'utf-8');\n\n\t\t\t\t\treturn true;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.map((entry) => entry.name);\n\n\t\treturn ids;\n\t}\n}\n","/**\n * Seeded pseudo-random number generator (mulberry32).\n * Returns a function that produces values in [0, 1).\n */\nexport function createRng(seed?: number): () => number {\n\tif (seed === undefined) {\n\t\treturn Math.random;\n\t}\n\n\tlet s = seed;\n\n\treturn function rng(): number {\n\t\ts += 0x6d2b79f5;\n\t\tlet z = s;\n\t\tz = Math.imul(z ^ (z >>> 15), z | 1);\n\t\tz ^= z + Math.imul(z ^ (z >>> 7), z | 61);\n\n\t\treturn ((z ^ (z >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\nexport function pickRandom<T>(array: T[], rng: () => number): T {\n\tconst index = Math.floor(rng() * array.length);\n\n\treturn array[index];\n}\n","import { pickRandom } from './random';\n\nimport type { DomainsData } from '@/types';\n\nexport class EmailGenerator {\n\tstatic generate(firstName: string, lastName: string, domains: DomainsData, rng: () => number): string {\n\t\tconst domain = pickRandom(domains.emailDomains, rng);\n\t\tconst first = firstName.toLowerCase();\n\t\tconst last = lastName.toLowerCase();\n\n\t\treturn `${first}.${last}@${domain}`;\n\t}\n\n\tstatic generateFromUsername(username: string, domains: DomainsData, rng: () => number): string {\n\t\tconst domain = pickRandom(domains.emailDomains, rng);\n\n\t\treturn `${username}@${domain}`;\n\t}\n}\n","import { pickRandom } from './random';\n\nimport type { DomainsData } from '@/types';\n\nexport class PhoneGenerator {\n\tstatic generate(domains: DomainsData, rng: () => number): string {\n\t\tif (domains.phoneEasterEggs && domains.phoneEasterEggs.length > 0 && rng() < 0.2) {\n\t\t\treturn pickRandom(domains.phoneEasterEggs, rng);\n\t\t}\n\n\t\tconst prefix = pickRandom(domains.phonePrefixes, rng);\n\t\tconst suffix = this.randomDigits(7, rng);\n\n\t\treturn `${prefix}-${suffix.slice(0, 3)}-${suffix.slice(3)}`;\n\t}\n\n\tprivate static randomDigits(count: number, rng: () => number): string {\n\t\tlet result = '';\n\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tresult += Math.floor(rng() * 10).toString();\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","import { pickRandom } from './random';\n\nimport type { DomainsData } from '@/types';\n\nexport class PasswordGenerator {\n\tstatic generate(domains: DomainsData, rng: () => number): string {\n\t\tif (domains.passwordEasterEggs && domains.passwordEasterEggs.length > 0 && rng() < 0.3) {\n\t\t\treturn pickRandom(domains.passwordEasterEggs, rng);\n\t\t}\n\n\t\treturn this.randomPassword(12, rng);\n\t}\n\n\tprivate static randomPassword(length: number, rng: () => number): string {\n\t\tconst chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$';\n\t\tlet result = '';\n\n\t\tfor (let i = 0; i < length; i++) {\n\t\t\tresult += chars[Math.floor(rng() * chars.length)];\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","import { createRng, pickRandom, EmailGenerator, PhoneGenerator, PasswordGenerator } from '@/generators';\n\nimport type { Person, UniverseData, CharacterData } from '@/types';\n\nexport class PersonFactory {\n\tstatic build(universe: UniverseData, seed?: number): Person {\n\t\tconst rng = createRng(seed);\n\n\t\tconst character = pickRandom(universe.characters, rng);\n\n\t\treturn this.buildFromCharacter(character, universe, rng);\n\t}\n\n\tstatic buildByCharacterId(characterId: string, universe: UniverseData, seed?: number): Person {\n\t\tconst character = universe.characters.find((c) => c.id === characterId);\n\n\t\tif (!character) {\n\t\t\tthrow new Error(`Character \"${characterId}\" not found in universe \"${universe.id}\"`);\n\t\t}\n\n\t\tconst rng = createRng(seed);\n\n\t\treturn this.buildFromCharacter(character, universe, rng);\n\t}\n\n\tstatic buildCanonical(characterId: string, universe: UniverseData): Person {\n\t\tconst character = universe.characters.find((c) => c.id === characterId);\n\n\t\tif (!character) {\n\t\t\tthrow new Error(`Character \"${characterId}\" not found in universe \"${universe.id}\"`);\n\t\t}\n\n\t\tconst address = character.address ?? universe.addresses[0];\n\t\tconst username = character.usernames[0];\n\t\tconst domains = character.emailDomain ? { ...universe.domains, emailDomains: [character.emailDomain] } : universe.domains;\n\t\tconst rng = createRng(0);\n\t\tconst email = EmailGenerator.generateFromUsername(username, domains, rng);\n\t\tconst phone = PhoneGenerator.generate(universe.domains, rng);\n\t\tconst password = PasswordGenerator.generate(universe.domains, rng);\n\t\tconst quote = character.quotes[0];\n\n\t\tconst person: Person = {\n\t\t\tid: crypto.randomUUID(),\n\t\t\tcharacterId: character.id,\n\t\t\tfirstName: character.firstName,\n\t\t\tlastName: character.lastName,\n\t\t\tusername,\n\t\t\temail,\n\t\t\tpassword,\n\t\t\tphone,\n\t\t\taddress: {\n\t\t\t\tstreet: address.street,\n\t\t\t\tcity: address.city,\n\t\t\t\tstate: address.state ?? '',\n\t\t\t\tzip: address.zip ?? '',\n\t\t\t\tcountry: address.country\n\t\t\t},\n\t\t\tprofession: character.profession,\n\t\t\tinterests: character.interests,\n\t\t\tquote,\n\t\t\tuniverse: universe.id,\n\t\t\tuniverseName: universe.name,\n\t\t\t...(character.symbol ? { symbol: character.symbol } : {}),\n\t\t\t...(character.color ? { color: character.color } : {})\n\t\t};\n\n\t\treturn person;\n\t}\n\n\tprivate static buildFromCharacter(character: CharacterData, universe: UniverseData, rng: () => number): Person {\n\t\tconst address = character.address ?? pickRandom(universe.addresses, rng);\n\n\t\tconst username = pickRandom(character.usernames, rng);\n\n\t\tconst domains = character.emailDomain ? { ...universe.domains, emailDomains: [character.emailDomain] } : universe.domains;\n\n\t\tconst email = EmailGenerator.generateFromUsername(username, domains, rng);\n\t\tconst phone = PhoneGenerator.generate(universe.domains, rng);\n\t\tconst password = PasswordGenerator.generate(universe.domains, rng);\n\t\tconst quote = pickRandom(character.quotes, rng);\n\n\t\tconst person: Person = {\n\t\t\tid: crypto.randomUUID(),\n\t\t\tcharacterId: character.id,\n\t\t\tfirstName: character.firstName,\n\t\t\tlastName: character.lastName,\n\t\t\tusername,\n\t\t\temail,\n\t\t\tpassword,\n\t\t\tphone,\n\t\t\taddress: {\n\t\t\t\tstreet: address.street,\n\t\t\t\tcity: address.city,\n\t\t\t\tstate: address.state ?? '',\n\t\t\t\tzip: address.zip ?? '',\n\t\t\t\tcountry: address.country\n\t\t\t},\n\t\t\tprofession: character.profession,\n\t\t\tinterests: character.interests,\n\t\t\tquote,\n\t\t\tuniverse: universe.id,\n\t\t\tuniverseName: universe.name,\n\t\t\t...(character.symbol ? { symbol: character.symbol } : {}),\n\t\t\t...(character.color ? { color: character.color } : {})\n\t\t};\n\n\t\treturn person;\n\t}\n}\n","import { UniverseLoader } from '@/universes';\n\nimport { PersonFactory } from './person-factory';\n\nimport type { Person, PersonOptions, GroupOptions, UniverseMeta, UniverseData } from '@/types';\n\nexport class PersonaBuilder {\n\tstatic buildPersonFromData(universe: UniverseData, options: Omit<PersonOptions, 'universe'>): Person {\n\t\treturn PersonFactory.build(universe, options.seed);\n\t}\n\n\tstatic buildPerson(options: PersonOptions): Person {\n\t\tconst universe = UniverseLoader.load(options.universe);\n\n\t\treturn PersonFactory.build(universe, options.seed);\n\t}\n\n\tstatic buildGroup(options: GroupOptions): Person[] {\n\t\tconst persons: Person[] = [];\n\n\t\tfor (let i = 0; i < options.size; i++) {\n\t\t\tconst seed = options.seed !== undefined ? options.seed + i : undefined;\n\n\t\t\tpersons.push(this.buildPerson({ universe: options.universe, seed }));\n\t\t}\n\n\t\treturn persons;\n\t}\n\n\tstatic listUniverses(): UniverseMeta[] {\n\t\tconst ids = UniverseLoader.listAvailable();\n\n\t\tconst result: UniverseMeta[] = ids.map((id) => {\n\t\t\tconst data = UniverseLoader.load(id);\n\n\t\t\tconst meta: UniverseMeta = {\n\t\t\t\tid,\n\t\t\t\tname: data.name,\n\t\t\t\tgenre: data.genre,\n\t\t\t\tdescription: data.description\n\t\t\t};\n\n\t\t\treturn meta;\n\t\t});\n\n\t\treturn result;\n\t}\n}\n","import { createRng } from '@/generators';\n\nimport { PersonFactory } from './person-factory';\n\nimport type { UniverseData, UniverseMeta, Person, CharacterEntry, CharacterQuery } from '@/types';\n\nexport class UniverseStore {\n\tprivate universes: UniverseData[];\n\tprivate characterIndex: CharacterEntry[];\n\n\tconstructor(universes: UniverseData[]) {\n\t\tthis.universes = universes;\n\t\tthis.characterIndex = this.buildIndex(universes);\n\t}\n\n\tget index(): CharacterEntry[] {\n\t\treturn this.characterIndex;\n\t}\n\n\tgetInterests(universeIds?: string[]): string[] {\n\t\tconst entries =\n\t\t\tuniverseIds && universeIds.length > 0\n\t\t\t\t? this.characterIndex.filter((e) => universeIds.includes(e.universeId))\n\t\t\t\t: this.characterIndex;\n\n\t\tconst interestSet = new Set<string>();\n\n\t\tfor (const entry of entries) {\n\t\t\tfor (const interest of entry.interests) {\n\t\t\t\tinterestSet.add(interest);\n\t\t\t}\n\t\t}\n\n\t\tconst result = Array.from(interestSet).sort();\n\n\t\treturn result;\n\t}\n\n\tgetUniverses(): UniverseMeta[] {\n\t\tconst result = this.universes.map((u) => {\n\t\t\tconst meta: UniverseMeta = {\n\t\t\t\tid: u.id,\n\t\t\t\tname: u.name,\n\t\t\t\tgenre: u.genre,\n\t\t\t\tdescription: u.description\n\t\t\t};\n\n\t\t\treturn meta;\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tfindCharacters(query: CharacterQuery): CharacterEntry[] {\n\t\tlet entries = this.characterIndex;\n\n\t\tif (query.universes && query.universes.length > 0) {\n\t\t\tentries = entries.filter((e) => query.universes!.includes(e.universeId));\n\t\t}\n\n\t\tif (query.name && query.name.trim().length > 0) {\n\t\t\tconst needle = query.name.toLowerCase();\n\n\t\t\tentries = entries.filter((e) => {\n\t\t\t\tconst fullName = `${e.firstName} ${e.lastName}`.toLowerCase();\n\n\t\t\t\treturn fullName.includes(needle);\n\t\t\t});\n\t\t}\n\n\t\tif (query.interests && query.interests.length > 0) {\n\t\t\tconst mode = query.interestsMode ?? 'or';\n\n\t\t\tif (mode === 'and') {\n\t\t\t\tentries = entries.filter((e) => query.interests!.every((interest) => e.interests.includes(interest)));\n\t\t\t} else {\n\t\t\t\tentries = entries.filter((e) => query.interests!.some((interest) => e.interests.includes(interest)));\n\t\t\t}\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\tpersonByCharacterId(characterId: string, seed?: number): Person {\n\t\tconst entry = this.characterIndex.find((e) => e.characterId === characterId);\n\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Character \"${characterId}\" not found in store`);\n\t\t}\n\n\t\tconst universe = this.universes.find((u) => u.id === entry.universeId);\n\n\t\tif (!universe) {\n\t\t\tthrow new Error(`Universe \"${entry.universeId}\" not found in store`);\n\t\t}\n\n\t\treturn PersonFactory.buildByCharacterId(characterId, universe, seed);\n\t}\n\n\tgeneratePersonas(query: CharacterQuery, count: number, seed?: number): Person[] {\n\t\tconst matching = this.findCharacters(query);\n\n\t\tif (matching.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst rng = createRng(seed);\n\t\tconst actualCount = Math.min(count, matching.length);\n\n\t\tconst shuffled = [...matching].sort(() => rng() - 0.5);\n\t\tconst selected = shuffled.slice(0, actualCount);\n\t\tconst personas: Person[] = [];\n\n\t\tfor (let i = 0; i < selected.length; i++) {\n\t\t\tconst entry = selected[i];\n\t\t\tconst universe = this.universes.find((u) => u.id === entry.universeId)!;\n\t\t\tconst personSeed = seed !== undefined ? seed + i : undefined;\n\n\t\t\tpersonas.push(PersonFactory.buildByCharacterId(entry.characterId, universe, personSeed));\n\t\t}\n\n\t\treturn personas;\n\t}\n\n\tprivate buildIndex(universes: UniverseData[]): CharacterEntry[] {\n\t\tconst index: CharacterEntry[] = [];\n\n\t\tfor (const universe of universes) {\n\t\t\tfor (const character of universe.characters) {\n\t\t\t\tconst entry: CharacterEntry = {\n\t\t\t\t\tcharacterId: character.id,\n\t\t\t\t\tuniverseId: universe.id,\n\t\t\t\t\tuniverseName: universe.name,\n\t\t\t\t\tfirstName: character.firstName,\n\t\t\t\t\tlastName: character.lastName,\n\t\t\t\t\tinterests: character.interests,\n\t\t\t\t\tprofession: character.profession\n\t\t\t\t};\n\n\t\t\t\tindex.push(entry);\n\t\t\t}\n\t\t}\n\n\t\treturn index;\n\t}\n}\n","import { PersonaBuilder } from '@/engine';\n\nimport type { Person, UniverseMeta, PersonOptions, GroupOptions, UniverseData } from '@/types';\n\nexport function person(options: PersonOptions): Person {\n\treturn PersonaBuilder.buildPerson(options);\n}\n\nexport function personFromData(universe: UniverseData, seed?: number): Person {\n\treturn PersonaBuilder.buildPersonFromData(universe, { seed });\n}\n\nexport function group(options: GroupOptions): Person[] {\n\treturn PersonaBuilder.buildGroup(options);\n}\n\nexport function universes(): UniverseMeta[] {\n\treturn PersonaBuilder.listUniverses();\n}\n\nexport { UniverseLoader } from '@/universes';\nexport { UniverseStore, PersonFactory } from '@/engine';\n\nexport type { Person, UniverseMeta, PersonOptions, GroupOptions } from '@/types';\nexport type { Address, UniverseData, CharacterData, AddressData, DomainsData } from '@/types';\nexport type { CharacterEntry, CharacterQuery } from '@/types';\n"],"mappings":"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","names":["require","universes"]}
package/dist/cli/cli.js CHANGED
@@ -5,17 +5,22 @@ import { Command } from "commander";
5
5
 
6
6
  // src/universes/loader.ts
7
7
  import { readFileSync, readdirSync } from "fs";
8
- import { join } from "path";
9
- var dataDir = join(import.meta.dirname, "../data");
8
+ import { createRequire } from "module";
9
+ import { dirname, join } from "path";
10
+ var require2 = createRequire(import.meta.url);
11
+ var packageRoot = dirname(require2.resolve("loredata/package.json"));
12
+ var dataDir = join(packageRoot, "data");
10
13
  var UniverseLoader = class {
11
14
  static cache = /* @__PURE__ */ new Map();
12
15
  static register(data) {
13
16
  this.cache.set(data.id, data);
14
17
  }
15
18
  static load(universeId) {
16
- const cached = this.cache.get(universeId);
17
- if (cached) {
18
- return cached;
19
+ if (process.env.NODE_ENV !== "development") {
20
+ const cached = this.cache.get(universeId);
21
+ if (cached) {
22
+ return cached;
23
+ }
19
24
  }
20
25
  const universeDir = join(dataDir, universeId);
21
26
  const meta = JSON.parse(readFileSync(join(universeDir, "meta.json"), "utf-8"));
@@ -25,6 +30,8 @@ var UniverseLoader = class {
25
30
  const universeData = {
26
31
  id: meta.id,
27
32
  name: meta.name,
33
+ genre: meta.genre,
34
+ description: meta.description,
28
35
  characters,
29
36
  addresses,
30
37
  domains
@@ -32,6 +39,49 @@ var UniverseLoader = class {
32
39
  this.cache.set(universeId, universeData);
33
40
  return universeData;
34
41
  }
42
+ static getManifest() {
43
+ const ids = this.listAvailable();
44
+ const result = ids.map((id) => {
45
+ const universeDir = join(dataDir, id);
46
+ const meta = JSON.parse(readFileSync(join(universeDir, "meta.json"), "utf-8"));
47
+ const entry = {
48
+ id: meta.id,
49
+ name: meta.name,
50
+ genre: meta.genre,
51
+ description: meta.description
52
+ };
53
+ return entry;
54
+ });
55
+ return result;
56
+ }
57
+ static getAllInterests() {
58
+ const ids = this.listAvailable();
59
+ const interestSet = /* @__PURE__ */ new Set();
60
+ for (const id of ids) {
61
+ const universe = this.load(id);
62
+ for (const character of universe.characters) {
63
+ for (const interest of character.interests) {
64
+ interestSet.add(interest);
65
+ }
66
+ }
67
+ }
68
+ const result = Array.from(interestSet).sort();
69
+ return result;
70
+ }
71
+ static getAllLocations() {
72
+ const ids = this.listAvailable();
73
+ const citySet = /* @__PURE__ */ new Set();
74
+ for (const id of ids) {
75
+ const universe = this.load(id);
76
+ for (const address of universe.addresses) {
77
+ if (address.city) {
78
+ citySet.add(address.city);
79
+ }
80
+ }
81
+ }
82
+ const result = Array.from(citySet).sort();
83
+ return result;
84
+ }
35
85
  static listAvailable() {
36
86
  const entries = readdirSync(dataDir, { withFileTypes: true });
37
87
  const ids = entries.filter((entry) => {
@@ -134,6 +184,45 @@ var PersonFactory = class {
134
184
  const rng = createRng(seed);
135
185
  return this.buildFromCharacter(character, universe, rng);
136
186
  }
187
+ static buildCanonical(characterId, universe) {
188
+ const character = universe.characters.find((c) => c.id === characterId);
189
+ if (!character) {
190
+ throw new Error(`Character "${characterId}" not found in universe "${universe.id}"`);
191
+ }
192
+ const address = character.address ?? universe.addresses[0];
193
+ const username = character.usernames[0];
194
+ const domains = character.emailDomain ? { ...universe.domains, emailDomains: [character.emailDomain] } : universe.domains;
195
+ const rng = createRng(0);
196
+ const email = EmailGenerator.generateFromUsername(username, domains, rng);
197
+ const phone = PhoneGenerator.generate(universe.domains, rng);
198
+ const password = PasswordGenerator.generate(universe.domains, rng);
199
+ const quote = character.quotes[0];
200
+ const person = {
201
+ id: crypto.randomUUID(),
202
+ characterId: character.id,
203
+ firstName: character.firstName,
204
+ lastName: character.lastName,
205
+ username,
206
+ email,
207
+ password,
208
+ phone,
209
+ address: {
210
+ street: address.street,
211
+ city: address.city,
212
+ state: address.state ?? "",
213
+ zip: address.zip ?? "",
214
+ country: address.country
215
+ },
216
+ profession: character.profession,
217
+ interests: character.interests,
218
+ quote,
219
+ universe: universe.id,
220
+ universeName: universe.name,
221
+ ...character.symbol ? { symbol: character.symbol } : {},
222
+ ...character.color ? { color: character.color } : {}
223
+ };
224
+ return person;
225
+ }
137
226
  static buildFromCharacter(character, universe, rng) {
138
227
  const address = character.address ?? pickRandom(universe.addresses, rng);
139
228
  const username = pickRandom(character.usernames, rng);
@@ -142,8 +231,6 @@ var PersonFactory = class {
142
231
  const phone = PhoneGenerator.generate(universe.domains, rng);
143
232
  const password = PasswordGenerator.generate(universe.domains, rng);
144
233
  const quote = pickRandom(character.quotes, rng);
145
- const avatarName = encodeURIComponent(`${character.firstName} ${character.lastName}`);
146
- const avatar = `https://ui-avatars.com/api/?name=${avatarName}&background=random`;
147
234
  const person = {
148
235
  id: crypto.randomUUID(),
149
236
  characterId: character.id,
@@ -165,7 +252,8 @@ var PersonFactory = class {
165
252
  quote,
166
253
  universe: universe.id,
167
254
  universeName: universe.name,
168
- avatar
255
+ ...character.symbol ? { symbol: character.symbol } : {},
256
+ ...character.color ? { color: character.color } : {}
169
257
  };
170
258
  return person;
171
259
  }
@@ -192,12 +280,13 @@ var PersonaBuilder = class {
192
280
  const ids = UniverseLoader.listAvailable();
193
281
  const result = ids.map((id) => {
194
282
  const data = UniverseLoader.load(id);
195
- const info = {
283
+ const meta = {
196
284
  id,
197
285
  name: data.name,
198
- characterCount: data.characters.length
286
+ genre: data.genre,
287
+ description: data.description
199
288
  };
200
- return info;
289
+ return meta;
201
290
  });
202
291
  return result;
203
292
  }
@@ -226,11 +315,15 @@ var UniverseStore = class {
226
315
  return result;
227
316
  }
228
317
  getUniverses() {
229
- const result = this.universes.map((u) => ({
230
- id: u.id,
231
- name: u.name,
232
- characterCount: u.characters.length
233
- }));
318
+ const result = this.universes.map((u) => {
319
+ const meta = {
320
+ id: u.id,
321
+ name: u.name,
322
+ genre: u.genre,
323
+ description: u.description
324
+ };
325
+ return meta;
326
+ });
234
327
  return result;
235
328
  }
236
329
  findCharacters(query) {
@@ -381,7 +474,7 @@ program.command("universes").description("List available universes").option("-f,
381
474
  return;
382
475
  }
383
476
  for (const u of list) {
384
- console.log(` ${u.id.padEnd(20)} ${u.name} (${u.characterCount} characters)`);
477
+ console.log(` ${u.id.padEnd(20)} ${u.name}`);
385
478
  }
386
479
  });
387
480
  program.parse();