@archivist-gg/dnd5e 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/LICENSES/SRD.md +25 -6
  2. package/dist/armor/armor.codec.js +4 -3
  3. package/dist/armor/armor.parser.js +3 -2
  4. package/dist/armor/armor.schema.d.ts +4 -0
  5. package/dist/armor/armor.schema.js +2 -1
  6. package/dist/armor/armor.types.d.ts +8 -0
  7. package/dist/background/background.codec.js +7 -6
  8. package/dist/background/background.parser.js +6 -5
  9. package/dist/background/background.schema.d.ts +26 -0
  10. package/dist/background/background.schema.js +5 -4
  11. package/dist/background/background.types.d.ts +22 -0
  12. package/dist/{chunk-4TIJKN6O.js → chunk-23LZLBJK.js} +1 -1
  13. package/dist/{chunk-AFKTJXRA.js → chunk-2DFENQIJ.js} +18 -0
  14. package/dist/chunk-2FRUSVZS.js +489 -0
  15. package/dist/chunk-4FWNK4FD.js +159 -0
  16. package/dist/chunk-4U55VNC7.js +22 -0
  17. package/dist/{chunk-4JHOHWBI.js → chunk-4XTNPNQB.js} +1 -1
  18. package/dist/{chunk-HCP3GMHS.js → chunk-5N2GW72F.js} +15 -2
  19. package/dist/{chunk-42Y5IJKT.js → chunk-5U5FFFA3.js} +5 -1
  20. package/dist/{chunk-4F6AIZ36.js → chunk-5XMQPTHD.js} +1 -1
  21. package/dist/{chunk-GJOI2GHP.js → chunk-6GNXYAYP.js} +1 -1
  22. package/dist/chunk-6PKV5V2H.js +178 -0
  23. package/dist/{chunk-66XXW2Z2.js → chunk-6PXTNCBP.js} +2 -2
  24. package/dist/chunk-6X2NV665.js +33 -0
  25. package/dist/{chunk-LV4SNWZQ.js → chunk-6X4KUDMU.js} +8 -1
  26. package/dist/{chunk-ZMR77DDL.js → chunk-AW3EHF3L.js} +1 -1
  27. package/dist/chunk-CJYCVNW2.js +7 -0
  28. package/dist/chunk-COEOOQX2.js +133 -0
  29. package/dist/{chunk-XTIQWSFM.js → chunk-D5U7GMPX.js} +1 -1
  30. package/dist/{chunk-RAWJTA45.js → chunk-DEEHHLAP.js} +1 -1
  31. package/dist/{chunk-6IRYTM5V.js → chunk-DPUJDOKV.js} +119 -28
  32. package/dist/{chunk-73HOFQQ5.js → chunk-ESS46GR4.js} +1 -1
  33. package/dist/chunk-EVMUJYUD.js +195 -0
  34. package/dist/{chunk-2BWAID3Y.js → chunk-FDBWYMZK.js} +29 -3
  35. package/dist/{chunk-K6Q5OUGK.js → chunk-GFZ7PITJ.js} +86 -10
  36. package/dist/chunk-H7KBLR4X.js +81 -0
  37. package/dist/{chunk-L7E2UT5A.js → chunk-HB7XZXDK.js} +5 -1
  38. package/dist/{chunk-BIK4DFWK.js → chunk-HJ5EEZ47.js} +1 -1
  39. package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
  40. package/dist/{chunk-TN6ZLTNU.js → chunk-I2NDWFDD.js} +51 -4
  41. package/dist/{chunk-RSDRMH54.js → chunk-IMZFOOVL.js} +1 -1
  42. package/dist/{chunk-3DZ74AKK.js → chunk-IN4M4X7P.js} +8 -2
  43. package/dist/chunk-IUAAXXNO.js +58 -0
  44. package/dist/{chunk-XJNECUPT.js → chunk-JC5UNIVM.js} +19 -0
  45. package/dist/chunk-JJ46T5XX.js +297 -0
  46. package/dist/{chunk-SE7AZDR3.js → chunk-JRDPUDT5.js} +29 -3
  47. package/dist/{chunk-24GFQU7T.js → chunk-JSZ4H6AQ.js} +1 -1
  48. package/dist/chunk-KU3YLF6D.js +106 -0
  49. package/dist/{chunk-5INR4NSL.js → chunk-LDDCNPHD.js} +1 -1
  50. package/dist/{chunk-5ZBE4CWV.js → chunk-LMBAEIP6.js} +1 -1
  51. package/dist/chunk-LVQLWRCU.js +15 -0
  52. package/dist/chunk-MI7P3NX3.js +161 -0
  53. package/dist/chunk-O4SRD3J5.js +165 -0
  54. package/dist/{chunk-D3CLOBE3.js → chunk-OSQFPGLM.js} +44 -8
  55. package/dist/chunk-OZP6JFGT.js +162 -0
  56. package/dist/{chunk-NAEPSZAM.js → chunk-PDJSUH24.js} +24 -2
  57. package/dist/chunk-PP2UHLBA.js +50 -0
  58. package/dist/chunk-PVCEA4HZ.js +6 -0
  59. package/dist/{chunk-XLSVNUTK.js → chunk-R3VAKWKW.js} +1 -1
  60. package/dist/chunk-RIXI5D3R.js +89 -0
  61. package/dist/chunk-RLGQ5PZZ.js +16 -0
  62. package/dist/chunk-S7REMWW4.js +21 -0
  63. package/dist/{chunk-7VJ6BOA5.js → chunk-SMLXZUBH.js} +9 -1
  64. package/dist/{chunk-NXSWH3AE.js → chunk-T26ZVCL5.js} +1 -1
  65. package/dist/chunk-TOM66P2P.js +38 -0
  66. package/dist/chunk-UA5KS2PL.js +26 -0
  67. package/dist/{chunk-N6N74K6X.js → chunk-UBBKSYBY.js} +1 -1
  68. package/dist/{chunk-353WXS6S.js → chunk-UIRMA5KM.js} +1 -1
  69. package/dist/{chunk-GE4TLPWM.js → chunk-US5IKYCW.js} +1 -1
  70. package/dist/chunk-UXPGW4TI.js +101 -0
  71. package/dist/chunk-VQAXTIBW.js +100 -0
  72. package/dist/{chunk-WP2CX2JO.js → chunk-WELIKLZF.js} +22 -1
  73. package/dist/chunk-WPWOHGPI.js +172 -0
  74. package/dist/{chunk-GZ46OVGZ.js → chunk-WSVMLNTO.js} +1 -1
  75. package/dist/{chunk-4LZJXJJS.js → chunk-WXWPK3LI.js} +6 -2
  76. package/dist/{chunk-ZZGEVJKX.js → chunk-XBQMCEI2.js} +1 -1
  77. package/dist/{chunk-PHQRGDRQ.js → chunk-XGOFP7AQ.js} +5 -1
  78. package/dist/chunk-XJ2F2WO6.js +8 -0
  79. package/dist/chunk-XQCSGNXE.js +1580 -0
  80. package/dist/chunk-YBLDE5CG.js +42 -0
  81. package/dist/{chunk-JEYQU3JT.js → chunk-YHYTW5QA.js} +2 -18
  82. package/dist/{chunk-TLYDBT5F.js → chunk-YJJTISBN.js} +64 -7
  83. package/dist/{chunk-PZOWBI3B.js → chunk-YLL2QXY2.js} +7 -2
  84. package/dist/{chunk-MDEJHMKK.js → chunk-ZONJ66NE.js} +26 -5
  85. package/dist/class/class.codec.js +12 -9
  86. package/dist/class/class.parser.js +11 -8
  87. package/dist/class/class.schema.d.ts +55 -3
  88. package/dist/class/class.schema.js +9 -7
  89. package/dist/class/class.types.d.ts +48 -3
  90. package/dist/condition/condition.codec.d.ts +9 -0
  91. package/dist/condition/condition.entity-type.d.ts +2 -0
  92. package/dist/condition/condition.parser.d.ts +13 -0
  93. package/dist/condition/condition.parser.js +7 -0
  94. package/dist/condition/condition.schema.d.ts +29 -0
  95. package/dist/condition/condition.types.d.ts +15 -0
  96. package/dist/condition/condition.types.js +0 -0
  97. package/dist/dnd/constants.d.ts +22 -0
  98. package/dist/dnd/constants.js +9 -3
  99. package/dist/dnd/formula-tags.js +2 -2
  100. package/dist/dnd/math.d.ts +26 -0
  101. package/dist/dnd/math.js +12 -2
  102. package/dist/dnd/prose-tags.js +1 -1
  103. package/dist/dnd/resource-formula.d.ts +36 -10
  104. package/dist/dnd/resource-formula.js +10 -155
  105. package/dist/dnd/srd-tag-converter.js +3 -3
  106. package/dist/dnd/tag-grammar.js +1 -1
  107. package/dist/dnd/warn-once.d.ts +2 -0
  108. package/dist/dnd/warn-once.js +8 -0
  109. package/dist/entities/base-item-resolver.js +2 -2
  110. package/dist/entities/slug.d.ts +13 -0
  111. package/dist/entities/slug.js +5 -3
  112. package/dist/entities/wikilink-display.d.ts +3 -0
  113. package/dist/entities/wikilink-display.js +10 -0
  114. package/dist/feat/feat.category-codes.d.ts +32 -0
  115. package/dist/feat/feat.category-codes.js +14 -0
  116. package/dist/feat/feat.codec.js +7 -5
  117. package/dist/feat/feat.parser.js +6 -4
  118. package/dist/feat/feat.schema.d.ts +230 -0
  119. package/dist/feat/feat.schema.js +5 -3
  120. package/dist/feat/feat.types.d.ts +37 -1
  121. package/dist/index.js +2033 -6907
  122. package/dist/item/item.codec.js +4 -3
  123. package/dist/item/item.enrichment.js +4 -4
  124. package/dist/item/item.parser.js +3 -2
  125. package/dist/item/item.schema.d.ts +69 -3
  126. package/dist/item/item.schema.js +2 -1
  127. package/dist/item/item.types.d.ts +69 -3
  128. package/dist/monster/monster.codec.d.ts +7 -6
  129. package/dist/monster/monster.enrichment.js +4 -4
  130. package/dist/monster/monster.format.d.ts +135 -0
  131. package/dist/monster/monster.format.js +78 -0
  132. package/dist/monster/monster.parser.d.ts +6 -1
  133. package/dist/monster/monster.parser.js +10 -1
  134. package/dist/monster/monster.schema.d.ts +369 -0
  135. package/dist/monster/monster.schema.js +34 -0
  136. package/dist/monster/monster.types.d.ts +200 -14
  137. package/dist/optional-feature/optional-feature.codec.js +6 -5
  138. package/dist/optional-feature/optional-feature.parser.js +5 -4
  139. package/dist/optional-feature/optional-feature.schema.d.ts +176 -0
  140. package/dist/optional-feature/optional-feature.schema.js +4 -3
  141. package/dist/pack.d.ts +1 -1
  142. package/dist/pc/conditions.constants.d.ts +0 -1
  143. package/dist/pc/conditions.constants.js +1 -3
  144. package/dist/pc/decision-recognizer.js +1 -1
  145. package/dist/pc/pc.additional-spells.d.ts +47 -0
  146. package/dist/pc/pc.additional-spells.js +23 -0
  147. package/dist/pc/pc.asi-flatten.d.ts +18 -0
  148. package/dist/pc/pc.decision-engine.d.ts +105 -17
  149. package/dist/pc/pc.decision-engine.js +28 -12
  150. package/dist/pc/pc.defense-normalize.d.ts +11 -0
  151. package/dist/pc/pc.defense-normalize.js +6 -0
  152. package/dist/pc/pc.equipment.d.ts +30 -1
  153. package/dist/pc/pc.equipment.js +12 -8
  154. package/dist/pc/pc.feature-effects.d.ts +225 -11
  155. package/dist/pc/pc.feature-effects.js +21 -5
  156. package/dist/pc/pc.parser.js +2 -2
  157. package/dist/pc/pc.pools.d.ts +11 -3
  158. package/dist/pc/pc.pools.js +9 -13
  159. package/dist/pc/pc.proficiencies.d.ts +54 -29
  160. package/dist/pc/pc.proficiencies.js +74 -95
  161. package/dist/pc/pc.proficiency-grants.d.ts +58 -0
  162. package/dist/pc/pc.proficiency-normalize.d.ts +24 -0
  163. package/dist/pc/pc.proficiency-normalize.js +12 -0
  164. package/dist/pc/pc.proficiency-query.d.ts +13 -0
  165. package/dist/pc/pc.proficiency-query.js +1 -1
  166. package/dist/pc/pc.recalc.d.ts +69 -14
  167. package/dist/pc/pc.recalc.js +261 -122
  168. package/dist/pc/pc.resolver.d.ts +34 -8
  169. package/dist/pc/pc.resolver.js +27 -12
  170. package/dist/pc/pc.resource-seed.d.ts +3 -2
  171. package/dist/pc/pc.resource-seed.js +19 -17
  172. package/dist/pc/pc.resources.d.ts +87 -0
  173. package/dist/pc/pc.resources.js +21 -0
  174. package/dist/pc/pc.rest.d.ts +10 -1
  175. package/dist/pc/pc.rest.js +66 -12
  176. package/dist/pc/pc.schema.d.ts +139 -3
  177. package/dist/pc/pc.schema.js +4 -2
  178. package/dist/pc/pc.slotting.js +3 -3
  179. package/dist/pc/pc.spellcasting.d.ts +75 -6
  180. package/dist/pc/pc.spellcasting.js +16 -6
  181. package/dist/pc/pc.table-column.d.ts +23 -0
  182. package/dist/pc/pc.table-column.js +13 -3
  183. package/dist/pc/pc.types.d.ts +251 -12
  184. package/dist/pc/pool-layout.d.ts +47 -0
  185. package/dist/pc/pool-layout.js +16 -0
  186. package/dist/pc/roll-scope.d.ts +2 -0
  187. package/dist/pc/roll-scope.js +8 -0
  188. package/dist/pc/roll-tag-labels.d.ts +40 -0
  189. package/dist/pc/roll-tag-labels.js +17 -0
  190. package/dist/pc/spell-source.d.ts +21 -0
  191. package/dist/pc/spell-source.js +13 -0
  192. package/dist/race/race.codec.js +9 -7
  193. package/dist/race/race.parser.js +8 -6
  194. package/dist/race/race.schema.d.ts +51 -1
  195. package/dist/race/race.schema.js +8 -5
  196. package/dist/race/race.structural.d.ts +7 -0
  197. package/dist/race/race.structural.js +6 -0
  198. package/dist/race/race.types.d.ts +20 -0
  199. package/dist/schemas/attack-schema.d.ts +5 -0
  200. package/dist/schemas/caster-type-schema.d.ts +14 -0
  201. package/dist/schemas/caster-type-schema.js +6 -0
  202. package/dist/schemas/choice-schema.js +2 -2
  203. package/dist/schemas/entity-extras-schema.d.ts +53 -0
  204. package/dist/schemas/entity-extras-schema.js +10 -0
  205. package/dist/schemas/equipment-grant-schema.d.ts +2 -0
  206. package/dist/schemas/equipment-grant-schema.js +1 -1
  207. package/dist/schemas/feature-alias.d.ts +34 -0
  208. package/dist/schemas/feature-effect-schema.d.ts +124 -0
  209. package/dist/schemas/feature-effect-schema.js +1 -1
  210. package/dist/schemas/feature-schema.d.ts +8 -0
  211. package/dist/schemas/feature-schema.js +6 -4
  212. package/dist/schemas/resource-schema.d.ts +43 -0
  213. package/dist/schemas/resource-schema.js +1 -1
  214. package/dist/schemas/selection-pool-schema.d.ts +2 -0
  215. package/dist/schemas/selection-pool-schema.js +1 -1
  216. package/dist/spell/spell.access.js +23 -3
  217. package/dist/spell/spell.codec.js +4 -3
  218. package/dist/spell/spell.enrichment.js +4 -4
  219. package/dist/spell/spell.parser.d.ts +30 -0
  220. package/dist/spell/spell.parser.js +9 -2
  221. package/dist/spell/spell.scaling.d.ts +27 -6
  222. package/dist/spell/spell.scaling.js +25 -6
  223. package/dist/spell/spell.schema.d.ts +44 -2
  224. package/dist/spell/spell.schema.js +2 -1
  225. package/dist/spell/spell.types.d.ts +14 -0
  226. package/dist/subclass/subclass.codec.d.ts +1 -1
  227. package/dist/subclass/subclass.codec.js +11 -8
  228. package/dist/subclass/subclass.parser.js +10 -7
  229. package/dist/subclass/subclass.schema.d.ts +54 -7
  230. package/dist/subclass/subclass.schema.js +8 -6
  231. package/dist/subclass/subclass.types.d.ts +20 -2
  232. package/dist/types/choice.d.ts +53 -2
  233. package/dist/types/choice.js +13 -1
  234. package/dist/types/equipment-grant.d.ts +8 -1
  235. package/dist/types/feature-effect.d.ts +78 -23
  236. package/dist/types/feature.d.ts +70 -1
  237. package/dist/types/optional-feature.types.d.ts +28 -0
  238. package/dist/types/resource.d.ts +21 -1
  239. package/dist/types/selection-pool.d.ts +17 -2
  240. package/dist/weapon/weapon.classify.js +2 -2
  241. package/dist/weapon/weapon.codec.js +4 -3
  242. package/dist/weapon/weapon.parser.js +3 -2
  243. package/dist/weapon/weapon.schema.d.ts +4 -0
  244. package/dist/weapon/weapon.schema.js +2 -1
  245. package/dist/weapon/weapon.types.d.ts +8 -0
  246. package/package.json +2 -2
  247. package/dist/chunk-5XUXXAQJ.js +0 -30
  248. package/dist/chunk-7P65Q426.js +0 -158
  249. package/dist/chunk-7TEK5KK7.js +0 -49
  250. package/dist/chunk-AIRFC5NQ.js +0 -143
  251. package/dist/chunk-CZPMTQOY.js +0 -68
  252. package/dist/chunk-N2JE7BWL.js +0 -81
  253. package/dist/chunk-NXR3BD2V.js +0 -90
  254. package/dist/chunk-O5EYMKFH.js +0 -6
  255. package/dist/chunk-Q5K6LWBZ.js +0 -45
  256. package/dist/chunk-TNUFPIXI.js +0 -1110
  257. package/dist/chunk-TZK36BU7.js +0 -38
  258. package/dist/chunk-W7HWMMBF.js +0 -46
  259. package/dist/chunk-Y4I74YEG.js +0 -33
@@ -0,0 +1,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-5ZBE4CWV.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(),
@@ -107,8 +107,54 @@ var characterOverridesSchema = z.object({
107
107
  // Scroll cast by a non-caster). Same ability enum as the per-instance spell_ability.
108
108
  spellcasting_ability: abilityEnum.optional(),
109
109
  spell_slots: z.record(z.coerce.number().int(), z.number().int().nonnegative()).optional(),
110
- attunement_limit: z.number().int().nonnegative().optional()
111
- }).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({});
112
158
  var characterStateSchema = z.object({
113
159
  hp: z.object({
114
160
  current: z.number().int(),
@@ -249,5 +295,6 @@ var characterSchema = z.object({
249
295
  });
250
296
 
251
297
  export {
298
+ characterOverridesShape,
252
299
  characterSchema
253
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 {
@@ -0,0 +1,297 @@
1
+ import {
2
+ normalizeRollScope
3
+ } from "./chunk-TOM66P2P.js";
4
+ import {
5
+ toProfSlug
6
+ } from "./chunk-RLGQ5PZZ.js";
7
+ import {
8
+ warnOnce
9
+ } from "./chunk-LVQLWRCU.js";
10
+ import {
11
+ bareEntitySlug
12
+ } from "./chunk-6X4KUDMU.js";
13
+ import {
14
+ normalizeAbility
15
+ } from "./chunk-WELIKLZF.js";
16
+
17
+ // src/pc/pc.feature-effects.ts
18
+ function emptyFeatureEffectTotals() {
19
+ return {
20
+ initiative_bonus: 0,
21
+ hp_per_level_bonus: 0,
22
+ hp_per_level_terms: [],
23
+ speed_walk_bonus: 0,
24
+ speed_walk_set: 0,
25
+ ability_bonus: {},
26
+ senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
27
+ ac_terms: [],
28
+ resistances: [],
29
+ immunities: [],
30
+ vulnerabilities: [],
31
+ condition_immunities: [],
32
+ proficiencies: { skills: [], skillExpertise: [], tools: [], languages: [], saves: [], armor: [], weapons: [] },
33
+ weaponAbilities: [],
34
+ rollModifiers: [],
35
+ saveOutcomes: [],
36
+ critRange: 20,
37
+ extraAttack: 0,
38
+ attackNotes: [],
39
+ damageBonuses: []
40
+ };
41
+ }
42
+ function pushUnique(list, value) {
43
+ const key = value.trim().toLowerCase();
44
+ if (!key) return;
45
+ if (!list.some((v) => v.trim().toLowerCase() === key)) list.push(value.trim());
46
+ }
47
+ function pushDefenseGrant(list, value, source, condition) {
48
+ const key = value.trim().toLowerCase();
49
+ if (!key) return;
50
+ const existing = list.find((g) => g.value.trim().toLowerCase() === key);
51
+ if (existing) {
52
+ if (!existing.sources.includes(source)) existing.sources.push(source);
53
+ if (!existing.condition && condition) existing.condition = condition;
54
+ return;
55
+ }
56
+ list.push({ value: value.trim(), sources: [source], ...condition ? { condition } : {} });
57
+ }
58
+ function foldsNow(rf, activeBuffs) {
59
+ if (rf.feature.activatable !== true) return true;
60
+ const id = rf.feature.id;
61
+ return !!id && activeBuffs.has(id);
62
+ }
63
+ function assembleEffectFeatures(resolved) {
64
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
65
+ const activeBuffs = new Set((_b = (_a = resolved.state) == null ? void 0 : _a.active_buffs) != null ? _b : []);
66
+ const featureIds = new Set(((_c = resolved.features) != null ? _c : []).map((f) => f.feature.id).filter((id) => !!id));
67
+ const aliases = [];
68
+ for (const pool of (_d = resolved.pools) != null ? _d : []) {
69
+ for (const entry of [...(_e = pool.selected) != null ? _e : [], ...(_f = pool.grants) != null ? _f : []]) {
70
+ if (activeBuffs.has(entry.slug)) continue;
71
+ const bare = bareEntitySlug(entry.slug);
72
+ for (const stored of activeBuffs) {
73
+ if (featureIds.has(stored)) continue;
74
+ if (bareEntitySlug(stored) === bare) {
75
+ aliases.push(entry.slug);
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ for (const a of aliases) activeBuffs.add(a);
82
+ const buffFeatures = [];
83
+ const pushBoon = (item, pool) => {
84
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
85
+ const e = item.entity;
86
+ if (!e || ((_b2 = (_a2 = e.effects) == null ? void 0 : _a2.length) != null ? _b2 : 0) === 0) return;
87
+ buffFeatures.push({
88
+ feature: { id: item.slug, name: e.name, activatable: (_c2 = e.activatable) != null ? _c2 : false, effects: e.effects },
89
+ // source is inert for the fold; attribute to the pool's owning class
90
+ // (never a hardcoded class) so the generic engine carries no homebrew name.
91
+ source: { kind: "class", slug: (_g2 = (_f2 = (_e2 = (_d2 = resolved.classes) == null ? void 0 : _d2[pool.classIndex]) == null ? void 0 : _e2.entity) == null ? void 0 : _f2.slug) != null ? _g2 : pool.id, level: pool.anchorLevel }
92
+ });
93
+ };
94
+ for (const pool of (_g = resolved.pools) != null ? _g : []) {
95
+ for (const sel of (_h = pool.selected) != null ? _h : []) pushBoon(sel, pool);
96
+ for (const g of (_i = pool.grants) != null ? _i : []) pushBoon(g, pool);
97
+ }
98
+ return { features: [...(_j = resolved.features) != null ? _j : [], ...buffFeatures], activeBuffs };
99
+ }
100
+ function foldsOnSelf(eff) {
101
+ if (eff.subject === void 0 || eff.subject === "self") return true;
102
+ warnOnce(eff.subject, `archivist: unrecognised effect subject "${eff.subject}" (only "self" folds)`);
103
+ return false;
104
+ }
105
+ function selfEffectsOf(f) {
106
+ var _a;
107
+ return ((_a = f.effects) != null ? _a : []).filter(foldsOnSelf);
108
+ }
109
+ function computeFeatureEffects(features, opts) {
110
+ var _a, _b, _c;
111
+ const out = emptyFeatureEffectTotals();
112
+ for (const rf of features) {
113
+ if (!foldsNow(rf, (_a = opts == null ? void 0 : opts.activeBuffs) != null ? _a : /* @__PURE__ */ new Set())) continue;
114
+ const level = (_b = opts == null ? void 0 : opts.levelFor) == null ? void 0 : _b.call(opts, rf.source);
115
+ for (const eff of selfEffectsOf(rf.feature)) {
116
+ applyEffect(out, eff, (_c = rf.feature.name) != null ? _c : "Feature", level);
117
+ }
118
+ }
119
+ return out;
120
+ }
121
+ function classifyProficiencyEffect(eff) {
122
+ if (eff.kind !== "proficiency") return null;
123
+ const raw = eff.value;
124
+ const expertise = eff.expertise === true;
125
+ switch (eff.proficiency_type) {
126
+ case "skill":
127
+ return { bucket: "skills", value: toProfSlug(raw), raw, expertise };
128
+ case "tool":
129
+ return { bucket: "tools", value: toProfSlug(raw), raw, expertise };
130
+ case "language":
131
+ return { bucket: "languages", value: toProfSlug(raw), raw, expertise };
132
+ case "armor":
133
+ return { bucket: "armor", value: raw.toLowerCase(), raw, expertise };
134
+ case "weapon":
135
+ return { bucket: "weapons", value: raw.toLowerCase(), raw, expertise };
136
+ default: {
137
+ const ab = normalizeAbility(raw);
138
+ return ab ? { bucket: "saves", value: ab, raw, expertise } : null;
139
+ }
140
+ }
141
+ }
142
+ function collectProficiencyEffectGrants(features, activeBuffs) {
143
+ const out = {
144
+ skills: [],
145
+ tools: [],
146
+ languages: [],
147
+ saves: [],
148
+ armor: [],
149
+ weapons: []
150
+ };
151
+ for (const rf of features) {
152
+ if (!foldsNow(rf, activeBuffs)) continue;
153
+ for (const eff of selfEffectsOf(rf.feature)) {
154
+ const c = classifyProficiencyEffect(eff);
155
+ if (!c) continue;
156
+ out[c.bucket].push({
157
+ value: c.value,
158
+ raw: c.raw,
159
+ sourceKind: rf.source.kind,
160
+ sourceSlug: rf.source.slug,
161
+ ...c.expertise ? { expertise: true } : {}
162
+ });
163
+ }
164
+ }
165
+ return out;
166
+ }
167
+ function scaled(base, key, scalesAt, level) {
168
+ if (level === void 0 || !(scalesAt == null ? void 0 : scalesAt.length)) return base;
169
+ let best = base;
170
+ let bestLevel = -1;
171
+ for (const step of scalesAt) {
172
+ if (step.level > level || step.level <= bestLevel) continue;
173
+ const v = step[key];
174
+ if (typeof v !== "number") continue;
175
+ best = v;
176
+ bestLevel = step.level;
177
+ }
178
+ return best;
179
+ }
180
+ function applyEffect(out, eff, label, level) {
181
+ var _a, _b, _c, _d, _e;
182
+ switch (eff.kind) {
183
+ case "initiative-bonus":
184
+ out.initiative_bonus += eff.value;
185
+ break;
186
+ case "hp-per-level-bonus":
187
+ out.hp_per_level_bonus += eff.value;
188
+ out.hp_per_level_terms.push({ label, value: eff.value });
189
+ break;
190
+ case "speed-bonus": {
191
+ const value = scaled(eff.value, "value", eff.scales_at, level);
192
+ if (eff.mode === "walk") {
193
+ if (eff.set) out.speed_walk_set = Math.max(out.speed_walk_set, value);
194
+ else out.speed_walk_bonus += value;
195
+ }
196
+ break;
197
+ }
198
+ case "sense":
199
+ out.senses[eff.type] = Math.max(out.senses[eff.type], eff.range);
200
+ break;
201
+ case "resistance":
202
+ pushDefenseGrant(out.resistances, eff.damage_type, label, eff.condition);
203
+ break;
204
+ case "immunity":
205
+ pushDefenseGrant(out.immunities, eff.damage_type, label, eff.condition);
206
+ break;
207
+ case "vulnerability":
208
+ pushDefenseGrant(out.vulnerabilities, eff.damage_type, label, eff.condition);
209
+ break;
210
+ case "immune-condition":
211
+ if (!eff.while) pushDefenseGrant(out.condition_immunities, eff.condition, label);
212
+ break;
213
+ case "proficiency": {
214
+ const c = classifyProficiencyEffect(eff);
215
+ if (!c) break;
216
+ if (c.bucket === "saves") {
217
+ pushUnique(out.proficiencies.saves, c.value);
218
+ } else {
219
+ pushUnique(out.proficiencies[c.bucket], c.value);
220
+ }
221
+ if (c.expertise && c.bucket === "skills") pushUnique(out.proficiencies.skillExpertise, c.value);
222
+ break;
223
+ }
224
+ case "ac-bonus":
225
+ out.ac_terms.push({
226
+ value: eff.value,
227
+ requires_armor: eff.requires_armor === true,
228
+ label,
229
+ condition: eff.condition
230
+ });
231
+ break;
232
+ case "weapon-ability": {
233
+ if (eff.ability !== "spellcasting") {
234
+ const w = eff.weapons;
235
+ const weaponSlugs = !w || w === "chosen" ? void 0 : Array.isArray(w) ? w.map(bareEntitySlug) : [bareEntitySlug(w)];
236
+ out.weaponAbilities.push({ ability: eff.ability, weaponSlugs });
237
+ }
238
+ break;
239
+ }
240
+ case "roll-modifier": {
241
+ const rolls = eff.roll === "any" ? ["ability-check", "saving-throw", "attack"] : [eff.roll];
242
+ const scopes = (_a = normalizeRollScope(eff.scope, rolls[0])) != null ? _a : [eff.scope];
243
+ for (const roll of rolls) for (const scope of scopes)
244
+ out.rollModifiers.push({ mode: eff.mode, roll, scope, condition: eff.condition, label });
245
+ break;
246
+ }
247
+ case "save-outcome":
248
+ out.saveOutcomes.push({
249
+ ability: eff.ability === "any" ? void 0 : eff.ability,
250
+ on_success: eff.on_success,
251
+ on_failure: eff.on_failure,
252
+ appliesTo: (_b = eff.applies_to) != null ? _b : void 0,
253
+ condition: eff.condition,
254
+ label
255
+ });
256
+ break;
257
+ case "crit-range":
258
+ if (((_c = eff.applies_to) != null ? _c : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
259
+ break;
260
+ case "extra-attack":
261
+ out.extraAttack = Math.max(out.extraAttack, scaled(eff.count, "count", eff.scales_at, level));
262
+ break;
263
+ case "reroll-damage":
264
+ out.attackNotes.push(`Reroll ${eff.max_reroll}s${eff.once_per_die ? " (once/die)" : ""}`);
265
+ break;
266
+ case "attack-rule":
267
+ if (eff.flag === "no-ranged-in-melee-disadvantage") out.attackNotes.push("No disadvantage firing in melee");
268
+ break;
269
+ case "damage-bonus":
270
+ if (((_d = eff.applies_to) != null ? _d : "weapon") !== "spell") {
271
+ out.damageBonuses.push({
272
+ amount: eff.amount,
273
+ damage_type: eff.damage_type,
274
+ source: label,
275
+ ...eff.condition ? { condition: eff.condition } : {}
276
+ });
277
+ }
278
+ break;
279
+ case "ability-score-increase":
280
+ if (eff.abilities === "chosen") break;
281
+ for (const ab of eff.abilities) out.ability_bonus[ab] = ((_e = out.ability_bonus[ab]) != null ? _e : 0) + eff.amount;
282
+ break;
283
+ default:
284
+ break;
285
+ }
286
+ }
287
+
288
+ export {
289
+ emptyFeatureEffectTotals,
290
+ foldsNow,
291
+ assembleEffectFeatures,
292
+ foldsOnSelf,
293
+ selfEffectsOf,
294
+ computeFeatureEffects,
295
+ classifyProficiencyEffect,
296
+ collectProficiencyEffectGrants
297
+ };
@@ -1,9 +1,13 @@
1
1
  import {
2
2
  startingEquipmentEntrySchema
3
- } from "./chunk-3DZ74AKK.js";
3
+ } from "./chunk-IN4M4X7P.js";
4
4
  import {
5
5
  choiceSchema
6
- } from "./chunk-NXSWH3AE.js";
6
+ } from "./chunk-T26ZVCL5.js";
7
+ import {
8
+ additionalSpellsEntrySchema,
9
+ imageField
10
+ } from "./chunk-UA5KS2PL.js";
7
11
 
8
12
  // src/background/background.schema.ts
9
13
  import { z } from "zod";
@@ -75,7 +79,29 @@ var backgroundEntitySchema = z.object({
75
79
  }).nullable(),
76
80
  origin_feat: z.string().regex(wikilinkRegex).nullable(),
77
81
  suggested_characteristics: suggestedCharSchema.nullable(),
78
- choices: z.array(choiceSchema).optional()
82
+ choices: z.array(choiceSchema).optional(),
83
+ rendering_hint: z.string().optional(),
84
+ // 175
85
+ has_fluff_images: z.boolean().optional(),
86
+ // 87
87
+ tables: z.array(z.object({
88
+ // 88 tables / 67 docs; key-sets EXACT (finding 6)
89
+ name: z.string().min(1),
90
+ dice: z.string().min(1),
91
+ rows: z.array(z.object({ roll: z.string(), text: z.string() }))
92
+ })).optional(),
93
+ additional_spells: z.array(additionalSpellsEntrySchema).optional(),
94
+ // 15
95
+ prerequisites: z.array(z.object({
96
+ // 4 docs; kind CLOSED AT THE EMITTER
97
+ kind: z.literal("campaign"),
98
+ slug: z.string().min(1)
99
+ // (background-mapper.ts pushes
100
+ })).optional(),
101
+ // {kind:"campaign", slug} literally — finding 24)
102
+ // The one-arm union STAYS (§2.6 adjudication): it is closed by CONSTRUCTION at the converter's
103
+ // only push site, and a future kind turns into a visible census REFUSAL — the correct loud failure.
104
+ image: imageField
79
105
  });
80
106
 
81
107
  export {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseSpell
3
- } from "./chunk-N2JE7BWL.js";
3
+ } from "./chunk-OZP6JFGT.js";
4
4
 
5
5
  // src/spell/spell.codec.ts
6
6
  import yaml from "js-yaml";