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.
- package/README.md +33 -5
- package/data/breaking-bad/characters.json +36 -7
- package/data/breaking-bad/meta.json +3 -1
- package/data/fast-and-furious/characters.json +26 -7
- package/data/fast-and-furious/meta.json +3 -1
- package/data/friends/characters.json +128 -22
- package/data/friends/meta.json +6 -1
- package/data/game-of-thrones/characters.json +45 -15
- package/data/game-of-thrones/meta.json +3 -1
- package/data/harry-potter/characters.json +30 -7
- package/data/harry-potter/meta.json +3 -1
- package/data/house-md/characters.json +58 -15
- package/data/house-md/meta.json +6 -1
- package/data/lost/characters.json +120 -24
- package/data/lost/meta.json +6 -1
- package/data/matrix/characters.json +112 -24
- package/data/matrix/meta.json +6 -1
- package/data/peaky-blinders/characters.json +33 -11
- package/data/peaky-blinders/meta.json +6 -1
- package/data/sherlock/characters.json +99 -19
- package/data/sherlock/meta.json +6 -1
- package/data/the-office/characters.json +55 -15
- package/data/the-office/meta.json +3 -1
- package/dist/browser.cjs +107 -8
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +7 -3
- package/dist/browser.d.ts +7 -3
- package/dist/browser.js +103 -8
- package/dist/browser.js.map +1 -1
- package/dist/cli/cli.cjs +109 -16
- package/dist/cli/cli.cjs.map +1 -1
- package/dist/cli/cli.js +110 -17
- package/dist/cli/cli.js.map +1 -1
- package/dist/index.cjs +110 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -4
- package/dist/index.d.ts +14 -4
- package/dist/index.js +110 -16
- package/dist/index.js.map +1 -1
- package/dist/{universe-store-DJrm7bP7.d.cts → universe-store-Del1UHgn.d.cts} +11 -9
- package/dist/{universe-store-DJrm7bP7.d.ts → universe-store-Del1UHgn.d.ts} +11 -9
- 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
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
285
|
+
const meta = {
|
|
198
286
|
id,
|
|
199
287
|
name: data.name,
|
|
200
|
-
|
|
288
|
+
genre: data.genre,
|
|
289
|
+
description: data.description
|
|
201
290
|
};
|
|
202
|
-
return
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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}
|
|
479
|
+
console.log(` ${u.id.padEnd(20)} ${u.name}`);
|
|
387
480
|
}
|
|
388
481
|
});
|
|
389
482
|
program.parse();
|
package/dist/cli/cli.cjs.map
CHANGED
|
@@ -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":"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","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 {
|
|
9
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
|
283
|
+
const meta = {
|
|
196
284
|
id,
|
|
197
285
|
name: data.name,
|
|
198
|
-
|
|
286
|
+
genre: data.genre,
|
|
287
|
+
description: data.description
|
|
199
288
|
};
|
|
200
|
-
return
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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}
|
|
477
|
+
console.log(` ${u.id.padEnd(20)} ${u.name}`);
|
|
385
478
|
}
|
|
386
479
|
});
|
|
387
480
|
program.parse();
|