@archivist-gg/dnd5e 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/LICENSES/SRD.md +25 -6
  2. package/dist/armor/armor.codec.js +4 -3
  3. package/dist/armor/armor.parser.js +3 -2
  4. package/dist/armor/armor.schema.d.ts +4 -0
  5. package/dist/armor/armor.schema.js +2 -1
  6. package/dist/armor/armor.types.d.ts +8 -0
  7. package/dist/background/background.codec.js +7 -6
  8. package/dist/background/background.parser.js +6 -5
  9. package/dist/background/background.schema.d.ts +26 -0
  10. package/dist/background/background.schema.js +5 -4
  11. package/dist/background/background.types.d.ts +22 -0
  12. package/dist/{chunk-4TIJKN6O.js → chunk-23LZLBJK.js} +1 -1
  13. package/dist/{chunk-AFKTJXRA.js → chunk-2DFENQIJ.js} +18 -0
  14. package/dist/chunk-2FRUSVZS.js +489 -0
  15. package/dist/chunk-4FWNK4FD.js +159 -0
  16. package/dist/chunk-4U55VNC7.js +22 -0
  17. package/dist/{chunk-4JHOHWBI.js → chunk-4XTNPNQB.js} +1 -1
  18. package/dist/{chunk-HCP3GMHS.js → chunk-5N2GW72F.js} +15 -2
  19. package/dist/{chunk-42Y5IJKT.js → chunk-5U5FFFA3.js} +5 -1
  20. package/dist/{chunk-4F6AIZ36.js → chunk-5XMQPTHD.js} +1 -1
  21. package/dist/{chunk-GJOI2GHP.js → chunk-6GNXYAYP.js} +1 -1
  22. package/dist/chunk-6PKV5V2H.js +178 -0
  23. package/dist/{chunk-66XXW2Z2.js → chunk-6PXTNCBP.js} +2 -2
  24. package/dist/chunk-6X2NV665.js +33 -0
  25. package/dist/{chunk-LV4SNWZQ.js → chunk-6X4KUDMU.js} +8 -1
  26. package/dist/{chunk-ZMR77DDL.js → chunk-AW3EHF3L.js} +1 -1
  27. package/dist/chunk-CJYCVNW2.js +7 -0
  28. package/dist/chunk-COEOOQX2.js +133 -0
  29. package/dist/{chunk-XTIQWSFM.js → chunk-D5U7GMPX.js} +1 -1
  30. package/dist/{chunk-RAWJTA45.js → chunk-DEEHHLAP.js} +1 -1
  31. package/dist/{chunk-6IRYTM5V.js → chunk-DPUJDOKV.js} +119 -28
  32. package/dist/{chunk-73HOFQQ5.js → chunk-ESS46GR4.js} +1 -1
  33. package/dist/chunk-EVMUJYUD.js +195 -0
  34. package/dist/{chunk-2BWAID3Y.js → chunk-FDBWYMZK.js} +29 -3
  35. package/dist/{chunk-K6Q5OUGK.js → chunk-GFZ7PITJ.js} +86 -10
  36. package/dist/chunk-H7KBLR4X.js +81 -0
  37. package/dist/{chunk-L7E2UT5A.js → chunk-HB7XZXDK.js} +5 -1
  38. package/dist/{chunk-BIK4DFWK.js → chunk-HJ5EEZ47.js} +1 -1
  39. package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
  40. package/dist/{chunk-TN6ZLTNU.js → chunk-I2NDWFDD.js} +51 -4
  41. package/dist/{chunk-RSDRMH54.js → chunk-IMZFOOVL.js} +1 -1
  42. package/dist/{chunk-3DZ74AKK.js → chunk-IN4M4X7P.js} +8 -2
  43. package/dist/chunk-IUAAXXNO.js +58 -0
  44. package/dist/{chunk-XJNECUPT.js → chunk-JC5UNIVM.js} +19 -0
  45. package/dist/chunk-JJ46T5XX.js +297 -0
  46. package/dist/{chunk-SE7AZDR3.js → chunk-JRDPUDT5.js} +29 -3
  47. package/dist/{chunk-24GFQU7T.js → chunk-JSZ4H6AQ.js} +1 -1
  48. package/dist/chunk-KU3YLF6D.js +106 -0
  49. package/dist/{chunk-5INR4NSL.js → chunk-LDDCNPHD.js} +1 -1
  50. package/dist/{chunk-5ZBE4CWV.js → chunk-LMBAEIP6.js} +1 -1
  51. package/dist/chunk-LVQLWRCU.js +15 -0
  52. package/dist/chunk-MI7P3NX3.js +161 -0
  53. package/dist/chunk-O4SRD3J5.js +165 -0
  54. package/dist/{chunk-D3CLOBE3.js → chunk-OSQFPGLM.js} +44 -8
  55. package/dist/chunk-OZP6JFGT.js +162 -0
  56. package/dist/{chunk-NAEPSZAM.js → chunk-PDJSUH24.js} +24 -2
  57. package/dist/chunk-PP2UHLBA.js +50 -0
  58. package/dist/chunk-PVCEA4HZ.js +6 -0
  59. package/dist/{chunk-XLSVNUTK.js → chunk-R3VAKWKW.js} +1 -1
  60. package/dist/chunk-RIXI5D3R.js +89 -0
  61. package/dist/chunk-RLGQ5PZZ.js +16 -0
  62. package/dist/chunk-S7REMWW4.js +21 -0
  63. package/dist/{chunk-7VJ6BOA5.js → chunk-SMLXZUBH.js} +9 -1
  64. package/dist/{chunk-NXSWH3AE.js → chunk-T26ZVCL5.js} +1 -1
  65. package/dist/chunk-TOM66P2P.js +38 -0
  66. package/dist/chunk-UA5KS2PL.js +26 -0
  67. package/dist/{chunk-N6N74K6X.js → chunk-UBBKSYBY.js} +1 -1
  68. package/dist/{chunk-353WXS6S.js → chunk-UIRMA5KM.js} +1 -1
  69. package/dist/{chunk-GE4TLPWM.js → chunk-US5IKYCW.js} +1 -1
  70. package/dist/chunk-UXPGW4TI.js +101 -0
  71. package/dist/chunk-VQAXTIBW.js +100 -0
  72. package/dist/{chunk-WP2CX2JO.js → chunk-WELIKLZF.js} +22 -1
  73. package/dist/chunk-WPWOHGPI.js +172 -0
  74. package/dist/{chunk-GZ46OVGZ.js → chunk-WSVMLNTO.js} +1 -1
  75. package/dist/{chunk-4LZJXJJS.js → chunk-WXWPK3LI.js} +6 -2
  76. package/dist/{chunk-ZZGEVJKX.js → chunk-XBQMCEI2.js} +1 -1
  77. package/dist/{chunk-PHQRGDRQ.js → chunk-XGOFP7AQ.js} +5 -1
  78. package/dist/chunk-XJ2F2WO6.js +8 -0
  79. package/dist/chunk-XQCSGNXE.js +1580 -0
  80. package/dist/chunk-YBLDE5CG.js +42 -0
  81. package/dist/{chunk-JEYQU3JT.js → chunk-YHYTW5QA.js} +2 -18
  82. package/dist/{chunk-TLYDBT5F.js → chunk-YJJTISBN.js} +64 -7
  83. package/dist/{chunk-PZOWBI3B.js → chunk-YLL2QXY2.js} +7 -2
  84. package/dist/{chunk-MDEJHMKK.js → chunk-ZONJ66NE.js} +26 -5
  85. package/dist/class/class.codec.js +12 -9
  86. package/dist/class/class.parser.js +11 -8
  87. package/dist/class/class.schema.d.ts +55 -3
  88. package/dist/class/class.schema.js +9 -7
  89. package/dist/class/class.types.d.ts +48 -3
  90. package/dist/condition/condition.codec.d.ts +9 -0
  91. package/dist/condition/condition.entity-type.d.ts +2 -0
  92. package/dist/condition/condition.parser.d.ts +13 -0
  93. package/dist/condition/condition.parser.js +7 -0
  94. package/dist/condition/condition.schema.d.ts +29 -0
  95. package/dist/condition/condition.types.d.ts +15 -0
  96. package/dist/condition/condition.types.js +0 -0
  97. package/dist/dnd/constants.d.ts +22 -0
  98. package/dist/dnd/constants.js +9 -3
  99. package/dist/dnd/formula-tags.js +2 -2
  100. package/dist/dnd/math.d.ts +26 -0
  101. package/dist/dnd/math.js +12 -2
  102. package/dist/dnd/prose-tags.js +1 -1
  103. package/dist/dnd/resource-formula.d.ts +36 -10
  104. package/dist/dnd/resource-formula.js +10 -155
  105. package/dist/dnd/srd-tag-converter.js +3 -3
  106. package/dist/dnd/tag-grammar.js +1 -1
  107. package/dist/dnd/warn-once.d.ts +2 -0
  108. package/dist/dnd/warn-once.js +8 -0
  109. package/dist/entities/base-item-resolver.js +2 -2
  110. package/dist/entities/slug.d.ts +13 -0
  111. package/dist/entities/slug.js +5 -3
  112. package/dist/entities/wikilink-display.d.ts +3 -0
  113. package/dist/entities/wikilink-display.js +10 -0
  114. package/dist/feat/feat.category-codes.d.ts +32 -0
  115. package/dist/feat/feat.category-codes.js +14 -0
  116. package/dist/feat/feat.codec.js +7 -5
  117. package/dist/feat/feat.parser.js +6 -4
  118. package/dist/feat/feat.schema.d.ts +230 -0
  119. package/dist/feat/feat.schema.js +5 -3
  120. package/dist/feat/feat.types.d.ts +37 -1
  121. package/dist/index.js +2033 -6907
  122. package/dist/item/item.codec.js +4 -3
  123. package/dist/item/item.enrichment.js +4 -4
  124. package/dist/item/item.parser.js +3 -2
  125. package/dist/item/item.schema.d.ts +69 -3
  126. package/dist/item/item.schema.js +2 -1
  127. package/dist/item/item.types.d.ts +69 -3
  128. package/dist/monster/monster.codec.d.ts +7 -6
  129. package/dist/monster/monster.enrichment.js +4 -4
  130. package/dist/monster/monster.format.d.ts +135 -0
  131. package/dist/monster/monster.format.js +78 -0
  132. package/dist/monster/monster.parser.d.ts +6 -1
  133. package/dist/monster/monster.parser.js +10 -1
  134. package/dist/monster/monster.schema.d.ts +369 -0
  135. package/dist/monster/monster.schema.js +34 -0
  136. package/dist/monster/monster.types.d.ts +200 -14
  137. package/dist/optional-feature/optional-feature.codec.js +6 -5
  138. package/dist/optional-feature/optional-feature.parser.js +5 -4
  139. package/dist/optional-feature/optional-feature.schema.d.ts +176 -0
  140. package/dist/optional-feature/optional-feature.schema.js +4 -3
  141. package/dist/pack.d.ts +1 -1
  142. package/dist/pc/conditions.constants.d.ts +0 -1
  143. package/dist/pc/conditions.constants.js +1 -3
  144. package/dist/pc/decision-recognizer.js +1 -1
  145. package/dist/pc/pc.additional-spells.d.ts +47 -0
  146. package/dist/pc/pc.additional-spells.js +23 -0
  147. package/dist/pc/pc.asi-flatten.d.ts +18 -0
  148. package/dist/pc/pc.decision-engine.d.ts +105 -17
  149. package/dist/pc/pc.decision-engine.js +28 -12
  150. package/dist/pc/pc.defense-normalize.d.ts +11 -0
  151. package/dist/pc/pc.defense-normalize.js +6 -0
  152. package/dist/pc/pc.equipment.d.ts +30 -1
  153. package/dist/pc/pc.equipment.js +12 -8
  154. package/dist/pc/pc.feature-effects.d.ts +225 -11
  155. package/dist/pc/pc.feature-effects.js +21 -5
  156. package/dist/pc/pc.parser.js +2 -2
  157. package/dist/pc/pc.pools.d.ts +11 -3
  158. package/dist/pc/pc.pools.js +9 -13
  159. package/dist/pc/pc.proficiencies.d.ts +54 -29
  160. package/dist/pc/pc.proficiencies.js +74 -95
  161. package/dist/pc/pc.proficiency-grants.d.ts +58 -0
  162. package/dist/pc/pc.proficiency-normalize.d.ts +24 -0
  163. package/dist/pc/pc.proficiency-normalize.js +12 -0
  164. package/dist/pc/pc.proficiency-query.d.ts +13 -0
  165. package/dist/pc/pc.proficiency-query.js +1 -1
  166. package/dist/pc/pc.recalc.d.ts +69 -14
  167. package/dist/pc/pc.recalc.js +261 -122
  168. package/dist/pc/pc.resolver.d.ts +34 -8
  169. package/dist/pc/pc.resolver.js +27 -12
  170. package/dist/pc/pc.resource-seed.d.ts +3 -2
  171. package/dist/pc/pc.resource-seed.js +19 -17
  172. package/dist/pc/pc.resources.d.ts +87 -0
  173. package/dist/pc/pc.resources.js +21 -0
  174. package/dist/pc/pc.rest.d.ts +10 -1
  175. package/dist/pc/pc.rest.js +66 -12
  176. package/dist/pc/pc.schema.d.ts +139 -3
  177. package/dist/pc/pc.schema.js +4 -2
  178. package/dist/pc/pc.slotting.js +3 -3
  179. package/dist/pc/pc.spellcasting.d.ts +75 -6
  180. package/dist/pc/pc.spellcasting.js +16 -6
  181. package/dist/pc/pc.table-column.d.ts +23 -0
  182. package/dist/pc/pc.table-column.js +13 -3
  183. package/dist/pc/pc.types.d.ts +251 -12
  184. package/dist/pc/pool-layout.d.ts +47 -0
  185. package/dist/pc/pool-layout.js +16 -0
  186. package/dist/pc/roll-scope.d.ts +2 -0
  187. package/dist/pc/roll-scope.js +8 -0
  188. package/dist/pc/roll-tag-labels.d.ts +40 -0
  189. package/dist/pc/roll-tag-labels.js +17 -0
  190. package/dist/pc/spell-source.d.ts +21 -0
  191. package/dist/pc/spell-source.js +13 -0
  192. package/dist/race/race.codec.js +9 -7
  193. package/dist/race/race.parser.js +8 -6
  194. package/dist/race/race.schema.d.ts +51 -1
  195. package/dist/race/race.schema.js +8 -5
  196. package/dist/race/race.structural.d.ts +7 -0
  197. package/dist/race/race.structural.js +6 -0
  198. package/dist/race/race.types.d.ts +20 -0
  199. package/dist/schemas/attack-schema.d.ts +5 -0
  200. package/dist/schemas/caster-type-schema.d.ts +14 -0
  201. package/dist/schemas/caster-type-schema.js +6 -0
  202. package/dist/schemas/choice-schema.js +2 -2
  203. package/dist/schemas/entity-extras-schema.d.ts +53 -0
  204. package/dist/schemas/entity-extras-schema.js +10 -0
  205. package/dist/schemas/equipment-grant-schema.d.ts +2 -0
  206. package/dist/schemas/equipment-grant-schema.js +1 -1
  207. package/dist/schemas/feature-alias.d.ts +34 -0
  208. package/dist/schemas/feature-effect-schema.d.ts +124 -0
  209. package/dist/schemas/feature-effect-schema.js +1 -1
  210. package/dist/schemas/feature-schema.d.ts +8 -0
  211. package/dist/schemas/feature-schema.js +6 -4
  212. package/dist/schemas/resource-schema.d.ts +43 -0
  213. package/dist/schemas/resource-schema.js +1 -1
  214. package/dist/schemas/selection-pool-schema.d.ts +2 -0
  215. package/dist/schemas/selection-pool-schema.js +1 -1
  216. package/dist/spell/spell.access.js +23 -3
  217. package/dist/spell/spell.codec.js +4 -3
  218. package/dist/spell/spell.enrichment.js +4 -4
  219. package/dist/spell/spell.parser.d.ts +30 -0
  220. package/dist/spell/spell.parser.js +9 -2
  221. package/dist/spell/spell.scaling.d.ts +27 -6
  222. package/dist/spell/spell.scaling.js +25 -6
  223. package/dist/spell/spell.schema.d.ts +44 -2
  224. package/dist/spell/spell.schema.js +2 -1
  225. package/dist/spell/spell.types.d.ts +14 -0
  226. package/dist/subclass/subclass.codec.d.ts +1 -1
  227. package/dist/subclass/subclass.codec.js +11 -8
  228. package/dist/subclass/subclass.parser.js +10 -7
  229. package/dist/subclass/subclass.schema.d.ts +54 -7
  230. package/dist/subclass/subclass.schema.js +8 -6
  231. package/dist/subclass/subclass.types.d.ts +20 -2
  232. package/dist/types/choice.d.ts +53 -2
  233. package/dist/types/choice.js +13 -1
  234. package/dist/types/equipment-grant.d.ts +8 -1
  235. package/dist/types/feature-effect.d.ts +78 -23
  236. package/dist/types/feature.d.ts +70 -1
  237. package/dist/types/optional-feature.types.d.ts +28 -0
  238. package/dist/types/resource.d.ts +21 -1
  239. package/dist/types/selection-pool.d.ts +17 -2
  240. package/dist/weapon/weapon.classify.js +2 -2
  241. package/dist/weapon/weapon.codec.js +4 -3
  242. package/dist/weapon/weapon.parser.js +3 -2
  243. package/dist/weapon/weapon.schema.d.ts +4 -0
  244. package/dist/weapon/weapon.schema.js +2 -1
  245. package/dist/weapon/weapon.types.d.ts +8 -0
  246. package/package.json +2 -2
  247. package/dist/chunk-5XUXXAQJ.js +0 -30
  248. package/dist/chunk-7P65Q426.js +0 -158
  249. package/dist/chunk-7TEK5KK7.js +0 -49
  250. package/dist/chunk-AIRFC5NQ.js +0 -143
  251. package/dist/chunk-CZPMTQOY.js +0 -68
  252. package/dist/chunk-N2JE7BWL.js +0 -81
  253. package/dist/chunk-NXR3BD2V.js +0 -90
  254. package/dist/chunk-O5EYMKFH.js +0 -6
  255. package/dist/chunk-Q5K6LWBZ.js +0 -45
  256. package/dist/chunk-TNUFPIXI.js +0 -1110
  257. package/dist/chunk-TZK36BU7.js +0 -38
  258. package/dist/chunk-W7HWMMBF.js +0 -46
  259. package/dist/chunk-Y4I74YEG.js +0 -33
@@ -0,0 +1,162 @@
1
+ import {
2
+ spellEntitySchema
3
+ } from "./chunk-VQAXTIBW.js";
4
+
5
+ // src/spell/spell.parser.ts
6
+ import { parseYaml, toStringSafe } from "@archivist-gg/core";
7
+ var KNOWN_KEYS = /* @__PURE__ */ new Set([
8
+ "name",
9
+ "level",
10
+ "school",
11
+ "casting_time",
12
+ "range",
13
+ "components",
14
+ "duration",
15
+ "concentration",
16
+ "ritual",
17
+ "classes",
18
+ "description",
19
+ "at_higher_levels",
20
+ "damage",
21
+ "saving_throw",
22
+ "casting_options",
23
+ // §2 · the fourteen converter keys. This gate and `spellEntitySchema` are independent: a key
24
+ // missing here is refused BEFORE zod ever sees it, so both must learn every new key.
25
+ "rendering_hint",
26
+ "misc_tags",
27
+ "area_tags",
28
+ "condition_inflict",
29
+ "affects_creature_type",
30
+ "spell_attack",
31
+ "ability_check",
32
+ "damage_resist",
33
+ "damage_immune",
34
+ "condition_immune",
35
+ "damage_vulnerable",
36
+ "has_fluff",
37
+ "image",
38
+ "has_fluff_images",
39
+ // Body-only metadata that's emitted to YAML but ignored at runtime — accept silently.
40
+ "slug",
41
+ "edition",
42
+ "source"
43
+ ]);
44
+ var DURATION_END_WORDS = { dispel: "dispelled", trigger: "triggered" };
45
+ function normalizeSpellComponents(value) {
46
+ if (typeof value === "string") return value;
47
+ if (value == null || typeof value !== "object" || Array.isArray(value)) return toStringSafe(value);
48
+ const c = value;
49
+ const parts = [];
50
+ if (c.v === true) parts.push("V");
51
+ if (c.s === true) parts.push("S");
52
+ if (typeof c.m === "string") parts.push(`M (${c.m})`);
53
+ else if (c.m === true) parts.push("M");
54
+ else if (c.m != null && typeof c.m === "object") {
55
+ const text = c.m.text;
56
+ parts.push(typeof text === "string" ? `M (${text})` : "M");
57
+ }
58
+ if (c.r === true) parts.push("R");
59
+ return parts.join(", ");
60
+ }
61
+ function normalizeSpellDurationEntry(entry) {
62
+ if (entry == null || typeof entry !== "object") return toStringSafe(entry);
63
+ const e = entry;
64
+ if (e.type === "permanent" && Array.isArray(e.ends)) {
65
+ return "until " + e.ends.map((w) => {
66
+ var _a;
67
+ return (_a = DURATION_END_WORDS[String(w)]) != null ? _a : String(w);
68
+ }).join(" or ");
69
+ }
70
+ if (e.type === "timed" && e.duration != null) {
71
+ const unit = e.duration.type;
72
+ const amount = e.duration.amount;
73
+ if (typeof unit === "string" && typeof amount === "number") {
74
+ const prefix = e.duration.up_to === true ? "up to " : "";
75
+ return `${prefix}${amount} ${unit}${amount > 1 ? "s" : ""}`;
76
+ }
77
+ }
78
+ return toStringSafe(entry);
79
+ }
80
+ function normalizeSpellDuration(value) {
81
+ if (typeof value === "string") return value;
82
+ if (!Array.isArray(value)) return toStringSafe(value);
83
+ return value.map((entry) => normalizeSpellDurationEntry(entry)).join("; ");
84
+ }
85
+ function mirrorSpellShapes(entity) {
86
+ return {
87
+ ...entity,
88
+ ...entity.components != null ? { components: normalizeSpellComponents(entity.components) } : {},
89
+ ...entity.duration != null ? { duration: normalizeSpellDuration(entity.duration) } : {}
90
+ };
91
+ }
92
+ function parseSpell(source) {
93
+ const result = parseYaml(source, ["name"]);
94
+ if (!result.success) return result;
95
+ const meta = {};
96
+ if (typeof result.data.source === "string") meta.source = result.data.source;
97
+ if (typeof result.data.edition === "string") meta.edition = result.data.edition;
98
+ const filtered = {};
99
+ for (const [k, v] of Object.entries(result.data)) {
100
+ if (k === "slug" || k === "edition" || k === "source") continue;
101
+ if (!KNOWN_KEYS.has(k)) {
102
+ return { success: false, error: `Unknown spell field: ${k}` };
103
+ }
104
+ filtered[k] = v;
105
+ }
106
+ const parsed = spellEntitySchema.safeParse(filtered);
107
+ if (!parsed.success) {
108
+ return { success: false, error: `spell schema validation failed: ${parsed.error.message}` };
109
+ }
110
+ const raw = parsed.data;
111
+ const spell = { name: raw.name };
112
+ if (raw.level != null) spell.level = raw.level;
113
+ if (raw.school != null) spell.school = toStringSafe(raw.school);
114
+ if (raw.casting_time != null) spell.casting_time = toStringSafe(raw.casting_time);
115
+ if (raw.range != null) spell.range = toStringSafe(raw.range);
116
+ if (raw.components != null) spell.components = normalizeSpellComponents(raw.components);
117
+ if (raw.duration != null) spell.duration = normalizeSpellDuration(raw.duration);
118
+ if (raw.concentration != null) spell.concentration = Boolean(raw.concentration);
119
+ if (raw.ritual != null) spell.ritual = Boolean(raw.ritual);
120
+ if (raw.classes) spell.classes = raw.classes.map(String);
121
+ if (raw.description) spell.description = raw.description;
122
+ if (raw.at_higher_levels) spell.at_higher_levels = raw.at_higher_levels.map(String);
123
+ if (raw.damage) spell.damage = { types: raw.damage.types.map(String) };
124
+ if (raw.saving_throw) spell.saving_throw = { ability: raw.saving_throw.ability };
125
+ if (raw.casting_options) {
126
+ spell.casting_options = raw.casting_options.map((opt) => {
127
+ const co = { type: opt.type };
128
+ if (opt.damage_roll !== void 0) co.damage_roll = opt.damage_roll;
129
+ if (opt.target_count !== void 0) co.target_count = opt.target_count;
130
+ if (opt.duration !== void 0) co.duration = opt.duration;
131
+ if (opt.range !== void 0) co.range = opt.range;
132
+ if (opt.concentration !== void 0) co.concentration = opt.concentration;
133
+ if (opt.shape_size !== void 0) co.shape_size = opt.shape_size;
134
+ if (opt.desc !== void 0) co.desc = opt.desc;
135
+ return co;
136
+ });
137
+ }
138
+ if (raw.rendering_hint != null) spell.rendering_hint = raw.rendering_hint;
139
+ if (raw.misc_tags != null) spell.misc_tags = raw.misc_tags;
140
+ if (raw.area_tags != null) spell.area_tags = raw.area_tags;
141
+ if (raw.condition_inflict != null) spell.condition_inflict = raw.condition_inflict;
142
+ if (raw.affects_creature_type != null) spell.affects_creature_type = raw.affects_creature_type;
143
+ if (raw.spell_attack != null) spell.spell_attack = raw.spell_attack;
144
+ if (raw.ability_check != null) spell.ability_check = raw.ability_check;
145
+ if (raw.damage_resist != null) spell.damage_resist = raw.damage_resist;
146
+ if (raw.damage_immune != null) spell.damage_immune = raw.damage_immune;
147
+ if (raw.condition_immune != null) spell.condition_immune = raw.condition_immune;
148
+ if (raw.damage_vulnerable != null) spell.damage_vulnerable = raw.damage_vulnerable;
149
+ if (raw.has_fluff != null) spell.has_fluff = raw.has_fluff;
150
+ if (raw.image != null) spell.image = raw.image;
151
+ if (raw.has_fluff_images != null) spell.has_fluff_images = raw.has_fluff_images;
152
+ if (meta.source) spell.source = meta.source;
153
+ if (meta.edition) spell.edition = meta.edition;
154
+ return { success: true, data: spell };
155
+ }
156
+
157
+ export {
158
+ normalizeSpellComponents,
159
+ normalizeSpellDuration,
160
+ mirrorSpellShapes,
161
+ parseSpell
162
+ };
@@ -2,8 +2,9 @@ import {
2
2
  ABILITY_KEYS,
3
3
  CR_PROFICIENCY,
4
4
  CR_XP,
5
+ DAMAGE_TYPES,
5
6
  SIZE_HIT_DICE
6
- } from "./chunk-WP2CX2JO.js";
7
+ } from "./chunk-WELIKLZF.js";
7
8
 
8
9
  // src/dnd/math.ts
9
10
  function abilityModifier(score) {
@@ -68,6 +69,22 @@ function abilityNameToKey(name) {
68
69
  }
69
70
  return null;
70
71
  }
72
+ var CANONICAL_DAMAGE_TYPES = new Set(DAMAGE_TYPES.map((t) => t.toLowerCase()));
73
+ function isCanonicalDamageType(type) {
74
+ return CANONICAL_DAMAGE_TYPES.has(type.trim().toLowerCase());
75
+ }
76
+ var CHOSEN_DAMAGE_TYPE = "chosen";
77
+ var CHOSEN_DAMAGE_TYPE_NOTE = "damage type of your choice";
78
+ function isChosenDamageType(type) {
79
+ return type !== void 0 && type.trim().toLowerCase() === CHOSEN_DAMAGE_TYPE;
80
+ }
81
+ function isRenderableDamageText(text) {
82
+ var _a;
83
+ const m = /^(?:\d*d\d+(?:\s*[+-]\s*\d+)?|[+-]?\d+)(?:\s+(.*))?$/i.exec(text.trim());
84
+ if (!m) return false;
85
+ const type = ((_a = m[1]) != null ? _a : "").trim();
86
+ return type === "" || isCanonicalDamageType(type);
87
+ }
71
88
 
72
89
  export {
73
90
  abilityModifier,
@@ -84,5 +101,10 @@ export {
84
101
  passivePerception,
85
102
  attackBonus,
86
103
  saveDC,
87
- abilityNameToKey
104
+ abilityNameToKey,
105
+ isCanonicalDamageType,
106
+ CHOSEN_DAMAGE_TYPE,
107
+ CHOSEN_DAMAGE_TYPE_NOTE,
108
+ isChosenDamageType,
109
+ isRenderableDamageText
88
110
  };
@@ -0,0 +1,50 @@
1
+ // src/pc/pool-layout.ts
2
+ var RENDERING_HINT_LAYOUT = {
3
+ "dice-pool": "dice-pool",
4
+ "point-pool": "point-pool"
5
+ };
6
+ function derivePoolLayout(entries) {
7
+ var _a, _b;
8
+ const votes = /* @__PURE__ */ new Map();
9
+ for (const e of entries) {
10
+ const hint = (_a = e.entity.rendering_hint) != null ? _a : "";
11
+ if (!Object.prototype.hasOwnProperty.call(RENDERING_HINT_LAYOUT, hint)) continue;
12
+ const layout = RENDERING_HINT_LAYOUT[hint];
13
+ votes.set(layout, ((_b = votes.get(layout)) != null ? _b : 0) + 1);
14
+ }
15
+ let best;
16
+ let bestN = 0;
17
+ let tied = false;
18
+ for (const [layout, n] of votes) {
19
+ if (n > bestN) {
20
+ best = layout;
21
+ bestN = n;
22
+ tied = false;
23
+ } else if (n === bestN) tied = true;
24
+ }
25
+ return tied ? void 0 : best;
26
+ }
27
+ var RENDERING_HINT_AFFORDANCE = {
28
+ "granted-die-to-ally": "granted-die"
29
+ };
30
+ function deriveEntryAffordance(entity) {
31
+ var _a;
32
+ const hint = (_a = entity.rendering_hint) != null ? _a : "";
33
+ if (!Object.prototype.hasOwnProperty.call(RENDERING_HINT_AFFORDANCE, hint)) return void 0;
34
+ return RENDERING_HINT_AFFORDANCE[hint];
35
+ }
36
+ var AFFORDANCE_CAPTIONS = {
37
+ "granted-die": "{amount}{die} to an ally"
38
+ };
39
+ function formatAffordanceCaption(affordance, values) {
40
+ return AFFORDANCE_CAPTIONS[affordance].replace(/\{(\w+)\}/g, (whole, key) => Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : whole);
41
+ }
42
+
43
+ export {
44
+ RENDERING_HINT_LAYOUT,
45
+ derivePoolLayout,
46
+ RENDERING_HINT_AFFORDANCE,
47
+ deriveEntryAffordance,
48
+ AFFORDANCE_CAPTIONS,
49
+ formatAffordanceCaption
50
+ };
@@ -0,0 +1,6 @@
1
+ // src/race/race.structural.ts
2
+ var RACE_STRUCTURAL_PSEUDO = /* @__PURE__ */ new Set(["size", "speed", "darkvision"]);
3
+
4
+ export {
5
+ RACE_STRUCTURAL_PSEUDO
6
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseSubclass
3
- } from "./chunk-L7E2UT5A.js";
3
+ } from "./chunk-HB7XZXDK.js";
4
4
 
5
5
  // src/subclass/subclass.codec.ts
6
6
  import yaml from "js-yaml";
@@ -0,0 +1,89 @@
1
+ import {
2
+ actionCostEnum,
3
+ resourceSchema
4
+ } from "./chunk-YLL2QXY2.js";
5
+ import {
6
+ choiceSchema
7
+ } from "./chunk-T26ZVCL5.js";
8
+ import {
9
+ featureEffectSchema
10
+ } from "./chunk-WPWOHGPI.js";
11
+ import {
12
+ additionalSpellsEntrySchema,
13
+ imageField,
14
+ progressionSchema
15
+ } from "./chunk-UA5KS2PL.js";
16
+
17
+ // src/feat/feat.schema.ts
18
+ import { z } from "zod";
19
+ var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
20
+ var editionEnum = z.enum(["2014", "2024"]);
21
+ var categoryEnum = z.enum(["origin", "general", "fighting-style", "epic-boon"]);
22
+ var prerequisiteSchema = z.discriminatedUnion("kind", [
23
+ z.object({ kind: z.literal("ability"), ability: abilityEnum, min: z.number().int().positive() }),
24
+ z.object({ kind: z.literal("level"), min: z.number().int().positive() }),
25
+ z.object({ kind: z.literal("spellcaster") }),
26
+ z.object({
27
+ kind: z.literal("proficiency"),
28
+ proficiency_type: z.enum(["armor", "weapon", "tool", "skill", "saving-throw"]),
29
+ value: z.string().min(1)
30
+ }),
31
+ z.object({ kind: z.literal("race"), slug: z.string().min(1) }),
32
+ z.object({ kind: z.literal("class"), slug: z.string().min(1) }),
33
+ // The exclusive-feat-category and feat-category arms carry a raw 5etools category code, not a
34
+ // slug; featCategoryLabel names it. The rest carry slugs the renderer humanizes, except other,
35
+ // which carries free prose that prereqText returns verbatim.
36
+ z.object({ kind: z.literal("feat"), slug: z.string().min(1) }),
37
+ z.object({ kind: z.literal("campaign"), slug: z.string().min(1) }),
38
+ z.object({ kind: z.literal("exclusive-feat-category"), slug: z.string().min(1) }),
39
+ z.object({ kind: z.literal("feature"), slug: z.string().min(1) }),
40
+ z.object({ kind: z.literal("other"), detail: z.string().min(1) }),
41
+ z.object({ kind: z.literal("feat-category"), slug: z.string().min(1) }),
42
+ z.object({ kind: z.literal("background"), slug: z.string().min(1) })
43
+ ]);
44
+ var featEntitySchema = z.object({
45
+ slug: z.string().min(1),
46
+ name: z.string().min(1),
47
+ edition: editionEnum,
48
+ source: z.string().min(1),
49
+ category: categoryEnum,
50
+ description: z.string(),
51
+ prerequisites: z.array(prerequisiteSchema),
52
+ benefits: z.array(z.string()),
53
+ effects: z.array(featureEffectSchema),
54
+ grants_asi: z.object({
55
+ amount: z.number().int().positive(),
56
+ pool: z.array(abilityEnum).optional()
57
+ }).nullable(),
58
+ repeatable: z.boolean(),
59
+ choices: z.array(choiceSchema),
60
+ rendering_hint: z.string().optional(),
61
+ additional_spells: z.array(additionalSpellsEntrySchema).optional(),
62
+ // 71
63
+ has_fluff: z.boolean().optional(),
64
+ // 0 carriers — symmetry, §9.4
65
+ has_fluff_images: z.boolean().optional(),
66
+ // 41
67
+ trait_tags: z.unknown().optional(),
68
+ // 7, nullable measured — same passthrough family
69
+ optionalfeature_progression: z.array(progressionSchema).optional(),
70
+ // 4
71
+ resources: z.array(resourceSchema).optional(),
72
+ // closes the feat.types.ts `Feat.resources` schema/type asymmetry (C);
73
+ // ZERO converter/bundle carriers (no census row) — the only behaviour delta is AUTHORED feats,
74
+ // where the type has promised the field all along. Import resourceSchema.
75
+ action_cost: actionCostEnum.optional(),
76
+ // ruling R-G1b-5 (gate1-r1 f1): a TOP-LEVEL bundle key on ONE
77
+ // feat (`SRD 2024/Feats/Boon of the Night Spirit.md`, `action_cost: bonus-action` at entity top
78
+ // level) — the ninth SRD-control non-kept row: top-level on the ENTITY, not featureSchema-shaped
79
+ // (finding 14's nine rows split 4 featureSchema / 4 §2.8-arity / this one, which needed its own
80
+ // declaration — gate1-r4 R4-1). `actionCostEnum` is exported from
81
+ // `schemas/resource-schema.ts` (optionalFeatureEntitySchema already imports it). MAPPED since
82
+ // R4-G3a §10.2.3: the resolver aliases it onto the resolved feature's `action`, which is what
83
+ // draws the feat note's Action line and files the row under that economy.
84
+ image: imageField
85
+ });
86
+
87
+ export {
88
+ featEntitySchema
89
+ };
@@ -0,0 +1,16 @@
1
+ // src/pc/pc.proficiency-normalize.ts
2
+ var normKey = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean).map((w) => w.endsWith("s") ? w.slice(0, -1) : w).sort().join(" ");
3
+ var toProfSlug = (s) => s.toLowerCase().replace(/’/g, "'").trim().replace(/\s+/g, "-");
4
+ var humanizeProficiency = (s) => s.replace(/-/g, " ").replace(/(^|\s)\w/g, (c) => c.toUpperCase());
5
+ var proficiencyLabel = (raw) => {
6
+ const text = raw.replace(/’/g, "'").trim().replace(/\s+/g, " ");
7
+ if (!/\s/.test(text) || text === text.toLowerCase()) return humanizeProficiency(toProfSlug(raw));
8
+ return text.charAt(0).toUpperCase() + text.slice(1);
9
+ };
10
+
11
+ export {
12
+ normKey,
13
+ toProfSlug,
14
+ humanizeProficiency,
15
+ proficiencyLabel
16
+ };
@@ -0,0 +1,21 @@
1
+ // src/pc/conditions.constants.ts
2
+ var CONDITION_SLUGS = [
3
+ "blinded",
4
+ "charmed",
5
+ "deafened",
6
+ "frightened",
7
+ "grappled",
8
+ "incapacitated",
9
+ "invisible",
10
+ "paralyzed",
11
+ "petrified",
12
+ "poisoned",
13
+ "prone",
14
+ "restrained",
15
+ "stunned",
16
+ "unconscious"
17
+ ];
18
+
19
+ export {
20
+ CONDITION_SLUGS
21
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  itemEntitySchema
3
- } from "./chunk-K6Q5OUGK.js";
3
+ } from "./chunk-GFZ7PITJ.js";
4
4
 
5
5
  // src/item/item.parser.ts
6
6
  import { parseYaml } from "@archivist-gg/core";
@@ -41,6 +41,14 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
41
41
  "scroll_level",
42
42
  "unidentified",
43
43
  "masked_category",
44
+ // Modelled top-level converter keys (spec §4). The schema declaring them is NOT
45
+ // enough: this set is the second, independent gate — a key missing here is
46
+ // copied into `raw` as well, so the two lists must move together.
47
+ "modify_speed",
48
+ "rendering_hint",
49
+ "has_fluff",
50
+ "has_fluff_images",
51
+ "image",
44
52
  // Legacy fields kept (removed in Slice 7 after grep verifies no consumer)
45
53
  "damage_dice",
46
54
  "damage_type",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  featureEffectSchema
3
- } from "./chunk-NXR3BD2V.js";
3
+ } from "./chunk-WPWOHGPI.js";
4
4
 
5
5
  // src/schemas/choice-schema.ts
6
6
  import { z } from "zod";
@@ -0,0 +1,38 @@
1
+ import {
2
+ ALL_SKILL_SLUGS
3
+ } from "./chunk-KU3YLF6D.js";
4
+ import {
5
+ normalizeAbility
6
+ } from "./chunk-WELIKLZF.js";
7
+
8
+ // src/pc/roll-scope.ts
9
+ var NOUNS = /\b(saving throws?|saves?|ability checks?|checks?|attack rolls?)\b/gi;
10
+ var QUANTIFIERS = /\b(all|any|other|the next|you make|that you make)\b/gi;
11
+ var slug = (s) => s.toLowerCase().trim().replace(/[^a-z]+/g, "-").replace(/(^-|-$)/g, "");
12
+ function normalizeRollScope(raw, _roll) {
13
+ if (raw == null) return void 0;
14
+ const cleaned = raw.replace(NOUNS, " ").replace(QUANTIFIERS, " ").replace(/\s+/g, " ").trim();
15
+ const parts = cleaned.split(/\s*(?:,|\/|\band\b|\bor\b)\s*/i).map((p) => p.trim()).filter(Boolean);
16
+ if (parts.length === 0 || parts.length > 3) return void 0;
17
+ const out = [];
18
+ for (let p of parts) {
19
+ const m = p.match(/^(?:[a-z]+\s*)?\(\s*([a-z -]+)\s*\)$/i);
20
+ if (m) p = m[1];
21
+ const s = slug(p);
22
+ if (ALL_SKILL_SLUGS.includes(s)) {
23
+ out.push(s);
24
+ continue;
25
+ }
26
+ const ab = normalizeAbility(p);
27
+ if (ab) {
28
+ out.push(ab);
29
+ continue;
30
+ }
31
+ return void 0;
32
+ }
33
+ return out;
34
+ }
35
+
36
+ export {
37
+ normalizeRollScope
38
+ };
@@ -0,0 +1,26 @@
1
+ // src/schemas/entity-extras-schema.ts
2
+ import { z } from "zod";
3
+ var imageField = z.union([z.string().min(1), z.array(z.string().min(1))]).optional();
4
+ var additionalSpellsEntrySchema = z.object({
5
+ name: z.string().min(1).optional(),
6
+ ability: z.union([z.string().min(1), z.object({ choose: z.array(z.string().min(1)) })]).optional(),
7
+ known: z.unknown().optional(),
8
+ innate: z.unknown().optional(),
9
+ prepared: z.unknown().optional(),
10
+ expanded: z.unknown().optional(),
11
+ resourceName: z.string().min(1).optional()
12
+ });
13
+ var progressionSchema = z.object({
14
+ name: z.string().min(1),
15
+ progression: z.union([z.record(z.string(), z.number()), z.array(z.number())]),
16
+ feature_type: z.array(z.string().min(1)).optional(),
17
+ featureType: z.array(z.string().min(1)).optional(),
18
+ category: z.array(z.string().min(1)).optional(),
19
+ required: z.record(z.string(), z.array(z.string())).optional()
20
+ });
21
+
22
+ export {
23
+ imageField,
24
+ additionalSpellsEntrySchema,
25
+ progressionSchema
26
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveBaseItem
3
- } from "./chunk-4LZJXJJS.js";
3
+ } from "./chunk-WXWPK3LI.js";
4
4
 
5
5
  // src/pc/pc.slotting.ts
6
6
  function isItemEntity(e) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  bareEntitySlug
3
- } from "./chunk-LV4SNWZQ.js";
3
+ } from "./chunk-6X4KUDMU.js";
4
4
 
5
5
  // src/weapon/weapon.classify.ts
6
6
  var PURE_RANGED_THROWN_SLUGS = /* @__PURE__ */ new Set(["dart", "net"]);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseClass
3
- } from "./chunk-42Y5IJKT.js";
3
+ } from "./chunk-5U5FFFA3.js";
4
4
 
5
5
  // src/class/class.codec.ts
6
6
  import yaml from "js-yaml";
@@ -0,0 +1,101 @@
1
+ import {
2
+ resolveSpellcasting
3
+ } from "./chunk-DPUJDOKV.js";
4
+ import {
5
+ warnOnce
6
+ } from "./chunk-LVQLWRCU.js";
7
+ import {
8
+ isValidMaxFormula
9
+ } from "./chunk-EVMUJYUD.js";
10
+
11
+ // src/pc/pc.resources.ts
12
+ function resourceLevelFor(source, resolved) {
13
+ var _a, _b, _c, _d;
14
+ if (source.kind === "class") {
15
+ return (_b = (_a = resolved.classes.find((c) => {
16
+ var _a2;
17
+ return ((_a2 = c.entity) == null ? void 0 : _a2.slug) === source.slug;
18
+ })) == null ? void 0 : _a.level) != null ? _b : resolved.totalLevel;
19
+ }
20
+ if (source.kind === "subclass") {
21
+ return (_d = (_c = resolved.classes.find((c) => {
22
+ var _a2;
23
+ return ((_a2 = c.subclass) == null ? void 0 : _a2.slug) === source.slug;
24
+ })) == null ? void 0 : _c.level) != null ? _d : resolved.totalLevel;
25
+ }
26
+ return resolved.totalLevel;
27
+ }
28
+ function resolveRecovery(entry) {
29
+ var _a;
30
+ const kind = (_a = entry.restores) != null ? _a : "uses";
31
+ const restTriggered = (entry.reset === "short-rest" || entry.reset === "long-rest") && !entry.action;
32
+ const flavour = kind === "spell-slots" ? "manual" : restTriggered ? "rest" : "manual";
33
+ return { entry, kind, flavour };
34
+ }
35
+ function toResolvedResource(r, fallbackName, owner) {
36
+ var _a, _b;
37
+ return {
38
+ id: r.id,
39
+ name: (_a = r.name) != null ? _a : fallbackName,
40
+ reset: r.reset,
41
+ maxFormula: r.max_formula,
42
+ ...r.die ? { die: r.die } : {},
43
+ ...r.scales_at ? { scalesAt: r.scales_at } : {},
44
+ ...((_b = r.recovery) == null ? void 0 : _b.length) ? { recovery: r.recovery.map(resolveRecovery) } : {},
45
+ owner
46
+ };
47
+ }
48
+ function resolveFeatureResources(features) {
49
+ var _a, _b;
50
+ const out = /* @__PURE__ */ new Map();
51
+ for (const rf of features) {
52
+ for (const r of (_a = rf.feature.resources) != null ? _a : []) {
53
+ if (out.has(r.id)) continue;
54
+ const owner = { kind: "feature", featureId: (_b = rf.feature.id) != null ? _b : rf.feature.name, featureName: rf.feature.name, source: rf.source };
55
+ out.set(r.id, toResolvedResource(r, rf.feature.name, owner));
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+ function resolveResourceIndex(resolved) {
61
+ var _a, _b, _c, _d, _e;
62
+ const out = new Map(resolveFeatureResources((_a = resolved.features) != null ? _a : []));
63
+ for (const pool of (_b = resolved.pools) != null ? _b : []) {
64
+ const rc = resolved.classes[pool.classIndex];
65
+ const classSlug = (_c = rc == null ? void 0 : rc.entity) == null ? void 0 : _c.slug;
66
+ if (!classSlug) continue;
67
+ for (const entry of [...(_d = pool.selected) != null ? _d : [], ...(_e = pool.grants) != null ? _e : []]) {
68
+ const uses = entry.entity.uses;
69
+ if (!uses || out.has(entry.slug)) continue;
70
+ const maxFormula = String(uses.max);
71
+ if (!isValidMaxFormula(maxFormula)) {
72
+ warnOnce(`pool-uses:${entry.slug}`, `optional feature "${entry.slug}" has a prose uses.max (${JSON.stringify(uses.max)}); no tracker`);
73
+ continue;
74
+ }
75
+ out.set(entry.slug, {
76
+ id: entry.slug,
77
+ name: entry.entity.name,
78
+ reset: uses.recharge,
79
+ maxFormula,
80
+ owner: { kind: "pool", poolId: pool.id, poolLabel: pool.label, source: { kind: "class", slug: classSlug, level: pool.anchorLevel } }
81
+ });
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+ function poolSaveDC(resolved, derived, pool) {
87
+ var _a, _b;
88
+ const rc = resolved.classes[pool.classIndex];
89
+ const ability = (_b = (_a = rc == null ? void 0 : rc.subclass) == null ? void 0 : _a.spellcasting) == null ? void 0 : _b.ability;
90
+ if (!rc || !ability) return null;
91
+ if (resolveSpellcasting(rc) !== null) return null;
92
+ return 8 + derived.proficiencyBonus + derived.mods[ability];
93
+ }
94
+
95
+ export {
96
+ resourceLevelFor,
97
+ resolveRecovery,
98
+ resolveFeatureResources,
99
+ resolveResourceIndex,
100
+ poolSaveDC
101
+ };