verseluft-dnd-library 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 CHANGED
@@ -14,6 +14,7 @@ A small JavaScript library for DnD-style apps and websites.
14
14
  - [Human docs](./docs/human/README.md)
15
15
  - [AI docs](./docs/ai/README.md)
16
16
  - [Dice tool](./docs/human/api/tools/dice.md)
17
+ - [Name picker tool](./docs/human/api/tools/name-picker.md)
17
18
  - The published npm package includes both doc sets
18
19
  - Build the standalone wiki with `npm run wiki:build`
19
20
 
package/docs/ai/README.md CHANGED
@@ -16,7 +16,8 @@ Start here:
16
16
 
17
17
  Scope:
18
18
 
19
- - Dice tool only
19
+ - Dice tool
20
+ - Name picker tool
20
21
 
21
22
  Rules:
22
23
 
@@ -5,6 +5,7 @@ Only the dice tool is in scope for now.
5
5
  ## Tools
6
6
 
7
7
  - [Dice tool](./tools/dice.md)
8
+ - [Name picker tool](./tools/name-picker.md)
8
9
 
9
10
  ## Agent rules
10
11
 
@@ -0,0 +1,112 @@
1
+ # AI Name Picker Reference
2
+
3
+ This file is the AI-specific reference for the name picker tool.
4
+
5
+ ## Exported functions
6
+
7
+ - `pickName(options?, randomFn?)`
8
+ - `getSupportedNameRaces()`
9
+ - `listNameData()`
10
+
11
+ ## Source files
12
+
13
+ - [src/tools/name-picker.js](/D:/Projects/Verseluft-Core/src/tools/name-picker.js)
14
+ - [src/data/nameDatabase.js](/D:/Projects/Verseluft-Core/src/data/nameDatabase.js)
15
+
16
+ ## Supported races
17
+
18
+ - `human`
19
+ - `elf`
20
+ - `dwarf`
21
+ - `halfling`
22
+ - `gnome`
23
+ - `dragonborn`
24
+ - `tiefling`
25
+ - `aasimar`
26
+ - `goliath`
27
+ - `orc`
28
+
29
+ ## Behavior summary
30
+
31
+ ### `pickName(options?, randomFn?)`
32
+ - Normalizes `race` to lowercase
33
+ - Unknown races fall back to `human`
34
+ - Normalizes `sex` to `male`, `female`, or `any`
35
+ - Normalizes `mode` to `full`, `first`, `surname`, or `split`
36
+ - If `sex` is `any`, the tool randomly chooses a male or female bucket first
37
+ - Returns output based on `mode`
38
+
39
+ ### Example
40
+
41
+ ```js
42
+ import { pickName } from "verseluft-dnd-library";
43
+
44
+ const name = pickName({ race: "dwarf", sex: "male" });
45
+ /*
46
+ {
47
+ race: "dwarf",
48
+ sex: "male",
49
+ firstName: "Baern",
50
+ surname: "Amberaxe",
51
+ fullName: "Baern Amberaxe"
52
+ }
53
+ */
54
+ ```
55
+
56
+ ### Output modes
57
+
58
+ - `full`: returns `race`, `sex`, `firstName`, `surname`, and `fullName`
59
+ - `first`: returns `race`, `sex`, and `firstName`
60
+ - `surname`: returns `race`, `sex`, and `surname`
61
+ - `split`: returns `race`, `sex`, `firstName`, and `surname`
62
+
63
+ ### `getSupportedNameRaces()`
64
+ - Returns the supported species keys from the internal database
65
+
66
+ ### Example
67
+
68
+ ```js
69
+ import { getSupportedNameRaces } from "verseluft-dnd-library";
70
+
71
+ getSupportedNameRaces();
72
+ // ["human", "elf", "dwarf", "halfling", "gnome", "dragonborn", "tiefling", "aasimar", "goliath", "orc"]
73
+ ```
74
+
75
+ ### `listNameData()`
76
+ - Returns a cloned copy of the whole database
77
+ - Intended for inspection, not mutation of the source data
78
+
79
+ ### Example
80
+
81
+ ```js
82
+ import { listNameData } from "verseluft-dnd-library";
83
+
84
+ const data = listNameData();
85
+ // data.human.male.first[0] === "Aren"
86
+ ```
87
+
88
+ ## Data model
89
+
90
+ The database is organized as:
91
+
92
+ ```js
93
+ {
94
+ [race]: {
95
+ male: { first: string[], surname: string[] },
96
+ female: { first: string[], surname: string[] }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ## Test guidance
102
+
103
+ - Use a deterministic `randomFn` when testing
104
+ - Verify the race fallback path for unknown values
105
+ - Verify that `sex: "any"` can select either bucket
106
+ - Verify the output mode behavior for `first`, `surname`, `split`, and `full`
107
+
108
+ ## Edit boundaries
109
+
110
+ - If name logic changes, update this file and the human version together
111
+ - If the database changes, update examples or supported race notes
112
+ - Do not touch the dice docs when editing the name picker
@@ -7,6 +7,23 @@
7
7
  - Starter content and tests added
8
8
  - Standalone wiki generator added
9
9
 
10
+ ## 0.1.1
11
+
12
+ - Added the name picker tool
13
+ - Added the internal name database for the supported species
14
+ - Added human and AI docs for the name picker tool
15
+
16
+ ## 0.1.2
17
+
18
+ - Added output modes for the name picker tool
19
+ - The picker can now return first-only, surname-only, split, or full-name output
20
+
21
+ ## 0.2.0
22
+
23
+ - Added the name picker tool as a packaged database-backed feature
24
+ - Added first-name-only, surname-only, split, and full output modes
25
+ - Expanded docs for both human and AI readers
26
+
10
27
  AI editing notes:
11
28
 
12
29
  - Keep changelog entries short and factual
@@ -5,7 +5,7 @@ Project: Verseluft DnD Library
5
5
  Purpose:
6
6
 
7
7
  - Small JavaScript toolkit for DnD-style apps and websites
8
- - Current scope is only the dice tool
8
+ - Current scope includes the dice tool and the name picker tool
9
9
 
10
10
  Agent guidance:
11
11
 
@@ -19,5 +19,6 @@ Documentation map:
19
19
  - `docs/ai/changelog.md`
20
20
  - `docs/ai/api/index.md`
21
21
  - `docs/ai/api/tools/dice.md`
22
+ - `docs/ai/api/tools/name-picker.md`
22
23
 
23
24
  Do not add new tool docs without explicit scope changes.
@@ -8,7 +8,8 @@ Start here:
8
8
 
9
9
  Current scope:
10
10
 
11
- - Dice tool only
11
+ - Dice tool
12
+ - Name picker tool
12
13
 
13
14
  To generate the standalone wiki site, run `npm run wiki:build`.
14
15
  The built HTML files will appear in `wiki-site/`.
@@ -5,6 +5,7 @@ This page is the central map for the library documentation.
5
5
  ## Tools
6
6
 
7
7
  - [Dice tool](./tools/dice.md)
8
+ - [Name picker tool](./tools/name-picker.md)
8
9
 
9
10
  ## Notes for editors
10
11
 
@@ -0,0 +1,125 @@
1
+ # Name Picker Tool
2
+
3
+ The name picker tool generates fantasy names based on race and sex.
4
+
5
+ ## Purpose
6
+
7
+ Use this tool to:
8
+
9
+ - Pick a first name and surname
10
+ - Generate names for common DnD species
11
+ - Keep the output simple and readable for games, apps, or character tools
12
+ - Return first-only, surname-only, split, or full-name output
13
+
14
+ ## Supported races
15
+
16
+ The current built-in database supports:
17
+
18
+ - `human`
19
+ - `elf`
20
+ - `dwarf`
21
+ - `halfling`
22
+ - `gnome`
23
+ - `dragonborn`
24
+ - `tiefling`
25
+ - `aasimar`
26
+ - `goliath`
27
+ - `orc`
28
+
29
+ ## Exports
30
+
31
+ - `pickName(options?, randomFn?)`
32
+ - `getSupportedNameRaces()`
33
+ - `listNameData()`
34
+
35
+ ## `pickName(options?, randomFn?)`
36
+
37
+ Returns a generated name for the selected race and sex.
38
+
39
+ ### Input
40
+
41
+ - `options.race`: the race/species name, such as `human`, `elf`, or `dwarf`
42
+ - `options.sex`: `male`, `female`, or `any`
43
+ - `options.mode`: `full`, `first`, `surname`, or `split`
44
+ - `randomFn`: optional random function, defaults to `Math.random`
45
+
46
+ ### Output
47
+
48
+ ```js
49
+ {
50
+ race: string,
51
+ sex: string,
52
+ firstName: string,
53
+ surname: string,
54
+ fullName: string
55
+ }
56
+ ```
57
+
58
+ ### Example
59
+
60
+ ```js
61
+ import { pickName } from "verseluft-dnd-library";
62
+
63
+ const name = pickName({ race: "elf", sex: "female" });
64
+ // { race: "elf", sex: "female", firstName: "Ayla", surname: "Galanodel", fullName: "Ayla Galanodel" }
65
+ ```
66
+
67
+ ### Example result
68
+
69
+ ```js
70
+ {
71
+ race: "elf",
72
+ sex: "female",
73
+ firstName: "Ayla",
74
+ surname: "Galanodel",
75
+ fullName: "Ayla Galanodel"
76
+ }
77
+ ```
78
+
79
+ ### Output modes
80
+
81
+ ```js
82
+ pickName({ race: "elf", sex: "female", mode: "first" });
83
+ // { race: "elf", sex: "female", firstName: "Ayla" }
84
+
85
+ pickName({ race: "elf", sex: "female", mode: "surname" });
86
+ // { race: "elf", sex: "female", surname: "Galanodel" }
87
+
88
+ pickName({ race: "elf", sex: "female", mode: "split" });
89
+ // { race: "elf", sex: "female", firstName: "Ayla", surname: "Galanodel" }
90
+
91
+ pickName({ race: "elf", sex: "female", mode: "full" });
92
+ // { race: "elf", sex: "female", firstName: "Ayla", surname: "Galanodel", fullName: "Ayla Galanodel" }
93
+ ```
94
+
95
+ If you do not pass `mode`, the default is `full`.
96
+
97
+ ## `getSupportedNameRaces()`
98
+
99
+ Returns the supported race list used by the current database.
100
+
101
+ ### Example
102
+
103
+ ```js
104
+ import { getSupportedNameRaces } from "verseluft-dnd-library";
105
+
106
+ getSupportedNameRaces();
107
+ // ["human", "elf", "dwarf", "halfling", "gnome", "dragonborn", "tiefling", "aasimar", "goliath", "orc"]
108
+ ```
109
+
110
+ ## `listNameData()`
111
+
112
+ Returns a copy of the full internal name database.
113
+
114
+ ### Example
115
+
116
+ ```js
117
+ import { listNameData } from "verseluft-dnd-library";
118
+
119
+ const data = listNameData();
120
+ ```
121
+
122
+ ## Editing rule
123
+
124
+ If you change the name picker behavior, update this file and the AI version together.
125
+ If the public API changes, update the central index and changelog too.
@@ -7,6 +7,23 @@
7
7
  - Added starter content modules
8
8
  - Added package tests and a browser playground
9
9
 
10
+ ## 0.1.1
11
+
12
+ - Added the name picker tool
13
+ - Added a small built-in name database for 2024-style DnD species
14
+ - Added human and AI documentation for the new tool
15
+
16
+ ## 0.1.2
17
+
18
+ - Added output modes for the name picker tool
19
+ - The picker can now return first-only, surname-only, split, or full-name output
20
+
21
+ ## 0.2.0
22
+
23
+ - Added the name picker tool as a packaged database-backed feature
24
+ - Added first-name-only, surname-only, split, and full output modes
25
+ - Expanded docs for both human and AI readers
26
+
10
27
  ## Documentation notes
11
28
 
12
29
  - The docs are organized by topic so AI edits can stay isolated
@@ -5,6 +5,7 @@ Verseluft DnD Library is a small JavaScript toolkit for DnD-style apps and websi
5
5
  Right now, this documentation set focuses on a single tool:
6
6
 
7
7
  - Dice tools
8
+ - Name picker tools
8
9
 
9
10
  ## What this docs set is for
10
11
 
@@ -18,8 +19,9 @@ Right now, this documentation set focuses on a single tool:
18
19
  - `docs/human/changelog.md` for version notes
19
20
  - `docs/human/api/index.md` for the API map
20
21
  - `docs/human/api/tools/dice.md` for the dice tool
22
+ - `docs/human/api/tools/name-picker.md` for the name picker tool
21
23
 
22
24
  ## Current scope
23
25
 
24
- This docs set intentionally covers only the dice tool for now.
26
+ This docs set intentionally covers only the current tools for now.
25
27
  Other tools and modules can be added later as separate files.
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "verseluft-dnd-library",
3
- "version": "0.1.0",
4
- "description": "A small DnD utility library with reusable content modules and dice tools for apps and websites.",
3
+ "version": "0.2.0",
4
+ "description": "A small DnD utility library with reusable content modules, dice tools, and a name picker for apps and websites.",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "dnd",
8
8
  "rpg",
9
9
  "library",
10
+ "name-generator",
10
11
  "javascript",
11
12
  "browser"
12
13
  ],
@@ -16,7 +17,8 @@
16
17
  ".": "./src/index.js",
17
18
  "./modules/*": "./src/modules/*.js",
18
19
  "./tools/*": "./src/tools/*.js",
19
- "./content/*": "./src/content/*.js"
20
+ "./content/*": "./src/content/*.js",
21
+ "./data/*": "./src/data/*.js"
20
22
  },
21
23
  "files": [
22
24
  "src",
@@ -0,0 +1,104 @@
1
+ export const nameDatabase = {
2
+ human: {
3
+ male: {
4
+ first: ["Aren", "Darin", "Gavin", "Joran", "Theron"],
5
+ surname: ["Ashford", "Black", "Rivers", "Stone", "Vale"],
6
+ },
7
+ female: {
8
+ first: ["Elira", "Mira", "Nora", "Selene", "Talia"],
9
+ surname: ["Ashford", "Dawn", "Frost", "Hart", "Vale"],
10
+ },
11
+ },
12
+ elf: {
13
+ male: {
14
+ first: ["Aelar", "Erevan", "Faelar", "Laucian", "Soveliss"],
15
+ surname: ["Amakiir", "Galanodel", "Liadon", "Nailo", "Xiloscient"],
16
+ },
17
+ female: {
18
+ first: ["Ayla", "Dayereth", "Keyleth", "Leshanna", "Thia"],
19
+ surname: ["Amakiir", "Galanodel", "Liadon", "Nailo", "Xiloscient"],
20
+ },
21
+ },
22
+ dwarf: {
23
+ male: {
24
+ first: ["Baern", "Dain", "Eberk", "Harbek", "Orsik"],
25
+ surname: ["Amberaxe", "Balderk", "Fireforge", "Ironfist", "Strakeln"],
26
+ },
27
+ female: {
28
+ first: ["Amber", "Diesa", "Eldeth", "Falkrunn", "Vistra"],
29
+ surname: ["Amberaxe", "Balderk", "Fireforge", "Ironfist", "Strakeln"],
30
+ },
31
+ },
32
+ halfling: {
33
+ male: {
34
+ first: ["Ander", "Corrin", "Eldon", "Milo", "Osborn"],
35
+ surname: ["Brushgather", "Goodbarrel", "Greenbottle", "High-hill", "Tealeaf"],
36
+ },
37
+ female: {
38
+ first: ["Callie", "Lavinia", "Merla", "Piper", "Rosie"],
39
+ surname: ["Brushgather", "Goodbarrel", "Greenbottle", "High-hill", "Tealeaf"],
40
+ },
41
+ },
42
+ gnome: {
43
+ male: {
44
+ first: ["Alston", "Boddynock", "Dimble", "Fonkin", "Wrenn"],
45
+ surname: ["Beren", "Nackle", "Murnig", "Scheppen", "Timbers"],
46
+ },
47
+ female: {
48
+ first: ["Bree", "Caramip", "Ella", "Lidda", "Nissa"],
49
+ surname: ["Beren", "Nackle", "Murnig", "Scheppen", "Timbers"],
50
+ },
51
+ },
52
+ dragonborn: {
53
+ male: {
54
+ first: ["Arjhan", "Balasar", "Donaar", "Ghesh", "Kriv"],
55
+ surname: ["Clethtinthiallor", "Daardendrian", "Delmirev", "Norixius", "Saariv"],
56
+ },
57
+ female: {
58
+ first: ["Akra", "Biri", "Kava", "Sora", "Zykro"],
59
+ surname: ["Clethtinthiallor", "Daardendrian", "Delmirev", "Norixius", "Saariv"],
60
+ },
61
+ },
62
+ tiefling: {
63
+ male: {
64
+ first: ["Akmenos", "Barakas", "Mordai", "Skamos", "Zef"],
65
+ surname: ["Ashspear", "Nightbloom", "Silverthread", "Duskwalker", "Stormeye"],
66
+ },
67
+ female: {
68
+ first: ["Astra", "Bryseis", "Kallista", "Lerissa", "Vesper"],
69
+ surname: ["Ashspear", "Nightbloom", "Silverthread", "Duskwalker", "Stormeye"],
70
+ },
71
+ },
72
+ aasimar: {
73
+ male: {
74
+ first: ["Aurel", "Caelum", "Irian", "Mikael", "Seraph"],
75
+ surname: ["Brightsoul", "Dawnwatch", "Starfall", "Lightborn", "Sunveil"],
76
+ },
77
+ female: {
78
+ first: ["Arielle", "Celeste", "Ilyra", "Mirael", "Seren"],
79
+ surname: ["Brightsoul", "Dawnwatch", "Starfall", "Lightborn", "Sunveil"],
80
+ },
81
+ },
82
+ goliath: {
83
+ male: {
84
+ first: ["Aukan", "Eglath", "Gathak", "Keth", "Thuun"],
85
+ surname: ["Cloudseer", "Ironpeak", "Stoneward", "Thunderborn", "Wolfrun"],
86
+ },
87
+ female: {
88
+ first: ["Ena", "Gunna", "Helli", "Orla", "Zanna"],
89
+ surname: ["Cloudseer", "Ironpeak", "Stoneward", "Thunderborn", "Wolfrun"],
90
+ },
91
+ },
92
+ orc: {
93
+ male: {
94
+ first: ["Dench", "Gell", "Henk", "Ront", "Shump"],
95
+ surname: ["Bloodjaw", "Ironhide", "Skullcrusher", "Stonefang", "Warborn"],
96
+ },
97
+ female: {
98
+ first: ["Baggi", "Emen", "Engong", "Myev", "Ovak"],
99
+ surname: ["Bloodjaw", "Ironhide", "Skullcrusher", "Stonefang", "Warborn"],
100
+ },
101
+ },
102
+ };
103
+
104
+ export const supportedSpecies = Object.keys(nameDatabase);
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { createDnDLibrary } from "./core/createDnDLibrary.js";
2
2
  export { rollDice, parseDiceExpression, averageRoll } from "./tools/dice.js";
3
+ export { pickName, getSupportedNameRaces, listNameData } from "./tools/name-picker.js";
3
4
  export { getMonsterById, listMonsters } from "./modules/monsters.js";
4
5
  export { getSpellById, listSpells } from "./modules/spells.js";
5
6
  export { getItemById, listItems } from "./modules/items.js";
@@ -0,0 +1,83 @@
1
+ import { nameDatabase, supportedSpecies } from "../data/nameDatabase.js";
2
+
3
+ function normalizeSpecies(race) {
4
+ const value = String(race ?? "human").trim().toLowerCase();
5
+ return supportedSpecies.includes(value) ? value : "human";
6
+ }
7
+
8
+ function normalizeSex(sex) {
9
+ const value = String(sex ?? "any").trim().toLowerCase();
10
+ if (value === "male" || value === "female") {
11
+ return value;
12
+ }
13
+ return "any";
14
+ }
15
+
16
+ function normalizeMode(mode) {
17
+ const value = String(mode ?? "full").trim().toLowerCase();
18
+ if (value === "first" || value === "surname" || value === "split" || value === "full") {
19
+ return value;
20
+ }
21
+ return "full";
22
+ }
23
+
24
+ function pickFrom(items, randomFn) {
25
+ if (!Array.isArray(items) || items.length === 0) {
26
+ throw new Error("No names available for the selected category.");
27
+ }
28
+
29
+ const index = Math.floor(randomFn() * items.length);
30
+ return items[index];
31
+ }
32
+
33
+ function pickSexBucket(speciesData, sex, randomFn) {
34
+ if (sex === "male" || sex === "female") {
35
+ return speciesData[sex];
36
+ }
37
+
38
+ return randomFn() < 0.5 ? speciesData.male : speciesData.female;
39
+ }
40
+
41
+ export function getSupportedNameRaces() {
42
+ return [...supportedSpecies];
43
+ }
44
+
45
+ export function pickName(options = {}, randomFn = Math.random) {
46
+ const race = normalizeSpecies(options.race);
47
+ const sex = normalizeSex(options.sex);
48
+ const mode = normalizeMode(options.mode);
49
+ const speciesData = nameDatabase[race] ?? nameDatabase.human;
50
+ const sexBucket = pickSexBucket(speciesData, sex, randomFn);
51
+ const firstName = pickFrom(sexBucket.first, randomFn);
52
+ const surname = pickFrom(sexBucket.surname, randomFn);
53
+
54
+ const result = {
55
+ race,
56
+ sex,
57
+ };
58
+
59
+ if (mode === "first") {
60
+ result.firstName = firstName;
61
+ return result;
62
+ }
63
+
64
+ if (mode === "surname") {
65
+ result.surname = surname;
66
+ return result;
67
+ }
68
+
69
+ if (mode === "split") {
70
+ result.firstName = firstName;
71
+ result.surname = surname;
72
+ return result;
73
+ }
74
+
75
+ result.firstName = firstName;
76
+ result.surname = surname;
77
+ result.fullName = `${firstName} ${surname}`;
78
+ return result;
79
+ }
80
+
81
+ export function listNameData() {
82
+ return structuredClone(nameDatabase);
83
+ }