@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,3 +1,7 @@
1
+ import {
2
+ imageField
3
+ } from "./chunk-UA5KS2PL.js";
4
+
1
5
  // src/item/item.schema.ts
2
6
  import { z } from "zod";
3
7
  var tier1Conditions = [
@@ -41,6 +45,7 @@ var conditionalBonusSchema = z.object({
41
45
  when: z.array(conditionSchema)
42
46
  });
43
47
  var numberOrConditional = z.union([z.number().int(), conditionalBonusSchema]);
48
+ var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
44
49
  var bonusesSchema = z.object({
45
50
  ac: numberOrConditional.optional(),
46
51
  weapon_attack: numberOrConditional.optional(),
@@ -48,6 +53,12 @@ var bonusesSchema = z.object({
48
53
  spell_attack: numberOrConditional.optional(),
49
54
  spell_save_dc: numberOrConditional.optional(),
50
55
  saving_throws: numberOrConditional.optional(),
56
+ // Converter-measured (spec §4): 24 / 3 / 2 / 1 carriers. Same numberOrConditional
57
+ // shape as the six above, so a conditional arm is legal on these too.
58
+ spell_damage: numberOrConditional.optional(),
59
+ ability_check: numberOrConditional.optional(),
60
+ proficiency_bonus: numberOrConditional.optional(),
61
+ saving_throw_concentration: numberOrConditional.optional(),
51
62
  ability_scores: z.object({
52
63
  // partial: each ability is independently optional. z.record(enum, …) in
53
64
  // Zod v4 treats every enum key as required, which doesn't match the
@@ -67,7 +78,16 @@ var bonusesSchema = z.object({
67
78
  int: numberOrConditional.optional(),
68
79
  wis: numberOrConditional.optional(),
69
80
  cha: numberOrConditional.optional()
70
- }).optional()
81
+ }).optional(),
82
+ // "choose `count` of `from`, each +`amount`" (6 measured leaves). `amount` is
83
+ // OPTIONAL: Lost Laboratory of Kwalish/Deck of Several Things carries
84
+ // {from: [all six], count: 1} with NO amount, and a required `amount` would
85
+ // refuse that document outright.
86
+ choose: z.array(z.strictObject({
87
+ from: z.array(abilityEnum),
88
+ count: z.number().int().positive(),
89
+ amount: z.number().int().optional()
90
+ })).optional()
71
91
  }).optional(),
72
92
  speed: z.object({
73
93
  walk: numberOrConditional.optional(),
@@ -78,6 +98,9 @@ var bonusesSchema = z.object({
78
98
  });
79
99
  var chargesSchema = z.object({
80
100
  max: z.number().int(),
101
+ // Dice regained per recharge, e.g. "1d4 - 1" (68 carriers). Distinct from
102
+ // `recharge_amount`, which the canonical pipeline also emits as a string.
103
+ dice: z.string().optional(),
81
104
  recharge: z.string().optional(),
82
105
  recharge_amount: z.string().optional(),
83
106
  destroy_on_empty: z.object({
@@ -96,16 +119,37 @@ var attachedSpellsSchema = z.object({
96
119
  // Source canonical pipeline emits this for Necklace of Fireballs, Helm of
97
120
  // Brilliance, Candle of Invocation, Efreeti Bottle. Key is total available
98
121
  // uses (e.g. "9": ["fireball"] for the necklace's 9 beads).
99
- limited: z.record(z.string(), z.array(z.string())).optional()
122
+ limited: z.record(z.string(), z.array(z.string())).optional(),
123
+ // Converter-measured (spec §4): spells the item lists without a cost model (18),
124
+ // the casting ability for the item's attached spells (10), and ritual-only
125
+ // spells (1).
126
+ other: z.array(z.string()).optional(),
127
+ ability: z.string().optional(),
128
+ ritual: z.array(z.string()).optional()
100
129
  });
101
130
  var attunementTagSchema = z.union([
102
- z.object({ class: z.string(), subclass: z.string().optional() }),
131
+ z.object({
132
+ class: z.string(),
133
+ subclass: z.string().optional(),
134
+ alignment: z.union([z.string(), z.array(z.string())]).optional()
135
+ }),
103
136
  // alignment can be a single string OR an array of alignment-letter codes
104
137
  // (e.g. ["G"] for good, ["L", "G"] for lawful good).
105
- z.object({ alignment: z.union([z.string(), z.array(z.string())]) }),
106
- z.object({ race: z.string() }),
107
- z.object({ creature_type: z.string() }),
108
- z.object({ spellcasting: z.boolean() })
138
+ z.object({
139
+ alignment: z.union([z.string(), z.array(z.string())]),
140
+ race: z.string().optional()
141
+ }),
142
+ z.object({
143
+ race: z.string(),
144
+ alignment: z.union([z.string(), z.array(z.string())]).optional()
145
+ }),
146
+ z.object({ creature_type: z.string(), size: z.string().optional() }),
147
+ z.object({ spellcasting: z.boolean() }),
148
+ z.object({ background: z.string() }),
149
+ z.object({ psionics: z.boolean() }),
150
+ z.object({ int: z.number().int() }),
151
+ z.object({ skill_proficiency: z.array(z.string()) }),
152
+ z.object({ language_proficiency: z.array(z.string()) })
109
153
  ]);
110
154
  var attunementCanonicalSchema = z.object({
111
155
  required: z.boolean(),
@@ -122,15 +166,27 @@ var grantsSchema = z.object({
122
166
  blindsight: z.number().optional()
123
167
  }).optional()
124
168
  });
169
+ var containerCapacitySchema = z.strictObject({
170
+ weight: z.array(z.number()).optional(),
171
+ item: z.array(z.record(z.string(), z.number())).optional(),
172
+ weightless: z.boolean().optional(),
173
+ volume: z.array(z.number()).optional()
174
+ });
125
175
  var containerSchema = z.object({
126
176
  capacity_weight: z.number().optional(),
127
177
  weightless: z.boolean().optional(),
128
- pack_contents: z.array(z.string()).optional()
178
+ pack_contents: z.array(z.string()).optional(),
179
+ capacity: containerCapacitySchema.optional()
129
180
  });
130
181
  var lightSchema = z.object({
131
182
  bright_radius: z.number(),
132
183
  dim_radius: z.number()
133
184
  });
185
+ var lightEntrySchema = z.strictObject({
186
+ bright: z.number().optional(),
187
+ dim: z.number().optional(),
188
+ shape: z.string().optional()
189
+ });
134
190
  var itemEntitySchema = z.object({
135
191
  name: z.string().min(1),
136
192
  slug: z.string().optional(),
@@ -147,10 +203,12 @@ var itemEntitySchema = z.object({
147
203
  attunement: z.union([attunementCanonicalSchema, z.boolean(), z.string()]).optional(),
148
204
  grants: grantsSchema.optional(),
149
205
  container: containerSchema.optional(),
150
- light: lightSchema.optional(),
206
+ light: z.union([lightSchema, z.array(lightEntrySchema).nonempty()]).optional(),
151
207
  cursed: z.boolean().optional(),
152
208
  sentient: z.boolean().optional(),
153
- focus: z.union([z.boolean(), z.string()]).optional(),
209
+ // 54 docs carry a LIST of classes that may use the item as a focus
210
+ // (e.g. ["Druid", "Ranger"]); 26 carry a single string, 2 a boolean.
211
+ focus: z.union([z.boolean(), z.string(), z.array(z.string())]).optional(),
154
212
  tier: z.enum(["major", "minor"]).optional(),
155
213
  damage: z.union([
156
214
  z.object({
@@ -182,6 +240,24 @@ var itemEntitySchema = z.object({
182
240
  scroll_level: z.number().optional(),
183
241
  unidentified: z.boolean().optional(),
184
242
  masked_category: z.string().optional(),
243
+ // Converter-modelled top-level keys (spec §4). Declared here so they survive
244
+ // parse AND listed in the parser's KNOWN_KEYS so they are not additionally
245
+ // copied into `raw` — the schema and the raw-bag are independent gates.
246
+ // `modify_speed` (124 carriers): equal 101 (string-valued, e.g. {fly: "walk"}) ·
247
+ // static 20 · multiply 3 · bonus 1 (keyed "*").
248
+ modify_speed: z.strictObject({
249
+ equal: z.record(z.string(), z.string()).optional(),
250
+ static: z.record(z.string(), z.number()).optional(),
251
+ multiply: z.record(z.string(), z.number()).optional(),
252
+ bonus: z.record(z.string(), z.number()).optional()
253
+ }).optional(),
254
+ // Carried by all 6,046 converter item docs, ALWAYS as the empty string today:
255
+ // `.min(1)` is forbidden here.
256
+ rendering_hint: z.string().optional(),
257
+ has_fluff: z.boolean().optional(),
258
+ has_fluff_images: z.boolean().optional(),
259
+ // Already `.optional()` at its source — never append a second one.
260
+ image: imageField,
185
261
  raw: z.record(z.string(), z.unknown()).optional(),
186
262
  // Legacy fields preserved
187
263
  damage_dice: z.string().optional(),
@@ -0,0 +1,81 @@
1
+ import {
2
+ poolGrantSchema,
3
+ selectionPoolSchema,
4
+ tabDeclSchema
5
+ } from "./chunk-23LZLBJK.js";
6
+ import {
7
+ casterTypeEnum
8
+ } from "./chunk-CJYCVNW2.js";
9
+ import {
10
+ featureSchema
11
+ } from "./chunk-4FWNK4FD.js";
12
+ import {
13
+ resourceSchema
14
+ } from "./chunk-YLL2QXY2.js";
15
+ import {
16
+ additionalSpellsEntrySchema,
17
+ imageField,
18
+ progressionSchema
19
+ } from "./chunk-UA5KS2PL.js";
20
+
21
+ // src/subclass/subclass.schema.ts
22
+ import { z } from "zod";
23
+ var editionEnum = z.enum(["2014", "2024"]);
24
+ var wikilinkRegex = /^\[\[[^[\]]+\]\]$/;
25
+ var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
26
+ var spellcastingSchema = z.object({
27
+ caster_type: casterTypeEnum.optional(),
28
+ ability: abilityEnum,
29
+ preparation: z.enum(["known", "prepared"]).optional(),
30
+ spell_list: z.string().min(1).optional()
31
+ });
32
+ var subclassTableRowSchema = z.object({
33
+ columns: z.record(z.string(), z.union([z.string(), z.number()])).optional()
34
+ });
35
+ var subclassEntitySchema = z.object({
36
+ slug: z.string().min(1),
37
+ name: z.string().min(1),
38
+ parent_class: z.string().regex(wikilinkRegex, "parent_class must be a wikilink like [[rogue]]"),
39
+ edition: editionEnum,
40
+ source: z.string().min(1),
41
+ description: z.string(),
42
+ spellcasting: spellcastingSchema.nullable().optional(),
43
+ table: z.record(z.string(), subclassTableRowSchema).optional(),
44
+ features_by_level: z.record(z.string(), z.array(featureSchema)),
45
+ resources: z.array(resourceSchema),
46
+ selection_pools: z.array(selectionPoolSchema).optional(),
47
+ pool_grants: z.array(poolGrantSchema).optional(),
48
+ tabs: z.array(tabDeclSchema).optional(),
49
+ rendering_hint: z.string().optional(),
50
+ // 346
51
+ short_name: z.string().min(1).optional(),
52
+ // 346
53
+ has_fluff: z.boolean().optional(),
54
+ // 50
55
+ has_fluff_images: z.boolean().optional(),
56
+ // 346
57
+ fluff: z.unknown().optional(),
58
+ // 62 docs, single key-set {_subclassFluff:{...}} — 5etools-internal
59
+ table_col_labels: z.array(z.string().min(1)).optional(),
60
+ // 8
61
+ cantrip_progression: z.array(z.number()).optional(),
62
+ // 6
63
+ prepared_spells_change: z.string().min(1).optional(),
64
+ // 2
65
+ prepared_spells_progression: z.array(z.number()).optional(),
66
+ // 2
67
+ spells_known_progression: z.array(z.number()).optional(),
68
+ // 4
69
+ feat_progression: z.array(progressionSchema).optional(),
70
+ // 1
71
+ optionalfeature_progression: z.array(progressionSchema).optional(),
72
+ // 13 carriers; featureType
73
+ // on all 13, required on 2
74
+ additional_spells: z.array(additionalSpellsEntrySchema).optional(),
75
+ // 212
76
+ image: imageField
77
+ });
78
+
79
+ export {
80
+ subclassEntitySchema
81
+ };
@@ -1,12 +1,16 @@
1
1
  import {
2
2
  subclassEntitySchema
3
- } from "./chunk-W7HWMMBF.js";
3
+ } from "./chunk-H7KBLR4X.js";
4
+ import {
5
+ aliasFeaturesByLevelActionCost
6
+ } from "./chunk-4U55VNC7.js";
4
7
 
5
8
  // src/subclass/subclass.parser.ts
6
9
  import { parseYaml } from "@archivist-gg/core";
7
10
  function parseSubclass(source) {
8
11
  const raw = parseYaml(source, ["name", "slug", "parent_class"]);
9
12
  if (!raw.success) return raw;
13
+ aliasFeaturesByLevelActionCost(raw.data.features_by_level);
10
14
  const result = subclassEntitySchema.safeParse({ edition: "2014", ...raw.data });
11
15
  if (!result.success) {
12
16
  return { success: false, error: `subclass schema validation failed: ${result.error.message}` };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseBackground
3
- } from "./chunk-DZXSWSBP.js";
3
+ } from "./chunk-LMBAEIP6.js";
4
4
 
5
5
  // src/background/background.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  weaponEntitySchema
3
- } from "./chunk-AFKTJXRA.js";
3
+ } from "./chunk-2DFENQIJ.js";
4
4
 
5
5
  // src/weapon/weapon.parser.ts
6
6
  import { parseYaml } from "@archivist-gg/core";
@@ -21,7 +21,14 @@ var KNOWN_KEYS = /* @__PURE__ */ new Set([
21
21
  "page",
22
22
  "edition",
23
23
  "entries",
24
- "raw"
24
+ "raw",
25
+ // Root extras declared on weaponEntitySchema (spec §5). The schema declaring them is NOT
26
+ // enough: this set is the second, independent gate — a key missing here is copied into
27
+ // `raw` as well, so the two lists must move together.
28
+ "rendering_hint",
29
+ "has_fluff",
30
+ "has_fluff_images",
31
+ "image"
25
32
  ]);
26
33
  var FLAG_PROPERTIES = /* @__PURE__ */ new Set([
27
34
  "finesse",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CONDITION_SLUGS
3
- } from "./chunk-TZK36BU7.js";
3
+ } from "./chunk-S7REMWW4.js";
4
4
 
5
5
  // src/pc/pc.schema.ts
6
6
  import { z } from "zod";
@@ -79,7 +79,7 @@ var equipmentEntrySchema = z.object({
79
79
  // finished file never carries it.
80
80
  granted_by: z.string().optional()
81
81
  });
82
- var characterOverridesSchema = z.object({
82
+ var characterOverridesShape = z.object({
83
83
  scores: z.partialRecord(abilityEnum, z.number().int()).optional(),
84
84
  saves: z.partialRecord(abilityEnum, z.object({
85
85
  bonus: z.number().int().optional(),
@@ -102,16 +102,59 @@ var characterOverridesSchema = z.object({
102
102
  ac: z.number().int().optional(),
103
103
  speed: z.number().int().optional(),
104
104
  initiative: z.number().int().optional(),
105
- spellcasting: z.object({
106
- saveDC: z.number().int().optional(),
107
- attackBonus: z.number().int().optional()
108
- }).optional(),
105
+ spellcasting_ability_by_class: z.record(z.string(), abilityEnum).optional(),
109
106
  // Character-level DC-ability fallback for own-ability-less spells (e.g. a Spell
110
107
  // Scroll cast by a non-caster). Same ability enum as the per-instance spell_ability.
111
108
  spellcasting_ability: abilityEnum.optional(),
112
109
  spell_slots: z.record(z.coerce.number().int(), z.number().int().nonnegative()).optional(),
113
- attunement_limit: z.number().int().nonnegative().optional()
114
- }).default({});
110
+ attunement_limit: z.number().int().nonnegative().optional(),
111
+ /** Manual proficiency edits. `remove` SUPPRESSES a rules-granted entry ("a dwarf who doesn't know
112
+ * dwarvish"). NOTHING here may carry a default, at either level, or an untouched note stops being
113
+ * byte-identical:
114
+ * - a `.default({})` on `languages`/`tools` materializes the key on every note that carries any
115
+ * `overrides` block at all;
116
+ * - a `.default([])` on `add`/`remove` materializes the sibling array whenever the parent key IS
117
+ * present, which is every add-only entry the mutators write. (It cannot fire while the parent
118
+ * is absent, but that is the uninteresting half of the case.)
119
+ * That rule covers `tools.proficiency` too: no default at any of its three levels.
120
+ * Every reader uses `?? []` (`?? {}` for the record). */
121
+ languages: z.object({ add: z.array(z.string()).optional(), remove: z.array(z.string()).optional() }).optional(),
122
+ tools: z.object({
123
+ add: z.array(z.string()).optional(),
124
+ remove: z.array(z.string()).optional(),
125
+ /** R4-G4 §9.3 (UR1): a per-tool tri. An OPEN key space (tool names are not an enum, unlike
126
+ * `skillEnum` above); no `bonus` sibling, because tools have no numeric consumer. `expertise`
127
+ * beats a data grant, `none` suppresses one, `proficient` clears a data expertise · applied in
128
+ * `computeEffectiveProficiencies` (pc.decision-engine.ts). A key a WRITER CREATES is
129
+ * `toProfSlug` output with the apostrophe RETAINED ("thieves'-tools"), matching the engine's
130
+ * own tool vocabulary in types/choice.ts. The READER is wider: `computeEffectiveProficiencies`
131
+ * normalises each key with `toProfSlug` on read, so a hand-typed "Thieves' Tools" also
132
+ * matches; and the plugin's two writers (`CharacterEditState.setToolProficiency` and the
133
+ * `addProficiency` clearance) match an existing key by slug too, so a key the note ALREADY
134
+ * carries is written THROUGH in its own spelling rather than duplicated · nothing here
135
+ * re-spells it (review M-4: "repaired in place" read as "normalised", which it is not).
136
+ * `languages` gets no such sibling: languages have no tri (§9.3). */
137
+ proficiency: z.record(z.string(), proficiencyTri).optional()
138
+ }).optional(),
139
+ /** Manual defense edits. SUPPRESSION-ONLY, and the missing `add` channel is a DELIBERATE
140
+ * asymmetry with `languages`/`tools` above · do not "complete the pattern". The additive
141
+ * store is `character.defenses.*`, which already exists and works; a second additive
142
+ * channel would recreate exactly the two-writers divergence R4-P5 exists to remove.
143
+ * The schema persists what the note wrote, VERBATIM · it neither normalizes nor validates
144
+ * the strings. As of this commit the store has NO consumer at all: Task 5 writes the first,
145
+ * and the intent it is to implement is that entries be matched with `toDefenseSlug`
146
+ * (pc.defense-normalize.ts), so a note may author any spelling.
147
+ * The no-defaults rule of the block above applies here at all three levels: a default on
148
+ * `defenses`, on a bucket, or on `remove` rewrites an untouched note. Readers should
149
+ * therefore treat every level as possibly absent (`?? []`). */
150
+ defenses: z.object({
151
+ resistances: z.object({ remove: z.array(z.string()).optional() }).optional(),
152
+ immunities: z.object({ remove: z.array(z.string()).optional() }).optional(),
153
+ vulnerabilities: z.object({ remove: z.array(z.string()).optional() }).optional(),
154
+ condition_immunities: z.object({ remove: z.array(z.string()).optional() }).optional()
155
+ }).optional()
156
+ });
157
+ var characterOverridesSchema = characterOverridesShape.default({});
115
158
  var characterStateSchema = z.object({
116
159
  hp: z.object({
117
160
  current: z.number().int(),
@@ -252,5 +295,6 @@ var characterSchema = z.object({
252
295
  });
253
296
 
254
297
  export {
298
+ characterOverridesShape,
255
299
  characterSchema
256
300
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseOptionalFeature
3
- } from "./chunk-4F6AIZ36.js";
3
+ } from "./chunk-5XMQPTHD.js";
4
4
 
5
5
  // src/optional-feature/optional-feature.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,8 +1,14 @@
1
1
  // src/schemas/equipment-grant-schema.ts
2
2
  import { z } from "zod";
3
3
  var equipmentGrantSchema = z.union([
4
- z.object({ item: z.string().min(1), qty: z.number().int().positive().optional() }).strict(),
5
- z.object({ category: z.string().min(1), qty: z.number().int().positive().optional() }).strict(),
4
+ z.object({
5
+ item: z.string().min(1),
6
+ qty: z.number().int().positive().optional(),
7
+ contains_value: z.number().int().nonnegative().optional(),
8
+ display_name: z.string().min(1).optional(),
9
+ worth_value: z.number().int().nonnegative().optional()
10
+ }).strict(),
11
+ z.object({ category: z.string().min(1), qty: z.number().int().positive().optional(), display_name: z.string().min(1).optional() }).strict(),
6
12
  z.object({ gold: z.number().int().positive() }).strict()
7
13
  ]);
8
14
  var equipmentOptionSchema = z.object({
@@ -0,0 +1,58 @@
1
+ // src/pc/pc.table-column.ts
2
+ function readTableColumn(table, level, keys) {
3
+ var _a;
4
+ const cols = (_a = table == null ? void 0 : table[level]) == null ? void 0 : _a.columns;
5
+ if (!cols) return null;
6
+ for (const k of keys) {
7
+ const raw = cols[k];
8
+ if (raw === void 0 || raw === null) continue;
9
+ const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
10
+ if (!Number.isNaN(n)) return n;
11
+ }
12
+ return null;
13
+ }
14
+ function numericColumnsAt(table, level) {
15
+ var _a;
16
+ const out = {};
17
+ const cols = (_a = table == null ? void 0 : table[level]) == null ? void 0 : _a.columns;
18
+ if (!cols) return out;
19
+ for (const [k, raw] of Object.entries(cols)) {
20
+ if (raw === void 0 || raw === null) continue;
21
+ const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
22
+ if (!Number.isNaN(n)) out[k] = n;
23
+ }
24
+ return out;
25
+ }
26
+ var COUNT_COLUMNS = {
27
+ "weapon-mastery": ["Weapon Mastery"]
28
+ };
29
+ function countColumnsFor(choiceId) {
30
+ if (!Object.prototype.hasOwnProperty.call(COUNT_COLUMNS, choiceId)) return void 0;
31
+ return COUNT_COLUMNS[choiceId];
32
+ }
33
+ function diceColumnAt(table, level, key) {
34
+ var _a;
35
+ const cols = (_a = table == null ? void 0 : table[level]) == null ? void 0 : _a.columns;
36
+ if (!cols || !Object.prototype.hasOwnProperty.call(cols, key)) return null;
37
+ const raw = cols[key];
38
+ if (typeof raw !== "string") return null;
39
+ const cell = raw.trim();
40
+ return /^\d+d\d+$/i.test(cell) ? cell : null;
41
+ }
42
+ var UNARMED_DIE_COLUMNS = {
43
+ "martial-arts": "Martial Arts"
44
+ };
45
+ function unarmedDieColumnFor(featureSlug) {
46
+ if (!Object.prototype.hasOwnProperty.call(UNARMED_DIE_COLUMNS, featureSlug)) return void 0;
47
+ return UNARMED_DIE_COLUMNS[featureSlug];
48
+ }
49
+
50
+ export {
51
+ readTableColumn,
52
+ numericColumnsAt,
53
+ COUNT_COLUMNS,
54
+ countColumnsFor,
55
+ diceColumnAt,
56
+ UNARMED_DIE_COLUMNS,
57
+ unarmedDieColumnFor
58
+ };
@@ -1,3 +1,7 @@
1
+ import {
2
+ imageField
3
+ } from "./chunk-UA5KS2PL.js";
4
+
1
5
  // src/armor/armor.schema.ts
2
6
  import { z } from "zod";
3
7
  var acSchema = z.object({
@@ -23,7 +27,22 @@ var armorEntitySchema = z.object({
23
27
  page: z.number().int().optional(),
24
28
  edition: z.string().optional(),
25
29
  entries: z.array(z.unknown()).optional(),
30
+ // Root extras the converter and the SRD bundle emit on more than one root (spec §5).
31
+ // Carrier counts measured on the G1b close census. `rendering_hint` is ALWAYS the empty
32
+ // string today, so `.min(1)` is forbidden here.
33
+ rendering_hint: z.string().optional(),
34
+ // 27
35
+ has_fluff: z.boolean().optional(),
36
+ // 0 carriers — symmetry with weapon
37
+ has_fluff_images: z.boolean().optional(),
38
+ // 13
39
+ // Already `.optional()` at its source — never append a second one.
40
+ image: imageField,
41
+ // 0 today (images-OFF tree)
26
42
  raw: z.record(z.string(), z.unknown()).optional()
43
+ // `.loose()` is load-bearing for the `strength_required` alias (armor.parser.ts): the alias
44
+ // rides through onto the typed output as an inert extra, which is what keeps its census
45
+ // disposal `kept` instead of minting a `stripped` row. See the parser's comment.
27
46
  }).loose();
28
47
 
29
48
  export {