@archivist-gg/dnd5e 0.3.1 → 0.3.5

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-2VDTW7QO.js +1580 -0
  16. package/dist/{chunk-ZZGEVJKX.js → chunk-3JOTQW7A.js} +1 -1
  17. package/dist/{chunk-5ZBE4CWV.js → chunk-3PZHBLFG.js} +1 -1
  18. package/dist/chunk-4U55VNC7.js +22 -0
  19. package/dist/{chunk-4JHOHWBI.js → chunk-4XTNPNQB.js} +1 -1
  20. package/dist/{chunk-HCP3GMHS.js → chunk-5N2GW72F.js} +15 -2
  21. package/dist/{chunk-42Y5IJKT.js → chunk-5RKLL2AD.js} +5 -1
  22. package/dist/{chunk-SE7AZDR3.js → chunk-63XQXPZL.js} +29 -3
  23. package/dist/{chunk-4F6AIZ36.js → chunk-6BFWZW6T.js} +1 -1
  24. package/dist/{chunk-GJOI2GHP.js → chunk-6GNXYAYP.js} +1 -1
  25. package/dist/{chunk-66XXW2Z2.js → chunk-6PXTNCBP.js} +2 -2
  26. package/dist/chunk-6X2NV665.js +33 -0
  27. package/dist/{chunk-LV4SNWZQ.js → chunk-6X4KUDMU.js} +8 -1
  28. package/dist/{chunk-ZMR77DDL.js → chunk-AW3EHF3L.js} +1 -1
  29. package/dist/{chunk-TLYDBT5F.js → chunk-CGRLQ34U.js} +64 -7
  30. package/dist/chunk-CJYCVNW2.js +7 -0
  31. package/dist/{chunk-XLSVNUTK.js → chunk-CV5XDJKU.js} +1 -1
  32. package/dist/{chunk-RAWJTA45.js → chunk-DEEHHLAP.js} +1 -1
  33. package/dist/{chunk-6IRYTM5V.js → chunk-DPUJDOKV.js} +119 -28
  34. package/dist/{chunk-GE4TLPWM.js → chunk-EHDATXP7.js} +1 -1
  35. package/dist/chunk-EVMUJYUD.js +195 -0
  36. package/dist/{chunk-K6Q5OUGK.js → chunk-GFZ7PITJ.js} +86 -10
  37. package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
  38. package/dist/{chunk-TN6ZLTNU.js → chunk-I2NDWFDD.js} +51 -4
  39. package/dist/{chunk-3DZ74AKK.js → chunk-IN4M4X7P.js} +8 -2
  40. package/dist/chunk-IUAAXXNO.js +58 -0
  41. package/dist/{chunk-NXSWH3AE.js → chunk-IWRVOCT5.js} +1 -1
  42. package/dist/{chunk-XJNECUPT.js → chunk-JC5UNIVM.js} +19 -0
  43. package/dist/{chunk-24GFQU7T.js → chunk-JSZ4H6AQ.js} +1 -1
  44. package/dist/{chunk-XTIQWSFM.js → chunk-KADJZGZY.js} +1 -1
  45. package/dist/{chunk-L7E2UT5A.js → chunk-KK33PIDN.js} +5 -1
  46. package/dist/chunk-KPEJMNRD.js +302 -0
  47. package/dist/chunk-KU3YLF6D.js +106 -0
  48. package/dist/{chunk-5INR4NSL.js → chunk-LDDCNPHD.js} +1 -1
  49. package/dist/chunk-LVQLWRCU.js +15 -0
  50. package/dist/chunk-MI7P3NX3.js +161 -0
  51. package/dist/{chunk-PHQRGDRQ.js → chunk-N3A5ICBO.js} +5 -1
  52. package/dist/chunk-O4SRD3J5.js +165 -0
  53. package/dist/chunk-OAXWSVJB.js +81 -0
  54. package/dist/chunk-OZP6JFGT.js +162 -0
  55. package/dist/{chunk-NAEPSZAM.js → chunk-PDJSUH24.js} +24 -2
  56. package/dist/chunk-PP2UHLBA.js +50 -0
  57. package/dist/chunk-PVCEA4HZ.js +6 -0
  58. package/dist/{chunk-RSDRMH54.js → chunk-RF4TYCA2.js} +1 -1
  59. package/dist/chunk-RLGQ5PZZ.js +16 -0
  60. package/dist/chunk-RVXC6ZHY.js +89 -0
  61. package/dist/chunk-S7REMWW4.js +21 -0
  62. package/dist/{chunk-7VJ6BOA5.js → chunk-SMLXZUBH.js} +9 -1
  63. package/dist/chunk-TOM66P2P.js +38 -0
  64. package/dist/chunk-UA5KS2PL.js +26 -0
  65. package/dist/{chunk-N6N74K6X.js → chunk-UBBKSYBY.js} +1 -1
  66. package/dist/{chunk-353WXS6S.js → chunk-UIRMA5KM.js} +1 -1
  67. package/dist/{chunk-BIK4DFWK.js → chunk-ULCQ73LA.js} +1 -1
  68. package/dist/{chunk-2BWAID3Y.js → chunk-UMWIPLLM.js} +29 -3
  69. package/dist/chunk-UQ3ZFCWD.js +178 -0
  70. package/dist/chunk-UXPGW4TI.js +101 -0
  71. package/dist/chunk-V2MJE6NC.js +133 -0
  72. package/dist/{chunk-D3CLOBE3.js → chunk-V373TRJE.js} +48 -10
  73. package/dist/chunk-VQAXTIBW.js +100 -0
  74. package/dist/chunk-W74BCA7V.js +159 -0
  75. package/dist/{chunk-WP2CX2JO.js → chunk-WELIKLZF.js} +22 -1
  76. package/dist/{chunk-MDEJHMKK.js → chunk-WHVLNFTG.js} +26 -5
  77. package/dist/{chunk-GZ46OVGZ.js → chunk-WSVMLNTO.js} +1 -1
  78. package/dist/{chunk-4LZJXJJS.js → chunk-WXWPK3LI.js} +6 -2
  79. package/dist/chunk-X3D6LXUU.js +185 -0
  80. package/dist/chunk-XJ2F2WO6.js +8 -0
  81. package/dist/{chunk-73HOFQQ5.js → chunk-Y4AGLCY6.js} +1 -1
  82. package/dist/chunk-YBLDE5CG.js +42 -0
  83. package/dist/{chunk-JEYQU3JT.js → chunk-YHYTW5QA.js} +2 -18
  84. package/dist/{chunk-PZOWBI3B.js → chunk-YLL2QXY2.js} +7 -2
  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 +239 -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 +185 -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 +31 -2
  153. package/dist/pc/pc.equipment.js +12 -8
  154. package/dist/pc/pc.feature-effects.d.ts +234 -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 +133 -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 +85 -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,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
+ };
@@ -0,0 +1,159 @@
1
+ import {
2
+ durationSchema
3
+ } from "./chunk-WYGD2UGN.js";
4
+ import {
5
+ actionCostEnum,
6
+ resourceConsumptionSchema,
7
+ resourceSchema
8
+ } from "./chunk-YLL2QXY2.js";
9
+ import {
10
+ choiceSchema
11
+ } from "./chunk-IWRVOCT5.js";
12
+ import {
13
+ featureEffectSchema
14
+ } from "./chunk-X3D6LXUU.js";
15
+
16
+ // src/schemas/feature-schema.ts
17
+ import { z as z2 } from "zod";
18
+
19
+ // src/schemas/attack-schema.ts
20
+ import { z } from "zod";
21
+ var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
22
+ var attackSchema = z.object({
23
+ name: z.string().min(1),
24
+ type: z.enum(["melee", "ranged", "spell"]),
25
+ ability: z.union([abilityEnum, z.array(abilityEnum).nonempty()]).optional(),
26
+ damage: z.string().optional(),
27
+ damage_type: z.string().optional(),
28
+ action: z.enum(["action", "bonus-action", "reaction"]).optional(),
29
+ properties: z.array(z.string()).optional(),
30
+ range: z.object({
31
+ normal: z.number().int().nonnegative().optional(),
32
+ long: z.number().int().nonnegative().optional(),
33
+ reach: z.number().int().nonnegative().optional()
34
+ }).optional(),
35
+ condition: z.string().optional(),
36
+ // R4-G6 §5: declared on the `Attack` interface since the monster port, stripped by this schema until now.
37
+ // Monsters are the only carriers (8,176 bonuses, 1,796 extra-damage riders in the converter corpus).
38
+ bonus: z.number().optional(),
39
+ extra_damage: z.object({ dice: z.string(), type: z.string() }).optional()
40
+ });
41
+
42
+ // src/schemas/feature-schema.ts
43
+ var companionSchema = z2.object({
44
+ statblock: z2.string().optional(),
45
+ statblock_wikilink: z2.string().nullable().optional(),
46
+ count: z2.string().optional(),
47
+ count_scaling: z2.array(z2.object({
48
+ level: z2.number().optional(),
49
+ count: z2.string().optional(),
50
+ source_quote: z2.string().optional()
51
+ })).optional(),
52
+ shares_action_economy: z2.boolean().optional(),
53
+ turn_order: z2.string().optional(),
54
+ turn_order_source_quote: z2.string().nullable().optional(),
55
+ proficiency_bonus_scaled: z2.array(z2.object({
56
+ stat: z2.string().optional(),
57
+ formula: z2.string().optional(),
58
+ source_quote: z2.string().optional()
59
+ })).optional(),
60
+ lifecycle: z2.object({
61
+ created_by: z2.string().optional(),
62
+ recreate_on: z2.string().nullable().optional(),
63
+ previous_instance_perishes: z2.boolean().nullable().optional(),
64
+ vanishes_if_owner_dies: z2.boolean().nullable().optional(),
65
+ vanishes_after: z2.string().nullable().optional(),
66
+ source_quote: z2.string().optional()
67
+ }).nullable().optional(),
68
+ autonomy: z2.object({
69
+ when: z2.string().optional(),
70
+ behaviour: z2.string().optional(),
71
+ source_quote: z2.string().optional()
72
+ }).nullable().optional(),
73
+ variants: z2.array(z2.object({
74
+ axis: z2.string().optional(),
75
+ choices: z2.array(z2.string()).optional(),
76
+ chosen_at: z2.string().optional(),
77
+ source_quote: z2.string().optional()
78
+ })).optional(),
79
+ base_statblock: z2.string().nullable().optional(),
80
+ overrides: z2.array(z2.object({
81
+ field: z2.string().optional(),
82
+ value: z2.string().optional(),
83
+ source_quote: z2.string().optional()
84
+ })).optional(),
85
+ source_quote: z2.string().optional()
86
+ });
87
+ var featureObjectSchema = z2.lazy(
88
+ () => z2.object({
89
+ id: z2.string().min(1).optional(),
90
+ name: z2.string().min(1),
91
+ description: z2.string().optional(),
92
+ entries: z2.array(z2.string()).optional(),
93
+ choices: z2.array(choiceSchema).optional(),
94
+ grants_resource: z2.string().optional(),
95
+ consumes: resourceConsumptionSchema.optional(),
96
+ attacks: z2.array(attackSchema).optional(),
97
+ action: z2.enum(["action", "bonus-action", "reaction", "free", "special"]).optional(),
98
+ trigger: z2.string().optional(),
99
+ dc_formula: z2.string().optional(),
100
+ effects: z2.array(featureEffectSchema).optional(),
101
+ sub_features: z2.array(featureSchemaTyped).optional(),
102
+ resources: z2.array(resourceSchema).optional(),
103
+ // Phase 3 activatable buffs: a feature flagged `activatable` folds its effects
104
+ // only while its id is present in state.active_buffs. `passive` renders a tag;
105
+ // `duration` is a static label (no live countdown this phase).
106
+ activatable: z2.boolean().optional(),
107
+ passive: z2.boolean().optional(),
108
+ duration: durationSchema.optional(),
109
+ // Converter feature-level keys (164 census strip rows, class 80 + subclass 84; counts per finding 2):
110
+ class_source: z2.string().min(1).optional(),
111
+ // class 646 + subclass 1616 leaves
112
+ rendering_hint: z2.string().optional(),
113
+ // always "" — NEVER .min(1)
114
+ subclass_short_name: z2.string().min(1).optional(),
115
+ // 1616
116
+ subclass_source: z2.string().min(1).optional(),
117
+ // 1616
118
+ header: z2.number().optional(),
119
+ // 1,237 carriers, ALL numbers
120
+ is_class_feature_variant: z2.boolean().optional(),
121
+ gain_subclass_feature: z2.boolean().optional(),
122
+ gain_subclass_feature_has_content: z2.boolean().optional(),
123
+ type: z2.string().min(1).optional(),
124
+ // values {"inset","item"}
125
+ recharge: z2.object({
126
+ // ONE class doc (UA Mystic), FOUR features, ALL
127
+ type: z2.enum(["recharge_on_roll", "per_day", "per_short_rest", "per_long_rest"]),
128
+ // {type:"per_day",param:1..4}
129
+ param: z2.number()
130
+ // tree-wide nested-recharge type census: exactly these
131
+ }).optional(),
132
+ // four members (gate1-r1 f9) = the EXISTING FeatureRecharge type
133
+ // SRD-BUNDLE-side keys (the converter emits NEITHER at feature level; finding 14 — our own
134
+ // generator emits them and featureSchema strips them, losing the SRD Dragonborn Breath Weapon's
135
+ // action cost and save DC). MAPPED since R4-G3a §10: `action_cost` is aliased onto `action` by
136
+ // the race / class / subclass parsers, and `save` renders as the feature card's Save line
137
+ // (`dc_formula` is echoed as TEXT, never evaluated).
138
+ action_cost: actionCostEnum.optional(),
139
+ // ONE vocabulary, imported from schemas/resource-schema
140
+ // (no cycle: resource-schema imports neither; two spellings of a closed enum is the G1a
141
+ // "fifth enum consumer" drift — gate1-r2 NEW-12)
142
+ save: z2.object({ ability: z2.string().min(1), dc_formula: z2.string().min(1) }).optional(),
143
+ // R4-G5 §7 (ruling G5-UR2): DECLARED so the class / subclass / race parsers stop stripping it on the day the
144
+ // G7 re-emit lands. The converter withholds it today (`P9_NOT_EMITTED_FIELDS`), so 0 converted and 0 bundle
145
+ // documents carry it and the census cannot see this key in either state (§12.1); the 47-block cache
146
+ // round-trip in `tests/feature-schema-widening.test.ts` is its instrument.
147
+ companion: companionSchema.optional()
148
+ })
149
+ );
150
+ var featureSchemaTyped = featureObjectSchema.refine(
151
+ (f) => f.description !== void 0 || f.entries !== void 0 && f.entries.length > 0,
152
+ { message: "feature requires either description or non-empty entries" }
153
+ );
154
+ var featureSchema = featureSchemaTyped;
155
+
156
+ export {
157
+ featureObjectSchema,
158
+ featureSchema
159
+ };
@@ -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
  };
@@ -5,23 +5,31 @@ import {
5
5
  actionCostEnum,
6
6
  resetTriggerEnum,
7
7
  resourceConsumptionSchema
8
- } from "./chunk-PZOWBI3B.js";
8
+ } from "./chunk-YLL2QXY2.js";
9
9
  import {
10
10
  featureEffectSchema
11
- } from "./chunk-NXR3BD2V.js";
11
+ } from "./chunk-X3D6LXUU.js";
12
+ import {
13
+ additionalSpellsEntrySchema,
14
+ imageField,
15
+ progressionSchema
16
+ } from "./chunk-UA5KS2PL.js";
12
17
 
13
18
  // src/optional-feature/optional-feature.schema.ts
14
19
  import { z } from "zod";
15
20
  var editionEnum = z.enum(["2014", "2024"]);
16
21
  var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
17
22
  var wikilinkRegex = /^\[\[[^[\]]+\]\]$/;
23
+ var namedRefSchema = z.object({ name: z.string().min(1), source: z.string().min(1).optional(), visible_stats: z.boolean().optional() });
18
24
  var prerequisiteSchema = z.discriminatedUnion("kind", [
19
- z.object({ kind: z.literal("level"), min: z.number().int().positive() }),
25
+ z.object({ kind: z.literal("level"), min: z.number().int().positive(), class: namedRefSchema.optional(), subclass: namedRefSchema.optional() }),
20
26
  z.object({ kind: z.literal("spell-known"), spell: z.string().regex(wikilinkRegex) }),
21
27
  z.object({ kind: z.literal("pact"), pact: z.enum(["tome", "blade", "chain", "talisman"]) }),
22
28
  z.object({ kind: z.literal("class"), class: z.string().regex(wikilinkRegex) }),
23
29
  z.object({ kind: z.literal("ability"), ability: abilityEnum, min: z.number().int().positive() }),
24
- z.object({ kind: z.literal("other"), detail: z.string() })
30
+ z.object({ kind: z.literal("other"), detail: z.string() }),
31
+ z.object({ kind: z.literal("optionalfeature"), optionalfeature: z.string().regex(wikilinkRegex) }),
32
+ z.object({ kind: z.literal("spell-choose"), choose: z.string().min(1), entry: z.string().optional(), entry_summary: z.string().optional() })
25
33
  ]);
26
34
  var usesSchema = z.object({
27
35
  max: z.union([z.number(), z.string()]),
@@ -44,7 +52,20 @@ var optionalFeatureEntitySchema = z.object({
44
52
  passive: z.boolean().optional(),
45
53
  // Phase 3 activatable buffs: an activatable boon folds its effects only while
46
54
  // its slug is present in state.active_buffs (toggled in the PoolTab).
47
- activatable: z.boolean().optional()
55
+ activatable: z.boolean().optional(),
56
+ rendering_hint: z.string().optional(),
57
+ // the ONE root with load-bearing VALUES (88 records, G4's)
58
+ additional_spells: z.array(additionalSpellsEntrySchema).optional(),
59
+ // 54
60
+ is_class_feature_variant: z.boolean().optional(),
61
+ // 25
62
+ has_fluff_images: z.boolean().optional(),
63
+ // 2
64
+ feat_progression: z.array(progressionSchema).optional(),
65
+ // 1
66
+ optionalfeature_progression: z.array(progressionSchema).optional(),
67
+ // 1
68
+ image: imageField
48
69
  });
49
70
 
50
71
  export {
@@ -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 "";
@@ -0,0 +1,185 @@
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
+ // The attunement CAP, as data. `value` is an absolute cap ("you can now attune to up to four"),
30
+ // never an increment, so two grants MAX rather than sum and a grant can never lower the cap:
31
+ // recalc folds it as Math.max(3, granted). `scales_at` carries a whole progression on ONE effect —
32
+ // the `speed-bonus` / `extra-attack` shape (R4-G7 §7.3) — because the overlay has no level axis and
33
+ // the Artificer's three steps (10 -> 4, 14 -> 5, 18 -> 6) would otherwise need three separate
34
+ // features, one of which is embedded prose inside another feature's description.
35
+ kind: z.literal("attunement-limit"),
36
+ value: z.number().int().positive(),
37
+ scales_at: z.array(z.object({ level: z.number().int().min(1).max(20), value: z.number().int().positive() })).optional(),
38
+ condition: conditionField,
39
+ subject: subjectField
40
+ }),
41
+ z.object({
42
+ kind: z.literal("sense"),
43
+ type: z.enum(["darkvision", "blindsight", "tremorsense", "truesight"]),
44
+ range: z.number().int().nonnegative(),
45
+ condition: conditionField,
46
+ subject: subjectField
47
+ }),
48
+ // `condition` here is the condition NAME being applied.
49
+ z.object({
50
+ kind: z.literal("apply-condition"),
51
+ condition: z.string().min(1),
52
+ duration: z.string().optional(),
53
+ ends_on: z.array(z.string()).optional(),
54
+ save_repeat: z.object({ ability: z.string().min(1), timing: z.string().min(1) }).optional(),
55
+ subject: subjectField
56
+ }),
57
+ z.object({
58
+ kind: z.literal("damage-bonus"),
59
+ // intentionally an open string, NOT a damage-type enum: must accept the
60
+ // literal "chosen" (player-selected damage type at action time) alongside
61
+ // canonical damage types. Do not tighten to an enum.
62
+ damage_type: z.string().min(1),
63
+ amount: z.string().min(1),
64
+ applies_to: z.enum(["weapon", "spell", "all"]).optional(),
65
+ condition: conditionField,
66
+ subject: subjectField
67
+ }),
68
+ z.object({
69
+ kind: z.literal("proficiency"),
70
+ proficiency_type: z.enum(["skill", "tool", "language", "saving-throw", "armor", "weapon"]),
71
+ value: z.string().min(1),
72
+ expertise: z.boolean().optional(),
73
+ condition: conditionField,
74
+ subject: subjectField
75
+ }),
76
+ z.object({ kind: z.literal("ac-bonus"), value: z.number().int(), requires_armor: z.boolean().optional(), condition: conditionField, subject: subjectField }),
77
+ z.object({
78
+ kind: z.literal("unarmored-ac"),
79
+ abilities: z.array(abilityEnum),
80
+ base: z.number().int().optional(),
81
+ allow_shield: z.boolean().optional(),
82
+ condition: conditionField,
83
+ subject: subjectField
84
+ }),
85
+ z.object({
86
+ kind: z.literal("unarmed-strike"),
87
+ // R4-G7 §7.2: a bare die expression, `<count>d<faces>`. `resolveUnarmedStrike` averages the flat arm with
88
+ // /^(\d+)d(\d+)$/ and silently averages anything else as 1, so a modifier tail ("1d6+1") or a missing
89
+ // half ("d6", "1d") must refuse here rather than mis-average there. The `i` arm keeps an authored `1D6`.
90
+ dice: z.union([z.string().regex(/^\d+d\d+$/i), z.object({ column: z.string().min(1) })]).optional(),
91
+ abilities: z.array(abilityEnum).nonempty().optional(),
92
+ condition: conditionField,
93
+ subject: subjectField
94
+ }),
95
+ z.object({
96
+ kind: z.literal("weapon-ability"),
97
+ ability: z.union([abilityEnum, z.literal("spellcasting")]),
98
+ weapons: z.union([z.literal("chosen"), z.string(), z.array(z.string())]).optional(),
99
+ condition: conditionField,
100
+ subject: subjectField
101
+ }),
102
+ z.object({
103
+ kind: z.literal("roll-modifier"),
104
+ mode: z.enum(["advantage", "disadvantage", "reroll", "add-d4"]),
105
+ roll: z.enum(["ability-check", "saving-throw", "attack", "any"]),
106
+ scope: z.string().optional(),
107
+ condition: conditionField,
108
+ subject: subjectField
109
+ }),
110
+ z.object({
111
+ kind: z.literal("extra-attack"),
112
+ // `count` is EXTRA attacks, never total attacks: the engine renders `1 + count`.
113
+ count: z.number().int().positive(),
114
+ // R4-G7 §7.3, the `speed-bonus` twin: the SRD 5e Fighter's one Extra Attack feature carries its whole
115
+ // 5 / 11 / 20 progression here, because `bucketFeaturesByLevel` emits one overlay record into every
116
+ // `gained_at` bucket and a per-copy count cannot be authored.
117
+ scales_at: z.array(z.object({ level: z.number().int().min(1).max(20), count: z.number().int().positive() })).optional(),
118
+ condition: conditionField,
119
+ subject: subjectField
120
+ }),
121
+ z.object({
122
+ kind: z.literal("crit-range"),
123
+ min_roll: z.number().int().min(2).max(20),
124
+ applies_to: z.enum(["weapon", "spell", "all"]).optional(),
125
+ condition: conditionField,
126
+ subject: subjectField
127
+ }),
128
+ z.object({
129
+ kind: z.literal("reroll-damage"),
130
+ max_reroll: z.number().int().positive(),
131
+ applies_to: z.enum(["weapon", "spell", "all"]).optional(),
132
+ once_per_die: z.boolean().optional(),
133
+ condition: conditionField,
134
+ subject: subjectField
135
+ }),
136
+ z.object({ kind: z.literal("attack-rule"), flag: z.enum(["no-ranged-in-melee-disadvantage"]), condition: conditionField, subject: subjectField }),
137
+ // R4-G1a declared these seven converter-emitted arms with the engine folding NOTHING for them
138
+ // (spec D3). R4-G3a §3-§7 gives SIX of them semantics: immunity / vulnerability FOLD (defenses
139
+ // pane); temp-hp / heal / extra-action are still NOT folded, the plugin reads them raw as
140
+ // row-local captions; save-outcome FOLDS (save-chip tag rail).
141
+ // `ability-score-increase` folds FLAT at the fold since R4-G3b §4 (fixed-list arms only; `chosen` arms
142
+ // are the ASI slot's second encoding; `max` is unread by user ruling).
143
+ // Damage immunity / vulnerability. NOT `immune-condition`, which is condition immunity.
144
+ z.object({ kind: z.literal("immunity"), damage_type: z.string().min(1), condition: conditionField, subject: subjectField }),
145
+ z.object({ kind: z.literal("vulnerability"), damage_type: z.string().min(1), condition: conditionField, subject: subjectField }),
146
+ // `amount` is the open non-empty STRING `damage-bonus.amount` already uses: "2d6", "5", "your Artificer level".
147
+ z.object({ kind: z.literal("temp-hp"), amount: z.string().min(1), condition: conditionField, subject: subjectField }),
148
+ z.object({ kind: z.literal("heal"), amount: z.string().min(1), condition: conditionField, subject: subjectField }),
149
+ z.object({
150
+ kind: z.literal("ability-score-increase"),
151
+ // "chosen" follows `weapon-ability.weapons`; the list follows `unarmored-ac.abilities`.
152
+ abilities: z.union([z.literal("chosen"), z.array(abilityEnum).min(1)]),
153
+ amount: z.number().int().positive(),
154
+ // Required-WITH-NULL, never optional: "the prose stated no cap" and "the extractor forgot" must not look alike.
155
+ choose: z.number().int().positive().nullable(),
156
+ max: z.number().int().positive().nullable(),
157
+ condition: conditionField,
158
+ subject: subjectField
159
+ }).refine((e) => e.abilities === "chosen" === (e.choose !== null), {
160
+ path: ["choose"],
161
+ message: 'choose is required IFF abilities is "chosen": a fixed LIST is its own count'
162
+ }),
163
+ // `extra-action` is an additional ACTION on your turn; `extra-attack` is attacks WITHIN the Attack action.
164
+ z.object({
165
+ kind: z.literal("extra-action"),
166
+ count: z.number().int().positive(),
167
+ action_type: z.enum(["action", "bonus-action", "reaction"]),
168
+ condition: conditionField,
169
+ subject: subjectField
170
+ }),
171
+ // `save-outcome` remaps the OUTCOME; `roll-modifier` modifies the ROLL.
172
+ z.object({
173
+ kind: z.literal("save-outcome"),
174
+ ability: z.union([abilityEnum, z.literal("any")]),
175
+ on_success: z.enum(["none", "half", "full"]),
176
+ on_failure: z.enum(["none", "half", "full"]),
177
+ applies_to: z.string().nullable(),
178
+ condition: conditionField,
179
+ subject: subjectField
180
+ })
181
+ ]);
182
+
183
+ export {
184
+ featureEffectSchema
185
+ };
@@ -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
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFeat
3
- } from "./chunk-XTIQWSFM.js";
3
+ } from "./chunk-KADJZGZY.js";
4
4
 
5
5
  // src/feat/feat.codec.ts
6
6
  import yaml from "js-yaml";
@@ -0,0 +1,42 @@
1
+ // src/feat/feat.category-codes.ts
2
+ var FEAT_CATEGORY_CODE_LABELS = {
3
+ D: "Dragonmark",
4
+ G: "General",
5
+ O: "Origin",
6
+ FS: "Fighting Style",
7
+ EB: "Epic Boon",
8
+ DG: "Dark Gift",
9
+ "FS:P": "Fighting Style",
10
+ "FS:R": "Fighting Style"
11
+ };
12
+ function featCategoryLabel(code) {
13
+ var _a;
14
+ return (_a = FEAT_CATEGORY_CODE_LABELS[code]) != null ? _a : code;
15
+ }
16
+ var FEAT_PROGRESSION_CATEGORY = {
17
+ FS: "fighting-style",
18
+ "FS:P": "fighting-style",
19
+ "FS:R": "fighting-style",
20
+ EB: "epic-boon",
21
+ O: "origin",
22
+ G: "general"
23
+ };
24
+ var FEATURE_TYPE_FOR_CATEGORY = {
25
+ "fighting-style": "fighting_style"
26
+ };
27
+ function featProgressionCategory(codes) {
28
+ for (const code of codes != null ? codes : []) {
29
+ if (Object.prototype.hasOwnProperty.call(FEAT_PROGRESSION_CATEGORY, code)) {
30
+ return FEAT_PROGRESSION_CATEGORY[code];
31
+ }
32
+ }
33
+ return void 0;
34
+ }
35
+
36
+ export {
37
+ FEAT_CATEGORY_CODE_LABELS,
38
+ featCategoryLabel,
39
+ FEAT_PROGRESSION_CATEGORY,
40
+ FEATURE_TYPE_FOR_CATEGORY,
41
+ featProgressionCategory
42
+ };
@@ -1,24 +1,8 @@
1
1
  // src/pc/decision-recognizer.ts
2
2
  var DECISION_SIGNAL = [/\bchoose (one|two|three|a|an)\b/i, /\bof your choice\b/i, /\byour choice of\b/i];
3
- var ASI_OR_FEAT = {
4
- kind: "select-inline",
5
- id: "asi-or-feat",
6
- count: 1,
7
- options: [
8
- {
9
- value: "asi",
10
- label: "Ability Score Increase",
11
- choices: [{ kind: "ability-points", id: "asi", points: 2, max_per: 2 }]
12
- },
13
- {
14
- value: "feat",
15
- label: "Feat",
16
- choices: [{ kind: "select-entity", id: "feat", entity_type: "feat", count: 1 }]
17
- }
18
- ]
19
- };
3
+ var ASI_FEAT = { kind: "select-entity", id: "feat", entity_type: "feat", count: 1 };
20
4
  var TABLE = {
21
- "ability-score-improvement": [ASI_OR_FEAT],
5
+ "ability-score-improvement": [ASI_FEAT],
22
6
  "expertise": [{ kind: "select-proficiency", id: "expertise", count: 2, domain: "skill", from_proficient: true, expertise: true }],
23
7
  "fighting-style": [{ kind: "select-entity", id: "fighting-style", count: 1, entity_type: "optional-feature", where: { feature_type: "fighting_style", available_to: "self" } }]
24
8
  };