verseluft-dnd-library 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Verseluft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Verseluft DnD Library
2
+
3
+ A small JavaScript library for DnD-style apps and websites.
4
+
5
+ ## What is inside
6
+
7
+ - `core` for shared library setup
8
+ - `modules` for game content such as spells, monsters, items, and classes
9
+ - `tools` for reusable gameplay helpers such as dice rolling
10
+ - `content` for starter data that other apps can extend
11
+
12
+ ## Documentation
13
+
14
+ - [Human docs](./docs/human/README.md)
15
+ - [AI docs](./docs/ai/README.md)
16
+ - [Dice tool](./docs/human/api/tools/dice.md)
17
+ - The published npm package includes both doc sets
18
+ - Build the standalone wiki with `npm run wiki:build`
19
+
20
+ ## Example
21
+
22
+ ```js
23
+ import { createDnDLibrary, rollDice } from "./src/index.js";
24
+
25
+ const lib = createDnDLibrary({
26
+ name: "My Campaign Toolkit"
27
+ });
28
+
29
+ console.log(lib.info());
30
+ console.log(rollDice("2d6+3"));
31
+ ```
32
+
33
+ ## Next steps
34
+
35
+ This scaffold is ready to grow into a real package with richer rules, more content packs, and optional UI helpers.
package/docs/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # Documentation Root
2
+
3
+ Choose the version that fits your needs:
4
+
5
+ - [Human docs](./human/README.md)
6
+ - [AI docs](./ai/README.md)
7
+
8
+ The published npm package includes both doc sets.
9
+ The standalone wiki is generated from the human docs only.
10
+ Run `npm run wiki:build` to create it in `wiki-site/`.
@@ -0,0 +1,25 @@
1
+ # AI Documentation
2
+
3
+ This folder is the AI-facing version of the docs.
4
+
5
+ Use it when you need:
6
+
7
+ - Precise edit boundaries
8
+ - Implementation notes
9
+ - Safer file-by-file updates
10
+
11
+ Start here:
12
+
13
+ - [Overview](./overview.md)
14
+ - [Changelog](./changelog.md)
15
+ - [API Index](./api/index.md)
16
+
17
+ Scope:
18
+
19
+ - Dice tool only
20
+
21
+ Rules:
22
+
23
+ - Update one tool file at a time
24
+ - Do not touch unrelated docs
25
+ - If the API changes, update the matching human docs too
@@ -0,0 +1,13 @@
1
+ # AI API Index
2
+
3
+ Only the dice tool is in scope for now.
4
+
5
+ ## Tools
6
+
7
+ - [Dice tool](./tools/dice.md)
8
+
9
+ ## Agent rules
10
+
11
+ - If you are editing dice behavior, edit only the dice doc first
12
+ - If the public export changes, update this index and the changelog
13
+ - Do not expand scope to new tools unless the task explicitly asks for it
@@ -0,0 +1,52 @@
1
+ # AI Dice Tool Reference
2
+
3
+ This file is the AI-specific reference for the dice tool.
4
+
5
+ ## Exported functions
6
+
7
+ - `parseDiceExpression(expression)`
8
+ - `rollDice(expression, randomFn?)`
9
+ - `averageRoll(expression)`
10
+
11
+ ## Source file
12
+
13
+ - [src/tools/dice.js](/D:/Projects/Verseluft-Core/src/tools/dice.js)
14
+
15
+ ## Behavior summary
16
+
17
+ ### `parseDiceExpression(expression)`
18
+ - Accepts strings that match `NdM` or `NdM+K` / `NdM-K`
19
+ - If `N` is omitted, it defaults to `1`
20
+ - Returns `{ count, sides, modifier }`
21
+ - Throws `Error` for invalid expressions
22
+
23
+ ### `rollDice(expression, randomFn?)`
24
+ - Parses the expression with `parseDiceExpression`
25
+ - Rolls `count` times using `randomFn`, defaulting to `Math.random`
26
+ - Returns `{ expression, rolls, modifier, total }`
27
+ - `total` includes the modifier
28
+
29
+ ### `averageRoll(expression)`
30
+ - Returns the mathematical average of the expression
31
+ - Uses the formula `count * ((sides + 1) / 2) + modifier`
32
+
33
+ ## Examples
34
+
35
+ ```js
36
+ import { parseDiceExpression, rollDice, averageRoll } from "verseluft-dnd-library";
37
+
38
+ parseDiceExpression("2d6+3");
39
+ rollDice("2d6+3");
40
+ averageRoll("2d6+3");
41
+ ```
42
+
43
+ ## Test guidance
44
+
45
+ - For deterministic tests, pass a custom `randomFn`
46
+ - Do not compare against random outputs without controlling the source
47
+
48
+ ## Edit boundaries
49
+
50
+ - If dice logic changes, update this file and the human dice doc together
51
+ - Do not modify unrelated docs when editing the dice tool
52
+ - If the export list changes, update `src/index.js` and both API index files
@@ -0,0 +1,13 @@
1
+ # AI Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial scaffold
6
+ - Dice parsing and rolling utilities added
7
+ - Starter content and tests added
8
+ - Standalone wiki generator added
9
+
10
+ AI editing notes:
11
+
12
+ - Keep changelog entries short and factual
13
+ - When behavior changes, mention the exact file and export affected
@@ -0,0 +1,23 @@
1
+ # AI Overview
2
+
3
+ Project: Verseluft DnD Library
4
+
5
+ Purpose:
6
+
7
+ - Small JavaScript toolkit for DnD-style apps and websites
8
+ - Current scope is only the dice tool
9
+
10
+ Agent guidance:
11
+
12
+ - Use the matching AI tool doc for implementation details
13
+ - Do not infer behavior from unrelated files
14
+ - Keep edits narrow and file-specific
15
+
16
+ Documentation map:
17
+
18
+ - `docs/ai/overview.md`
19
+ - `docs/ai/changelog.md`
20
+ - `docs/ai/api/index.md`
21
+ - `docs/ai/api/tools/dice.md`
22
+
23
+ Do not add new tool docs without explicit scope changes.
@@ -0,0 +1,14 @@
1
+ # Documentation
2
+
3
+ Start here:
4
+
5
+ - [Overview](./overview.md)
6
+ - [Changelog](./changelog.md)
7
+ - [API Index](./api/index.md)
8
+
9
+ Current scope:
10
+
11
+ - Dice tool only
12
+
13
+ To generate the standalone wiki site, run `npm run wiki:build`.
14
+ The built HTML files will appear in `wiki-site/`.
@@ -0,0 +1,13 @@
1
+ # API Index
2
+
3
+ This page is the central map for the library documentation.
4
+
5
+ ## Tools
6
+
7
+ - [Dice tool](./tools/dice.md)
8
+
9
+ ## Notes for editors
10
+
11
+ - Update only the matching tool file when changing one tool
12
+ - Keep cross-tool changes rare and explicit
13
+ - Add new tools as new Markdown files instead of mixing them together
@@ -0,0 +1,95 @@
1
+ # Dice Tool
2
+
3
+ The dice tool parses and rolls dice expressions for tabletop and game-style applications.
4
+
5
+ ## Purpose
6
+
7
+ Use this tool to:
8
+
9
+ - Parse expressions like `2d6+3`
10
+ - Roll dice with a deterministic or random source
11
+ - Estimate the average result of a dice expression
12
+
13
+ ## Exports
14
+
15
+ - `parseDiceExpression(expression)`
16
+ - `rollDice(expression, randomFn?)`
17
+ - `averageRoll(expression)`
18
+
19
+ ## `parseDiceExpression(expression)`
20
+
21
+ Parses a dice expression and returns a structured object.
22
+
23
+ ### Input
24
+
25
+ - `expression`: a string such as `d20`, `2d6`, or `4d8+2`
26
+
27
+ ### Output
28
+
29
+ ```js
30
+ {
31
+ count: number,
32
+ sides: number,
33
+ modifier: number
34
+ }
35
+ ```
36
+
37
+ ### Example
38
+
39
+ ```js
40
+ import { parseDiceExpression } from "verseluft-dnd-library";
41
+
42
+ const dice = parseDiceExpression("2d6+3");
43
+ // { count: 2, sides: 6, modifier: 3 }
44
+ ```
45
+
46
+ ## `rollDice(expression, randomFn?)`
47
+
48
+ Rolls dice and returns the individual rolls plus the total.
49
+
50
+ ### Input
51
+
52
+ - `expression`: a dice expression string
53
+ - `randomFn`: optional random function, defaults to `Math.random`
54
+
55
+ ### Output
56
+
57
+ ```js
58
+ {
59
+ expression: string,
60
+ rolls: number[],
61
+ modifier: number,
62
+ total: number
63
+ }
64
+ ```
65
+
66
+ ### Example
67
+
68
+ ```js
69
+ import { rollDice } from "verseluft-dnd-library";
70
+
71
+ const result = rollDice("2d6+3");
72
+ // { expression: "2d6+3", rolls: [4, 2], modifier: 3, total: 9 }
73
+ ```
74
+
75
+ ### Testing tip
76
+
77
+ Pass a custom `randomFn` when you want repeatable tests.
78
+
79
+ ## `averageRoll(expression)`
80
+
81
+ Returns the mathematical average for the expression.
82
+
83
+ ### Example
84
+
85
+ ```js
86
+ import { averageRoll } from "verseluft-dnd-library";
87
+
88
+ averageRoll("2d6+3");
89
+ // 10
90
+ ```
91
+
92
+ ## Editing rule
93
+
94
+ If you change the dice tool behavior, update only this file first.
95
+ If the public API changes, then update the central index and changelog too.
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial library scaffold
6
+ - Added dice parsing and rolling helpers
7
+ - Added starter content modules
8
+ - Added package tests and a browser playground
9
+
10
+ ## Documentation notes
11
+
12
+ - The docs are organized by topic so AI edits can stay isolated
13
+ - Dice is the only documented tool in the first pass
@@ -0,0 +1,25 @@
1
+ # Verseluft DnD Library
2
+
3
+ Verseluft DnD Library is a small JavaScript toolkit for DnD-style apps and websites.
4
+
5
+ Right now, this documentation set focuses on a single tool:
6
+
7
+ - Dice tools
8
+
9
+ ## What this docs set is for
10
+
11
+ - Human readers who want clear usage guidance
12
+ - AI readers who need a stable, file-based reference
13
+ - Safe editing, where each tool has its own isolated page
14
+
15
+ ## Documentation layout
16
+
17
+ - `docs/human/overview.md` for the big picture
18
+ - `docs/human/changelog.md` for version notes
19
+ - `docs/human/api/index.md` for the API map
20
+ - `docs/human/api/tools/dice.md` for the dice tool
21
+
22
+ ## Current scope
23
+
24
+ This docs set intentionally covers only the dice tool for now.
25
+ Other tools and modules can be added later as separate files.
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
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.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "dnd",
8
+ "rpg",
9
+ "library",
10
+ "javascript",
11
+ "browser"
12
+ ],
13
+ "type": "module",
14
+ "main": "./src/index.js",
15
+ "exports": {
16
+ ".": "./src/index.js",
17
+ "./modules/*": "./src/modules/*.js",
18
+ "./tools/*": "./src/tools/*.js",
19
+ "./content/*": "./src/content/*.js"
20
+ },
21
+ "files": [
22
+ "src",
23
+ "docs",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "scripts": {
31
+ "test": "node --test test/library.test.js",
32
+ "test:watch": "node --test --watch test/library.test.js",
33
+ "wiki:build": "node scripts/build-wiki.mjs"
34
+ }
35
+ }
@@ -0,0 +1,28 @@
1
+ export const starterContent = {
2
+ monsters: [
3
+ {
4
+ id: "goblin",
5
+ name: "Goblin",
6
+ challengeRating: 0.25,
7
+ },
8
+ ],
9
+ spells: [
10
+ {
11
+ id: "magic-missile",
12
+ name: "Magic Missile",
13
+ level: 1,
14
+ },
15
+ ],
16
+ items: [
17
+ {
18
+ id: "healing-potion",
19
+ name: "Healing Potion",
20
+ },
21
+ ],
22
+ classes: [
23
+ {
24
+ id: "fighter",
25
+ name: "Fighter",
26
+ },
27
+ ],
28
+ };
@@ -0,0 +1,17 @@
1
+ import { starterContent } from "../content/starterContent.js";
2
+
3
+ export function createDnDLibrary(options = {}) {
4
+ const libraryName = options.name ?? "DnD Library";
5
+ const content = options.content ?? starterContent;
6
+
7
+ return {
8
+ name: libraryName,
9
+ content,
10
+ info() {
11
+ return {
12
+ name: libraryName,
13
+ modules: Object.keys(content),
14
+ };
15
+ },
16
+ };
17
+ }
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { createDnDLibrary } from "./core/createDnDLibrary.js";
2
+ export { rollDice, parseDiceExpression, averageRoll } from "./tools/dice.js";
3
+ export { getMonsterById, listMonsters } from "./modules/monsters.js";
4
+ export { getSpellById, listSpells } from "./modules/spells.js";
5
+ export { getItemById, listItems } from "./modules/items.js";
6
+ export { getClassById, listClasses } from "./modules/classes.js";
7
+ export { starterContent } from "./content/starterContent.js";
@@ -0,0 +1,20 @@
1
+ const classes = [
2
+ {
3
+ id: "fighter",
4
+ name: "Fighter",
5
+ primaryAbility: "strength",
6
+ },
7
+ {
8
+ id: "wizard",
9
+ name: "Wizard",
10
+ primaryAbility: "intelligence",
11
+ },
12
+ ];
13
+
14
+ export function listClasses() {
15
+ return [...classes];
16
+ }
17
+
18
+ export function getClassById(id) {
19
+ return classes.find((characterClass) => characterClass.id === id) ?? null;
20
+ }
@@ -0,0 +1,22 @@
1
+ const items = [
2
+ {
3
+ id: "healing-potion",
4
+ name: "Healing Potion",
5
+ rarity: "common",
6
+ kind: "consumable",
7
+ },
8
+ {
9
+ id: "longsword",
10
+ name: "Longsword",
11
+ rarity: "common",
12
+ kind: "weapon",
13
+ },
14
+ ];
15
+
16
+ export function listItems() {
17
+ return [...items];
18
+ }
19
+
20
+ export function getItemById(id) {
21
+ return items.find((item) => item.id === id) ?? null;
22
+ }
@@ -0,0 +1,24 @@
1
+ const monsters = [
2
+ {
3
+ id: "goblin",
4
+ name: "Goblin",
5
+ type: "humanoid",
6
+ challengeRating: 0.25,
7
+ tags: ["small", "sneaky", "cave"],
8
+ },
9
+ {
10
+ id: "young-dragon",
11
+ name: "Young Dragon",
12
+ type: "dragon",
13
+ challengeRating: 6,
14
+ tags: ["flying", "boss", "fire"],
15
+ },
16
+ ];
17
+
18
+ export function listMonsters() {
19
+ return [...monsters];
20
+ }
21
+
22
+ export function getMonsterById(id) {
23
+ return monsters.find((monster) => monster.id === id) ?? null;
24
+ }
@@ -0,0 +1,22 @@
1
+ const spells = [
2
+ {
3
+ id: "magic-missile",
4
+ name: "Magic Missile",
5
+ level: 1,
6
+ school: "evocation",
7
+ },
8
+ {
9
+ id: "fireball",
10
+ name: "Fireball",
11
+ level: 3,
12
+ school: "evocation",
13
+ },
14
+ ];
15
+
16
+ export function listSpells() {
17
+ return [...spells];
18
+ }
19
+
20
+ export function getSpellById(id) {
21
+ return spells.find((spell) => spell.id === id) ?? null;
22
+ }
@@ -0,0 +1,35 @@
1
+ const DICE_PATTERN = /^(\d*)d(\d+)([+-]\d+)?$/i;
2
+
3
+ export function parseDiceExpression(expression) {
4
+ const cleaned = String(expression).trim();
5
+ const match = cleaned.match(DICE_PATTERN);
6
+
7
+ if (!match) {
8
+ throw new Error(`Invalid dice expression: ${expression}`);
9
+ }
10
+
11
+ const count = match[1] ? Number(match[1]) : 1;
12
+ const sides = Number(match[2]);
13
+ const modifier = match[3] ? Number(match[3]) : 0;
14
+
15
+ return { count, sides, modifier };
16
+ }
17
+
18
+ export function rollDice(expression, randomFn = Math.random) {
19
+ const { count, sides, modifier } = parseDiceExpression(expression);
20
+ let total = modifier;
21
+ const rolls = [];
22
+
23
+ for (let index = 0; index < count; index += 1) {
24
+ const roll = Math.floor(randomFn() * sides) + 1;
25
+ rolls.push(roll);
26
+ total += roll;
27
+ }
28
+
29
+ return { expression, rolls, modifier, total };
30
+ }
31
+
32
+ export function averageRoll(expression) {
33
+ const { count, sides, modifier } = parseDiceExpression(expression);
34
+ return count * ((sides + 1) / 2) + modifier;
35
+ }