@archivist-gg/dnd5e 0.3.0 → 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 (260) 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 +34 -0
  10. package/dist/background/background.schema.js +9 -6
  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-DPUJDOKV.js +365 -0
  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-VTYUBINW.js → chunk-HJ5EEZ47.js} +1 -1
  39. package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
  40. package/dist/{chunk-A4WENOUQ.js → chunk-I2NDWFDD.js} +52 -8
  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-TUIFSVLI.js → chunk-JRDPUDT5.js} +30 -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-DZXSWSBP.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-EMOORSQ7.js → chunk-OSQFPGLM.js} +45 -6
  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 +2120 -6923
  122. package/dist/item/item.ai-schema.d.ts +1 -1
  123. package/dist/item/item.codec.js +4 -3
  124. package/dist/item/item.enrichment.js +4 -4
  125. package/dist/item/item.parser.js +3 -2
  126. package/dist/item/item.schema.d.ts +69 -3
  127. package/dist/item/item.schema.js +2 -1
  128. package/dist/item/item.types.d.ts +69 -3
  129. package/dist/monster/monster.codec.d.ts +7 -6
  130. package/dist/monster/monster.enrichment.js +4 -4
  131. package/dist/monster/monster.format.d.ts +135 -0
  132. package/dist/monster/monster.format.js +78 -0
  133. package/dist/monster/monster.parser.d.ts +6 -1
  134. package/dist/monster/monster.parser.js +10 -1
  135. package/dist/monster/monster.schema.d.ts +369 -0
  136. package/dist/monster/monster.schema.js +34 -0
  137. package/dist/monster/monster.types.d.ts +200 -14
  138. package/dist/optional-feature/optional-feature.codec.js +6 -5
  139. package/dist/optional-feature/optional-feature.parser.js +5 -4
  140. package/dist/optional-feature/optional-feature.schema.d.ts +176 -0
  141. package/dist/optional-feature/optional-feature.schema.js +4 -3
  142. package/dist/pack.d.ts +1 -1
  143. package/dist/pc/conditions.constants.d.ts +0 -1
  144. package/dist/pc/conditions.constants.js +1 -3
  145. package/dist/pc/decision-recognizer.js +1 -1
  146. package/dist/pc/pc.additional-spells.d.ts +47 -0
  147. package/dist/pc/pc.additional-spells.js +23 -0
  148. package/dist/pc/pc.asi-flatten.d.ts +18 -0
  149. package/dist/pc/pc.decision-engine.d.ts +106 -5
  150. package/dist/pc/pc.decision-engine.js +28 -10
  151. package/dist/pc/pc.defense-normalize.d.ts +11 -0
  152. package/dist/pc/pc.defense-normalize.js +6 -0
  153. package/dist/pc/pc.equipment.d.ts +30 -1
  154. package/dist/pc/pc.equipment.js +12 -7
  155. package/dist/pc/pc.feature-effects.d.ts +225 -11
  156. package/dist/pc/pc.feature-effects.js +21 -5
  157. package/dist/pc/pc.parser.js +2 -2
  158. package/dist/pc/pc.pools.d.ts +11 -3
  159. package/dist/pc/pc.pools.js +9 -13
  160. package/dist/pc/pc.proficiencies.d.ts +60 -4
  161. package/dist/pc/pc.proficiencies.js +89 -37
  162. package/dist/pc/pc.proficiency-grants.d.ts +58 -0
  163. package/dist/pc/pc.proficiency-normalize.d.ts +29 -0
  164. package/dist/pc/pc.proficiency-normalize.js +12 -0
  165. package/dist/pc/pc.proficiency-query.d.ts +13 -0
  166. package/dist/pc/pc.proficiency-query.js +5 -1
  167. package/dist/pc/pc.recalc.d.ts +69 -14
  168. package/dist/pc/pc.recalc.js +291 -142
  169. package/dist/pc/pc.resolver.d.ts +34 -8
  170. package/dist/pc/pc.resolver.js +27 -12
  171. package/dist/pc/pc.resource-seed.d.ts +3 -2
  172. package/dist/pc/pc.resource-seed.js +19 -17
  173. package/dist/pc/pc.resources.d.ts +87 -0
  174. package/dist/pc/pc.resources.js +21 -0
  175. package/dist/pc/pc.rest.d.ts +10 -1
  176. package/dist/pc/pc.rest.js +66 -12
  177. package/dist/pc/pc.schema.d.ts +147 -7
  178. package/dist/pc/pc.schema.js +4 -2
  179. package/dist/pc/pc.slotting.js +3 -3
  180. package/dist/pc/pc.spellcasting.d.ts +80 -7
  181. package/dist/pc/pc.spellcasting.js +18 -6
  182. package/dist/pc/pc.table-column.d.ts +23 -0
  183. package/dist/pc/pc.table-column.js +13 -3
  184. package/dist/pc/pc.types.d.ts +253 -16
  185. package/dist/pc/pool-layout.d.ts +47 -0
  186. package/dist/pc/pool-layout.js +16 -0
  187. package/dist/pc/roll-scope.d.ts +2 -0
  188. package/dist/pc/roll-scope.js +8 -0
  189. package/dist/pc/roll-tag-labels.d.ts +40 -0
  190. package/dist/pc/roll-tag-labels.js +17 -0
  191. package/dist/pc/spell-source.d.ts +21 -0
  192. package/dist/pc/spell-source.js +13 -0
  193. package/dist/race/race.codec.js +9 -7
  194. package/dist/race/race.parser.js +8 -6
  195. package/dist/race/race.schema.d.ts +51 -1
  196. package/dist/race/race.schema.js +8 -5
  197. package/dist/race/race.structural.d.ts +7 -0
  198. package/dist/race/race.structural.js +6 -0
  199. package/dist/race/race.types.d.ts +20 -0
  200. package/dist/schemas/attack-schema.d.ts +5 -0
  201. package/dist/schemas/caster-type-schema.d.ts +14 -0
  202. package/dist/schemas/caster-type-schema.js +6 -0
  203. package/dist/schemas/choice-schema.js +2 -2
  204. package/dist/schemas/entity-extras-schema.d.ts +53 -0
  205. package/dist/schemas/entity-extras-schema.js +10 -0
  206. package/dist/schemas/equipment-grant-schema.d.ts +2 -0
  207. package/dist/schemas/equipment-grant-schema.js +1 -1
  208. package/dist/schemas/feature-alias.d.ts +34 -0
  209. package/dist/schemas/feature-effect-schema.d.ts +124 -0
  210. package/dist/schemas/feature-effect-schema.js +1 -1
  211. package/dist/schemas/feature-schema.d.ts +8 -0
  212. package/dist/schemas/feature-schema.js +6 -4
  213. package/dist/schemas/resource-schema.d.ts +43 -0
  214. package/dist/schemas/resource-schema.js +1 -1
  215. package/dist/schemas/selection-pool-schema.d.ts +2 -0
  216. package/dist/schemas/selection-pool-schema.js +1 -1
  217. package/dist/spell/spell.access.js +23 -3
  218. package/dist/spell/spell.codec.js +4 -3
  219. package/dist/spell/spell.enrichment.js +4 -4
  220. package/dist/spell/spell.parser.d.ts +30 -0
  221. package/dist/spell/spell.parser.js +9 -2
  222. package/dist/spell/spell.scaling.d.ts +27 -6
  223. package/dist/spell/spell.scaling.js +25 -6
  224. package/dist/spell/spell.schema.d.ts +44 -2
  225. package/dist/spell/spell.schema.js +2 -1
  226. package/dist/spell/spell.types.d.ts +14 -0
  227. package/dist/subclass/subclass.codec.d.ts +1 -1
  228. package/dist/subclass/subclass.codec.js +11 -8
  229. package/dist/subclass/subclass.parser.js +10 -7
  230. package/dist/subclass/subclass.schema.d.ts +54 -7
  231. package/dist/subclass/subclass.schema.js +8 -6
  232. package/dist/subclass/subclass.types.d.ts +20 -2
  233. package/dist/types/choice.d.ts +56 -0
  234. package/dist/types/choice.js +21 -3
  235. package/dist/types/equipment-grant.d.ts +8 -1
  236. package/dist/types/feature-effect.d.ts +78 -23
  237. package/dist/types/feature.d.ts +70 -1
  238. package/dist/types/optional-feature.types.d.ts +28 -0
  239. package/dist/types/resource.d.ts +21 -1
  240. package/dist/types/selection-pool.d.ts +17 -2
  241. package/dist/weapon/weapon.classify.js +2 -2
  242. package/dist/weapon/weapon.codec.js +4 -3
  243. package/dist/weapon/weapon.parser.js +3 -2
  244. package/dist/weapon/weapon.schema.d.ts +4 -0
  245. package/dist/weapon/weapon.schema.js +2 -1
  246. package/dist/weapon/weapon.types.d.ts +8 -0
  247. package/package.json +2 -2
  248. package/dist/chunk-5MZSB6OM.js +0 -1089
  249. package/dist/chunk-5XUXXAQJ.js +0 -30
  250. package/dist/chunk-7P65Q426.js +0 -158
  251. package/dist/chunk-AIRFC5NQ.js +0 -143
  252. package/dist/chunk-BH6AMZVY.js +0 -25
  253. package/dist/chunk-CZPMTQOY.js +0 -68
  254. package/dist/chunk-M2EMFD6S.js +0 -269
  255. package/dist/chunk-N2JE7BWL.js +0 -81
  256. package/dist/chunk-NXR3BD2V.js +0 -90
  257. package/dist/chunk-Q5K6LWBZ.js +0 -45
  258. package/dist/chunk-TZK36BU7.js +0 -38
  259. package/dist/chunk-W7HWMMBF.js +0 -46
  260. package/dist/chunk-Y4I74YEG.js +0 -33
@@ -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
+ };
@@ -0,0 +1,100 @@
1
+ import {
2
+ imageField
3
+ } from "./chunk-UA5KS2PL.js";
4
+
5
+ // src/spell/spell.schema.ts
6
+ import { z } from "zod";
7
+ var castingOptionSchema = z.object({
8
+ type: z.string(),
9
+ damage_roll: z.string().optional(),
10
+ target_count: z.number().optional(),
11
+ duration: z.string().optional(),
12
+ range: z.number().optional(),
13
+ concentration: z.boolean().optional(),
14
+ shape_size: z.number().optional(),
15
+ desc: z.string().optional()
16
+ });
17
+ var spellMaterialSchema = z.union([
18
+ z.boolean(),
19
+ z.string(),
20
+ z.strictObject({
21
+ text: z.string(),
22
+ cost: z.number().optional(),
23
+ consume: z.union([z.boolean(), z.string()]).optional()
24
+ })
25
+ ]);
26
+ var spellComponentsObjectSchema = z.strictObject({
27
+ v: z.boolean().optional(),
28
+ s: z.boolean().optional(),
29
+ m: spellMaterialSchema.optional(),
30
+ r: z.boolean().optional()
31
+ });
32
+ var spellDurationEntrySchema = z.discriminatedUnion("type", [
33
+ z.strictObject({
34
+ type: z.literal("permanent"),
35
+ ends: z.array(z.enum(["dispel", "trigger"])).nonempty()
36
+ }),
37
+ z.strictObject({
38
+ type: z.literal("timed"),
39
+ duration: z.strictObject({
40
+ type: z.enum(["minute", "hour"]),
41
+ amount: z.number().int().positive(),
42
+ up_to: z.boolean().optional()
43
+ })
44
+ })
45
+ ]);
46
+ var spellEntitySchema = z.object({
47
+ name: z.string().min(1),
48
+ level: z.number().int().min(0).optional(),
49
+ school: z.string().optional(),
50
+ casting_time: z.string().optional(),
51
+ range: z.string().optional(),
52
+ // Union arms per §3.2; `parseSpell` normalises the structured arms back to the corpus's own
53
+ // string form, so `Spell.components` / `Spell.duration` stay `string` and no consumer changes.
54
+ components: z.union([z.string(), spellComponentsObjectSchema]).optional(),
55
+ duration: z.union([z.string(), z.array(spellDurationEntrySchema).nonempty()]).optional(),
56
+ concentration: z.boolean().optional(),
57
+ ritual: z.boolean().optional(),
58
+ classes: z.array(z.string()).optional(),
59
+ description: z.string().optional(),
60
+ at_higher_levels: z.array(z.string()).optional(),
61
+ damage: z.object({ types: z.array(z.string()) }).optional(),
62
+ saving_throw: z.object({ ability: z.string() }).optional(),
63
+ casting_options: z.array(castingOptionSchema).optional(),
64
+ // §2 · the fourteen converter keys this root refused before R4-G2. Doc counts are MEASURED over
65
+ // the frozen tree; the two zero-carrier keys are declared because the root is `.strict()` and an
66
+ // images-ON run (`image`) or a non-images-gated fluff flag (`has_fluff_images`) would otherwise
67
+ // refuse the whole document. Declared-only: NO rendering this phase (booked to G8 by name).
68
+ rendering_hint: z.string().optional(),
69
+ // 1,048 docs — NEVER .min(1): every value is ''
70
+ misc_tags: z.array(z.string()).optional(),
71
+ // 769
72
+ area_tags: z.array(z.string()).optional(),
73
+ // 707
74
+ condition_inflict: z.array(z.string()).optional(),
75
+ // 194
76
+ affects_creature_type: z.array(z.string()).optional(),
77
+ // 87
78
+ spell_attack: z.enum(["melee", "ranged"]).optional(),
79
+ // 77 (ranged 44 / melee 33)
80
+ ability_check: z.array(z.string()).optional(),
81
+ // 61
82
+ damage_resist: z.array(z.string()).optional(),
83
+ // 35
84
+ damage_immune: z.array(z.string()).optional(),
85
+ // 21
86
+ condition_immune: z.array(z.string()).optional(),
87
+ // 11
88
+ damage_vulnerable: z.array(z.string()).optional(),
89
+ // 2
90
+ has_fluff: z.boolean().optional(),
91
+ // 1
92
+ image: imageField,
93
+ // 0 today; `imageField` already ends .optional()
94
+ has_fluff_images: z.boolean().optional()
95
+ // 0 today
96
+ }).strict();
97
+
98
+ export {
99
+ spellEntitySchema
100
+ };
@@ -203,6 +203,10 @@ var CONDITIONS = [
203
203
  "Unconscious"
204
204
  ];
205
205
  var STANDARD_SENSES = ["Blindsight", "Darkvision", "Tremorsense", "Truesight"];
206
+ var STANDARD_ACTIONS = {
207
+ "2014": ["Attack", "Cast a Spell", "Dash", "Disengage", "Dodge", "Help", "Hide", "Ready", "Search", "Use an Object"],
208
+ "2024": ["Attack", "Dash", "Disengage", "Dodge", "Help", "Hide", "Influence", "Magic", "Ready", "Search", "Study", "Utilize"]
209
+ };
206
210
  var ALIGNMENT_ETHICAL = ["Lawful", "Neutral", "Chaotic", "Unaligned", "Any"];
207
211
  var ALIGNMENT_MORAL = ["Good", "Neutral", "Evil"];
208
212
  var ALL_SECTIONS = [
@@ -214,6 +218,20 @@ var ALL_SECTIONS = [
214
218
  "Lair Actions",
215
219
  "Mythic Actions"
216
220
  ];
221
+ var ABILITY_NAME_TO_KEY = {
222
+ strength: "str",
223
+ dexterity: "dex",
224
+ constitution: "con",
225
+ intelligence: "int",
226
+ wisdom: "wis",
227
+ charisma: "cha"
228
+ };
229
+ function normalizeAbility(value) {
230
+ var _a;
231
+ const k = value.trim().toLowerCase();
232
+ if (ABILITY_KEYS.includes(k)) return k;
233
+ return (_a = ABILITY_NAME_TO_KEY[k]) != null ? _a : null;
234
+ }
217
235
 
218
236
  export {
219
237
  CR_PROFICIENCY,
@@ -229,7 +247,10 @@ export {
229
247
  DAMAGE_NONMAGICAL_VARIANTS,
230
248
  CONDITIONS,
231
249
  STANDARD_SENSES,
250
+ STANDARD_ACTIONS,
232
251
  ALIGNMENT_ETHICAL,
233
252
  ALIGNMENT_MORAL,
234
- ALL_SECTIONS
253
+ ALL_SECTIONS,
254
+ ABILITY_NAME_TO_KEY,
255
+ normalizeAbility
235
256
  };
@@ -0,0 +1,172 @@
1
+ // src/schemas/feature-effect-schema.ts
2
+ import { z } from "zod";
3
+ var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
4
+ var subjectField = z.string().min(1).optional();
5
+ var conditionField = z.string().optional();
6
+ var featureEffectSchema = z.discriminatedUnion("kind", [
7
+ z.object({ kind: z.literal("initiative-bonus"), value: z.number().int(), condition: conditionField, subject: subjectField }),
8
+ // `condition` here is the condition NAME (frightened, poisoned); the qualifier is `while`.
9
+ z.object({ kind: z.literal("immune-condition"), condition: z.string().min(1), while: z.string().optional(), subject: subjectField }),
10
+ z.object({ kind: z.literal("resistance"), damage_type: z.string().min(1), condition: conditionField, subject: subjectField }),
11
+ z.object({ kind: z.literal("hp-per-level-bonus"), value: z.number().int(), condition: conditionField, subject: subjectField }),
12
+ z.object({
13
+ kind: z.literal("speed-bonus"),
14
+ mode: z.enum(["walk", "fly", "swim", "climb", "burrow"]),
15
+ value: z.number().int(),
16
+ // when true, value is an absolute floor (Math.max), not additive; currently
17
+ // only surfaced for mode:"walk" · other modes have no derived speed yet.
18
+ set: z.boolean().optional(),
19
+ // R4-G7 §7.3: the WHOLE progression lives inside the effect, because the overlay's `class_features`
20
+ // map is keyed `<class>:<feature-slug>` with no level component and, after §7.1's fold, the sheet
21
+ // reads exactly ONE copy of a repeated feature. The engine takes the highest entry at or below the
22
+ // effect's OWN source level (`levelFor`), and applies it to the additive bonus and to a `set` floor
23
+ // alike. The Monk's Unarmored Movement is the shipped carrier shape.
24
+ scales_at: z.array(z.object({ level: z.number().int().min(1).max(20), value: z.number().int() })).optional(),
25
+ condition: conditionField,
26
+ subject: subjectField
27
+ }),
28
+ z.object({
29
+ kind: z.literal("sense"),
30
+ type: z.enum(["darkvision", "blindsight", "tremorsense", "truesight"]),
31
+ range: z.number().int().nonnegative(),
32
+ condition: conditionField,
33
+ subject: subjectField
34
+ }),
35
+ // `condition` here is the condition NAME being applied.
36
+ z.object({
37
+ kind: z.literal("apply-condition"),
38
+ condition: z.string().min(1),
39
+ duration: z.string().optional(),
40
+ ends_on: z.array(z.string()).optional(),
41
+ save_repeat: z.object({ ability: z.string().min(1), timing: z.string().min(1) }).optional(),
42
+ subject: subjectField
43
+ }),
44
+ z.object({
45
+ kind: z.literal("damage-bonus"),
46
+ // intentionally an open string, NOT a damage-type enum: must accept the
47
+ // literal "chosen" (player-selected damage type at action time) alongside
48
+ // canonical damage types. Do not tighten to an enum.
49
+ damage_type: z.string().min(1),
50
+ amount: z.string().min(1),
51
+ applies_to: z.enum(["weapon", "spell", "all"]).optional(),
52
+ condition: conditionField,
53
+ subject: subjectField
54
+ }),
55
+ z.object({
56
+ kind: z.literal("proficiency"),
57
+ proficiency_type: z.enum(["skill", "tool", "language", "saving-throw", "armor", "weapon"]),
58
+ value: z.string().min(1),
59
+ expertise: z.boolean().optional(),
60
+ condition: conditionField,
61
+ subject: subjectField
62
+ }),
63
+ z.object({ kind: z.literal("ac-bonus"), value: z.number().int(), requires_armor: z.boolean().optional(), condition: conditionField, subject: subjectField }),
64
+ z.object({
65
+ kind: z.literal("unarmored-ac"),
66
+ abilities: z.array(abilityEnum),
67
+ base: z.number().int().optional(),
68
+ allow_shield: z.boolean().optional(),
69
+ condition: conditionField,
70
+ subject: subjectField
71
+ }),
72
+ z.object({
73
+ kind: z.literal("unarmed-strike"),
74
+ // R4-G7 §7.2: a bare die expression, `<count>d<faces>`. `resolveUnarmedStrike` averages the flat arm with
75
+ // /^(\d+)d(\d+)$/ and silently averages anything else as 1, so a modifier tail ("1d6+1") or a missing
76
+ // half ("d6", "1d") must refuse here rather than mis-average there. The `i` arm keeps an authored `1D6`.
77
+ dice: z.union([z.string().regex(/^\d+d\d+$/i), z.object({ column: z.string().min(1) })]).optional(),
78
+ abilities: z.array(abilityEnum).nonempty().optional(),
79
+ condition: conditionField,
80
+ subject: subjectField
81
+ }),
82
+ z.object({
83
+ kind: z.literal("weapon-ability"),
84
+ ability: z.union([abilityEnum, z.literal("spellcasting")]),
85
+ weapons: z.union([z.literal("chosen"), z.string(), z.array(z.string())]).optional(),
86
+ condition: conditionField,
87
+ subject: subjectField
88
+ }),
89
+ z.object({
90
+ kind: z.literal("roll-modifier"),
91
+ mode: z.enum(["advantage", "disadvantage", "reroll", "add-d4"]),
92
+ roll: z.enum(["ability-check", "saving-throw", "attack", "any"]),
93
+ scope: z.string().optional(),
94
+ condition: conditionField,
95
+ subject: subjectField
96
+ }),
97
+ z.object({
98
+ kind: z.literal("extra-attack"),
99
+ // `count` is EXTRA attacks, never total attacks: the engine renders `1 + count`.
100
+ count: z.number().int().positive(),
101
+ // R4-G7 §7.3, the `speed-bonus` twin: the SRD 5e Fighter's one Extra Attack feature carries its whole
102
+ // 5 / 11 / 20 progression here, because `bucketFeaturesByLevel` emits one overlay record into every
103
+ // `gained_at` bucket and a per-copy count cannot be authored.
104
+ scales_at: z.array(z.object({ level: z.number().int().min(1).max(20), count: z.number().int().positive() })).optional(),
105
+ condition: conditionField,
106
+ subject: subjectField
107
+ }),
108
+ z.object({
109
+ kind: z.literal("crit-range"),
110
+ min_roll: z.number().int().min(2).max(20),
111
+ applies_to: z.enum(["weapon", "spell", "all"]).optional(),
112
+ condition: conditionField,
113
+ subject: subjectField
114
+ }),
115
+ z.object({
116
+ kind: z.literal("reroll-damage"),
117
+ max_reroll: z.number().int().positive(),
118
+ applies_to: z.enum(["weapon", "spell", "all"]).optional(),
119
+ once_per_die: z.boolean().optional(),
120
+ condition: conditionField,
121
+ subject: subjectField
122
+ }),
123
+ z.object({ kind: z.literal("attack-rule"), flag: z.enum(["no-ranged-in-melee-disadvantage"]), condition: conditionField, subject: subjectField }),
124
+ // R4-G1a declared these seven converter-emitted arms with the engine folding NOTHING for them
125
+ // (spec D3). R4-G3a §3-§7 gives SIX of them semantics: immunity / vulnerability FOLD (defenses
126
+ // pane); temp-hp / heal / extra-action are still NOT folded, the plugin reads them raw as
127
+ // row-local captions; save-outcome FOLDS (save-chip tag rail).
128
+ // `ability-score-increase` folds FLAT at the fold since R4-G3b §4 (fixed-list arms only; `chosen` arms
129
+ // are the ASI slot's second encoding; `max` is unread by user ruling).
130
+ // Damage immunity / vulnerability. NOT `immune-condition`, which is condition immunity.
131
+ z.object({ kind: z.literal("immunity"), damage_type: z.string().min(1), condition: conditionField, subject: subjectField }),
132
+ z.object({ kind: z.literal("vulnerability"), damage_type: z.string().min(1), condition: conditionField, subject: subjectField }),
133
+ // `amount` is the open non-empty STRING `damage-bonus.amount` already uses: "2d6", "5", "your Artificer level".
134
+ z.object({ kind: z.literal("temp-hp"), amount: z.string().min(1), condition: conditionField, subject: subjectField }),
135
+ z.object({ kind: z.literal("heal"), amount: z.string().min(1), condition: conditionField, subject: subjectField }),
136
+ z.object({
137
+ kind: z.literal("ability-score-increase"),
138
+ // "chosen" follows `weapon-ability.weapons`; the list follows `unarmored-ac.abilities`.
139
+ abilities: z.union([z.literal("chosen"), z.array(abilityEnum).min(1)]),
140
+ amount: z.number().int().positive(),
141
+ // Required-WITH-NULL, never optional: "the prose stated no cap" and "the extractor forgot" must not look alike.
142
+ choose: z.number().int().positive().nullable(),
143
+ max: z.number().int().positive().nullable(),
144
+ condition: conditionField,
145
+ subject: subjectField
146
+ }).refine((e) => e.abilities === "chosen" === (e.choose !== null), {
147
+ path: ["choose"],
148
+ message: 'choose is required IFF abilities is "chosen": a fixed LIST is its own count'
149
+ }),
150
+ // `extra-action` is an additional ACTION on your turn; `extra-attack` is attacks WITHIN the Attack action.
151
+ z.object({
152
+ kind: z.literal("extra-action"),
153
+ count: z.number().int().positive(),
154
+ action_type: z.enum(["action", "bonus-action", "reaction"]),
155
+ condition: conditionField,
156
+ subject: subjectField
157
+ }),
158
+ // `save-outcome` remaps the OUTCOME; `roll-modifier` modifies the ROLL.
159
+ z.object({
160
+ kind: z.literal("save-outcome"),
161
+ ability: z.union([abilityEnum, z.literal("any")]),
162
+ on_success: z.enum(["none", "half", "full"]),
163
+ on_failure: z.enum(["none", "half", "full"]),
164
+ applies_to: z.string().nullable(),
165
+ condition: conditionField,
166
+ subject: subjectField
167
+ })
168
+ ]);
169
+
170
+ export {
171
+ featureEffectSchema
172
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  STATIC_FALLBACK_CONTEXT,
3
3
  convertDescToTags
4
- } from "./chunk-RAWJTA45.js";
4
+ } from "./chunk-DEEHHLAP.js";
5
5
 
6
6
  // src/spell/spell.enrichment.ts
7
7
  function enrichSpell(raw) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  slugify
3
- } from "./chunk-LV4SNWZQ.js";
3
+ } from "./chunk-6X4KUDMU.js";
4
4
 
5
5
  // src/entities/base-item-resolver.ts
6
6
  function unwrapWikilink(input) {
@@ -10,7 +10,11 @@ function unwrapWikilink(input) {
10
10
  const pipe = inner.indexOf("|");
11
11
  return pipe === -1 ? inner : inner.slice(0, pipe);
12
12
  }
13
- var FOLDER_TO_TYPE = { Weapons: "weapon", Armor: "armor" };
13
+ var FOLDER_TO_TYPE = {
14
+ Weapons: "weapon",
15
+ Armor: "armor",
16
+ "Magic Items": "item"
17
+ };
14
18
  function vaultPathToSlug(target) {
15
19
  const segments = target.split("/").map((s) => s.trim()).filter((s) => s.length > 0);
16
20
  if (segments.length === 0) return "";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseRace
3
- } from "./chunk-PHQRGDRQ.js";
3
+ } from "./chunk-XGOFP7AQ.js";
4
4
 
5
5
  // src/race/race.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,12 +1,16 @@
1
1
  import {
2
2
  raceEntitySchema
3
- } from "./chunk-2BWAID3Y.js";
3
+ } from "./chunk-FDBWYMZK.js";
4
+ import {
5
+ aliasFeatureListActionCost
6
+ } from "./chunk-4U55VNC7.js";
4
7
 
5
8
  // src/race/race.parser.ts
6
9
  import { parseYaml } from "@archivist-gg/core";
7
10
  function parseRace(source) {
8
11
  const raw = parseYaml(source, ["name", "slug"]);
9
12
  if (!raw.success) return raw;
13
+ aliasFeatureListActionCost(raw.data.traits);
10
14
  const result = raceEntitySchema.safeParse({ edition: "2014", ...raw.data });
11
15
  if (!result.success) {
12
16
  return { success: false, error: `race schema validation failed: ${result.error.message}` };
@@ -0,0 +1,8 @@
1
+ // src/pc/pc.defense-normalize.ts
2
+ function toDefenseSlug(s) {
3
+ return s.trim().toLowerCase().replace(/\s+/g, " ");
4
+ }
5
+
6
+ export {
7
+ toDefenseSlug
8
+ };