@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,4 @@
1
+ import type { Edition } from "../types/edition.js";
1
2
  export declare const CR_PROFICIENCY: Record<string, number>;
2
3
  export declare const CR_XP: Record<string, number>;
3
4
  export declare const ALL_CR_VALUES: string[];
@@ -11,6 +12,27 @@ export declare const DAMAGE_TYPES: string[];
11
12
  export declare const DAMAGE_NONMAGICAL_VARIANTS: string[];
12
13
  export declare const CONDITIONS: string[];
13
14
  export declare const STANDARD_SENSES: string[];
15
+ /** R4-G7 T8 RIDER-22 (F-STDACT) · the standard combat actions any character can take, per rules edition, in the book's
16
+ * alphabetical order. The plugin's Actions tab reads the character's edition, so this is rendering data, never a
17
+ * renderer constant (it printed ONE union of both editions). 2014 is the PHB chapter 9 list; 2024 is the 2024 action
18
+ * list, which drops Cast a Spell (now Magic) and Use an Object (now Utilize) and adds Influence and Study. No rules
19
+ * glossary ships in the SRD 2024 bundle; its prose names ten of the twelve as "the <X> action" and none of the 2014-only
20
+ * names (measured 2026-09-15 over the plugin repo's `.compendium-bundle/SRD 2024`). */
21
+ export declare const STANDARD_ACTIONS: Record<Edition, readonly string[]>;
14
22
  export declare const ALIGNMENT_ETHICAL: string[];
15
23
  export declare const ALIGNMENT_MORAL: string[];
16
24
  export declare const ALL_SECTIONS: string[];
25
+ /**
26
+ * Full ability NAME (lower-case) → canonical three-letter key. Distinct from `ABILITY_NAMES`
27
+ * above, which is the OTHER direction and yields the display casing ("str" → "STR").
28
+ *
29
+ * Moved here from `pc/pc.feature-effects.ts` in R4-G3a §6.2.3, where it was private: the scope
30
+ * normaliser (`pc/roll-scope.ts`) needs exactly the same vocabulary, and a second copy is the
31
+ * defect class this phase exists to remove. `src/dnd/*` imports nothing from `src/pc/*`, so the
32
+ * move introduces no cycle; the type is spelled `(typeof ABILITY_KEYS)[number]` rather than the
33
+ * `Ability` alias so this module keeps its zero imports.
34
+ */
35
+ export declare const ABILITY_NAME_TO_KEY: Record<string, (typeof ABILITY_KEYS)[number]>;
36
+ /** Accepts a canonical key ("dex") or a full ability name ("Dexterity"), any casing / padding;
37
+ * returns the canonical key, or null when the value is neither. */
38
+ export declare function normalizeAbility(value: string): (typeof ABILITY_KEYS)[number] | null;
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  ABILITY_KEYS,
3
3
  ABILITY_NAMES,
4
+ ABILITY_NAME_TO_KEY,
4
5
  ALIGNMENT_ETHICAL,
5
6
  ALIGNMENT_MORAL,
6
7
  ALL_CR_VALUES,
@@ -14,11 +15,14 @@ import {
14
15
  DAMAGE_TYPES,
15
16
  SIZE_HIT_DICE,
16
17
  SKILL_ABILITY,
17
- STANDARD_SENSES
18
- } from "../chunk-WP2CX2JO.js";
18
+ STANDARD_ACTIONS,
19
+ STANDARD_SENSES,
20
+ normalizeAbility
21
+ } from "../chunk-WELIKLZF.js";
19
22
  export {
20
23
  ABILITY_KEYS,
21
24
  ABILITY_NAMES,
25
+ ABILITY_NAME_TO_KEY,
22
26
  ALIGNMENT_ETHICAL,
23
27
  ALIGNMENT_MORAL,
24
28
  ALL_CR_VALUES,
@@ -32,5 +36,7 @@ export {
32
36
  DAMAGE_TYPES,
33
37
  SIZE_HIT_DICE,
34
38
  SKILL_ABILITY,
35
- STANDARD_SENSES
39
+ STANDARD_ACTIONS,
40
+ STANDARD_SENSES,
41
+ normalizeAbility
36
42
  };
@@ -5,10 +5,10 @@ import {
5
5
  import {
6
6
  abilityModifier,
7
7
  formatModifier
8
- } from "../chunk-NAEPSZAM.js";
8
+ } from "../chunk-PDJSUH24.js";
9
9
  import {
10
10
  ABILITY_KEYS
11
- } from "../chunk-WP2CX2JO.js";
11
+ } from "../chunk-WELIKLZF.js";
12
12
 
13
13
  // src/dnd/formula-tags.ts
14
14
  function resolveSlugForTag(slug, tagType, registry) {
@@ -22,3 +22,29 @@ export declare function passivePerception(wisScore: number, perceptionProf: "non
22
22
  export declare function attackBonus(abilityScore: number, profBonus: number): number;
23
23
  export declare function saveDC(abilityScore: number, profBonus: number): number;
24
24
  export declare function abilityNameToKey(name: string): (typeof ABILITY_KEYS)[number] | null;
25
+ /** Is `type` one of the canonical damage types, whatever its casing? A rider whose declared type is
26
+ * anything else, a sentinel (`weapon`) or prose ("same as the weapon's type"), INHERITS the weapon row's
27
+ * own damage type; `pc.recalc.ts` resolves that where the rider is merged onto the row, never in a
28
+ * renderer, and never from a list of prose spellings. The ONE exception is the schema's own sentinel
29
+ * {@link CHOSEN_DAMAGE_TYPE}, which means the player's choice, not the row's type (R4-G7 T8 RIDER-13). */
30
+ export declare function isCanonicalDamageType(type: string): boolean;
31
+ /** The schema's own damage-type sentinel for a rider whose type the PLAYER picks when the damage is dealt
32
+ * (`feature-effect-schema.ts`, the `damage-bonus` arm: "player-selected damage type at action time"). It is a
33
+ * declared schema value, not a prose spelling, so it never inherits the weapon row's type: the rider keeps it,
34
+ * and the sheet prints the amount with no type and {@link CHOSEN_DAMAGE_TYPE_NOTE} in the row's caption
35
+ * (R4-G7 T8 RIDER-13). */
36
+ export declare const CHOSEN_DAMAGE_TYPE = "chosen";
37
+ /** The caption words a renderer prints for a {@link CHOSEN_DAMAGE_TYPE} rider in place of a damage type. One
38
+ * constant, so no renderer spells the phrase itself. */
39
+ export declare const CHOSEN_DAMAGE_TYPE_NOTE = "damage type of your choice";
40
+ /** Is `type` the {@link CHOSEN_DAMAGE_TYPE} sentinel, whatever its casing or surrounding space? */
41
+ export declare function isChosenDamageType(type: string | undefined): boolean;
42
+ /** Can a damage rider be printed INSIDE the damage text, as a dice chip? Only when it reads as a dice
43
+ * expression or a (signed) number, optionally followed by ONE canonical damage type: `1d6`, `+2`,
44
+ * `1d6 fire`. Anything else is prose ("your Wisdom modifier necrotic", "half your fighter level
45
+ * slashing") and belongs in the row's caption instead (R4-G7 T6a E-4 (c)).
46
+ *
47
+ * `text` is the rider's PRINTED form, its amount plus its resolved damage type, because a shipped
48
+ * rider may carry the type inside the amount itself (`{amount: "1d6 fire"}` with no `damage_type`, the
49
+ * migrated manual override of `pc.equipment.ts`). */
50
+ export declare function isRenderableDamageText(text: string): boolean;
package/dist/dnd/math.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import {
2
+ CHOSEN_DAMAGE_TYPE,
3
+ CHOSEN_DAMAGE_TYPE_NOTE,
2
4
  abilityModifier,
3
5
  abilityNameToKey,
4
6
  attackBonus,
@@ -6,6 +8,9 @@ import {
6
8
  formatModifier,
7
9
  hitDiceSizeFromCreatureSize,
8
10
  hpFromHitDice,
11
+ isCanonicalDamageType,
12
+ isChosenDamageType,
13
+ isRenderableDamageText,
9
14
  parseHitDiceFormula,
10
15
  passive,
11
16
  passivePerception,
@@ -14,9 +19,11 @@ import {
14
19
  saveDC,
15
20
  savingThrow,
16
21
  skillBonus
17
- } from "../chunk-NAEPSZAM.js";
18
- import "../chunk-WP2CX2JO.js";
22
+ } from "../chunk-PDJSUH24.js";
23
+ import "../chunk-WELIKLZF.js";
19
24
  export {
25
+ CHOSEN_DAMAGE_TYPE,
26
+ CHOSEN_DAMAGE_TYPE_NOTE,
20
27
  abilityModifier,
21
28
  abilityNameToKey,
22
29
  attackBonus,
@@ -24,6 +31,9 @@ export {
24
31
  formatModifier,
25
32
  hitDiceSizeFromCreatureSize,
26
33
  hpFromHitDice,
34
+ isCanonicalDamageType,
35
+ isChosenDamageType,
36
+ isRenderableDamageText,
27
37
  parseHitDiceFormula,
28
38
  passive,
29
39
  passivePerception,
@@ -1,6 +1,6 @@
1
1
  // src/dnd/prose-tags.ts
2
2
  function convert5eToolsTags(text) {
3
- const rewritten = text.replace(/\{@atk\s+mw,rw\}/gi, "Melee or Ranged Weapon Attack:").replace(/\{@atk\s+mws\}/gi, "Melee or Ranged Weapon Attack:").replace(/\{@atk\s+msw\}/gi, "Melee or Ranged Spell Attack:").replace(/\{@atk\s+mw\}/gi, "Melee Weapon Attack:").replace(/\{@atk\s+rw\}/gi, "Ranged Weapon Attack:").replace(/\{@atk\s+ms\}/gi, "Melee Spell Attack:").replace(/\{@atk\s+rs\}/gi, "Ranged Spell Attack:").replace(/\{@hit\s+(\d+)\}/gi, "`atk:+$1`").replace(/\{@h\}/gi, "Hit:").replace(/\{@damage\s+([^}]+)\}/gi, "`damage:$1`").replace(/\{@dice\s+([^}]+)\}/gi, "`roll:$1`").replace(/\{@d20\s+([^}]+)\}/gi, "`roll:d20$1`").replace(/\{@dc\s+(\d+)\}/gi, "`dc:$1`").replace(/\{@recharge\s+(\d)\}/gi, "(Recharge $1-6)").replace(/\{@recharge\}/gi, "(Recharge)").replace(/\{@chance\s+(\d+)\}/gi, "$1% chance").replace(/\{@b(?:old)?\s+([^}]+)\}/gi, "**$1**").replace(/\{@i(?:talic)?\s+([^}]+)\}/gi, "_$1_").replace(/\{@s(?:trike)?\s+([^}]+)\}/gi, "~~$1~~").replace(/\{@note\s+([^}]+)\}/gi, "($1)").replace(/\{@spell\s+([^|}]+)[^}]*\}/gi, "_$1_").replace(/\{@item\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@creature\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@condition\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@skill\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@sense\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@action\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@status\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@ability\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@class\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@feat\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@background\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@race\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@disease\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@hazard\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@plane\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@language\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@book\s+([^|}]+)[^}]*\}/gi, "_$1_").replace(/\{@adventure\s+([^|}]+)[^}]*\}/gi, "_$1_").replace(/\{@\w+\s+([^}]+)\}/g, "$1");
3
+ const rewritten = text.replace(/\{@atk\s+mw,rw\}/gi, "Melee or Ranged Weapon Attack:").replace(/\{@atk\s+mws\}/gi, "Melee or Ranged Weapon Attack:").replace(/\{@atk\s+msw\}/gi, "Melee or Ranged Spell Attack:").replace(/\{@atk\s+mw\}/gi, "Melee Weapon Attack:").replace(/\{@atk\s+rw\}/gi, "Ranged Weapon Attack:").replace(/\{@atk\s+ms\}/gi, "Melee Spell Attack:").replace(/\{@atk\s+rs\}/gi, "Ranged Spell Attack:").replace(/\{@hit\s+(\d+)\}/gi, "`atk:+$1`").replace(/\{@h\}\s*/gi, "Hit: ").replace(/\{@damage\s+([^}]+)\}/gi, "`damage:$1`").replace(/\{@dice\s+([^}]+)\}/gi, "`roll:$1`").replace(/\{@d20\s+([^}]+)\}/gi, "`roll:d20$1`").replace(/\{@dc\s+(\d+)\}/gi, "`dc:$1`").replace(/\{@recharge\s+(\d)\}/gi, "(Recharge $1-6)").replace(/\{@recharge\}/gi, "(Recharge)").replace(/\{@chance\s+(\d+)\}/gi, "$1% chance").replace(/\{@b(?:old)?\s+([^}]+)\}/gi, "**$1**").replace(/\{@i(?:talic)?\s+([^}]+)\}/gi, "_$1_").replace(/\{@s(?:trike)?\s+([^}]+)\}/gi, "~~$1~~").replace(/\{@note\s+([^}]+)\}/gi, "($1)").replace(/\{@spell\s+([^|}]+)[^}]*\}/gi, "_$1_").replace(/\{@item\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@creature\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@condition\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@skill\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@sense\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@action\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@status\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@ability\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@class\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@feat\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@background\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@race\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@disease\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@hazard\s+([^|}]+)[^}]*\}/gi, "**$1**").replace(/\{@plane\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@language\s+([^|}]+)[^}]*\}/gi, "$1").replace(/\{@book\s+([^|}]+)[^}]*\}/gi, "_$1_").replace(/\{@adventure\s+([^|}]+)[^}]*\}/gi, "_$1_").replace(/\{@\w+\s+([^}]+)\}/g, "$1");
4
4
  return decorateProseDice(rewritten);
5
5
  }
6
6
  function decorateProseDice(text) {
@@ -1,16 +1,23 @@
1
1
  /**
2
- * Tiny DSL for Resource.max_formula and Resource.recovery[].amount (SP4d). Grammar:
2
+ * Tiny DSL for Resource.max_formula and Resource.recovery[].amount (SP4d; R4-G4 §6 added max / min). Grammar:
3
3
  * expr := term (('+' | '-') term)*
4
4
  * term := factor (('*' | '/') factor)*
5
- * factor := number | string-arg-only | ident | '{' ident '}' | fn '(' expr ')'
5
+ * factor := number | string-arg-only | ident | '{' ident '}' | fn1 '(' expr ')' | fnN '(' expr (',' expr)+ ')'
6
6
  * | "column" '(' string ')' | '(' expr ')'
7
- * fn := 'ceil' | 'floor'
7
+ * fn1 := 'ceil' | 'floor' (exactly one argument)
8
+ * fnN := 'max' | 'min' (two or more comma-separated arguments)
8
9
  * ident := level | class_level | prof | <abil>_mod
9
- * The single-quoted string literal (e.g. 'Seals') is valid ONLY as the
10
- * argument to column(...).
11
- * '*' and '/' bind tighter than '+'/'-'. '/' is real division; wrap in
12
- * ceil()/floor() for integer results (e.g. "ceil({class_level}/2)").
13
- * `999` is the at-will sentinel; it evaluates as the literal 999.
10
+ * The single-quoted string literal (e.g. 'Seals') is valid ONLY as the argument to column(...).
11
+ * '*' and '/' bind tighter than '+'/'-'. '/' is real division; wrap in ceil()/floor() for integer
12
+ * results (e.g. "ceil({class_level}/2)"). "max(1, {cha_mod})" is how the data says "a minimum of once".
13
+ * `AT_WILL_MAX` (999) is the at-will sentinel: it evaluates as the literal 999; R4-G4 T3 (§5) taught
14
+ * the plugin's charge boxes to render "at will" for it instead of 999 boxes (the `opts.atWill` branch
15
+ * at the head of `renderChargeBoxes`, fed by the `atWill: fu.max === AT_WILL_MAX` opt its callers
16
+ * pass). Measured 2026-09-05 on the plugin tree that carries T7b with
17
+ * `grep -rn "atWill:" packages/obsidian/src`: FOUR lines, the two T3 feature trackers
18
+ * `renderCardResource` and `renderFirstResourceTracker`, T5's pool-tab dice head `renderPoolHead`,
19
+ * and T7b's `renderPickTracker`, which draws a pool pick's own `uses` on the pool row, the granted
20
+ * row and the boon row.
14
21
  */
15
22
  export interface FormulaBindings {
16
23
  level: number;
@@ -27,6 +34,7 @@ export interface FormulaBindings {
27
34
  * Read by the `column('Name')` accessor; missing name → 0. */
28
35
  columns: Record<string, number>;
29
36
  }
37
+ export declare const AT_WILL_MAX = 999;
30
38
  export declare function isValidMaxFormula(formula: string): boolean;
31
39
  export declare function evaluateMaxFormula(formula: string, bindings: FormulaBindings): number;
32
40
  export interface ScalableResource {
@@ -36,6 +44,24 @@ export interface ScalableResource {
36
44
  max: string;
37
45
  }[];
38
46
  }
39
- /** The max-formula string in effect at `totalLevel`: the highest `scales_at`
40
- * step whose level ≤ totalLevel, else the base `max_formula`. */
47
+ /** The max-formula string in effect at `totalLevel`: the highest `scales_at` step whose level
48
+ * ≤ totalLevel (the FIRST declaration wins on a duplicate level; a `level: 0` step is skipped), else
49
+ * the base `max_formula`. Parse-free, so a die-string step comes back as the die string. Its one
50
+ * production caller, the plugin's `seedFeatureUses` (`pc.resource-seed.ts`), moved to
51
+ * `resolveMaxCountAt` in R4-G4 T3, so it is now TEST-ONLY: zero callers in this repo's `src/` and zero
52
+ * in the plugin's `packages/obsidian/src/`, pinned by this repo's `tests/resource-formula.test.ts`
53
+ * (its Bardic-Die-2024 and duplicate-level-tie cases) and by the plugin's cross-repo file of the same
54
+ * name (its `describe("resolveMaxAt")` block). */
41
55
  export declare function resolveMaxAt(totalLevel: number, resource: ScalableResource): string;
56
+ /** The max COUNT in effect at `level`: the highest `scales_at` step at or below `level` whose `max`
57
+ * PARSES under the DSL, else the base `max_formula` if it parses, else null. Ties break exactly as
58
+ * `resolveMaxAt` breaks them (the FIRST declaration at a duplicate level wins, a `level: 0` step is
59
+ * skipped), so a duplicate-level pair of PARSING steps cannot make the count and the die label pick
60
+ * different steps; where one member of the pair is a die string they DO pick different steps
61
+ * (`[{level:3,max:"1d8"},{level:3,max:"3"}]` at 3 reads 3 here and "1d8" there), which is the same
62
+ * parse-vs-string divergence described next. A step that does not parse is a die size (Bardic Die
63
+ * 2024: "1d8" at 5), not a count, and is skipped HERE while `resolveMaxAt` still returns it; a base
64
+ * that does not parse (Sneak Attack "1d6") is a damage die, correctly no tracker. `resolveMaxAt`
65
+ * keeps the parse-free string lookup; R4-G4 T3 handed its one production caller, the plugin seed, to
66
+ * this function (see its docblock), so a null here is what the seed warns on and leaves un-seeded. */
67
+ export declare function resolveMaxCountAt(level: number, resource: ScalableResource, bindings: FormulaBindings): number | null;
@@ -1,159 +1,14 @@
1
- // src/dnd/resource-formula.ts
2
- var IDENTS = /* @__PURE__ */ new Set([
3
- "level",
4
- "class_level",
5
- "prof",
6
- "str_mod",
7
- "dex_mod",
8
- "con_mod",
9
- "int_mod",
10
- "wis_mod",
11
- "cha_mod"
12
- ]);
13
- var FUNCS = /* @__PURE__ */ new Set(["ceil", "floor"]);
14
- function tokenize(input) {
15
- input = input.trim();
16
- const tokens = [];
17
- const re = /\s*('[^']*'|\{[a-z_]+\}|[a-z_]+|\d+|[+\-*/()])/y;
18
- let i = 0;
19
- while (i < input.length) {
20
- re.lastIndex = i;
21
- const m = re.exec(input);
22
- if (!m || m.index !== i) return null;
23
- const raw = m[1];
24
- i = re.lastIndex;
25
- if (/^\d+$/.test(raw)) tokens.push({ kind: "num", value: parseInt(raw, 10) });
26
- else if (raw.startsWith("'")) tokens.push({ kind: "str", value: raw.slice(1, -1) });
27
- else if (raw === "(") tokens.push({ kind: "lparen" });
28
- else if (raw === ")") tokens.push({ kind: "rparen" });
29
- else if (raw === "+" || raw === "-" || raw === "*" || raw === "/") tokens.push({ kind: "op", op: raw });
30
- else {
31
- const braced = raw.startsWith("{");
32
- const name = braced ? raw.slice(1, -1) : raw;
33
- if (!braced && FUNCS.has(name)) tokens.push({ kind: "fn", name });
34
- else if (!braced && name === "column") tokens.push({ kind: "col" });
35
- else if (IDENTS.has(name)) tokens.push({ kind: "id", name });
36
- else return null;
37
- }
38
- }
39
- return tokens.length > 0 ? tokens : null;
40
- }
41
- function evalTokens(tokens, b) {
42
- let pos = 0;
43
- const peek = () => tokens[pos];
44
- const factor = () => {
45
- var _a, _b, _c, _d, _e, _f, _g;
46
- const t = peek();
47
- if (!t) return null;
48
- if (t.kind === "num") {
49
- pos++;
50
- return t.value;
51
- }
52
- if (t.kind === "id") {
53
- const val = b[t.name];
54
- if (val === void 0) return null;
55
- pos++;
56
- return val;
57
- }
58
- if (t.kind === "fn") {
59
- pos++;
60
- if (((_a = peek()) == null ? void 0 : _a.kind) !== "lparen") return null;
61
- pos++;
62
- const inner = expr();
63
- if (inner === null) return null;
64
- if (((_b = peek()) == null ? void 0 : _b.kind) !== "rparen") return null;
65
- pos++;
66
- return t.name === "ceil" ? Math.ceil(inner) : Math.floor(inner);
67
- }
68
- if (t.kind === "col") {
69
- pos++;
70
- if (((_c = peek()) == null ? void 0 : _c.kind) !== "lparen") return null;
71
- pos++;
72
- const s = peek();
73
- if (!s || s.kind !== "str") return null;
74
- pos++;
75
- if (((_d = peek()) == null ? void 0 : _d.kind) !== "rparen") return null;
76
- pos++;
77
- return (_f = (_e = b.columns) == null ? void 0 : _e[s.value]) != null ? _f : 0;
78
- }
79
- if (t.kind === "lparen") {
80
- pos++;
81
- const inner = expr();
82
- if (inner === null) return null;
83
- if (((_g = peek()) == null ? void 0 : _g.kind) !== "rparen") return null;
84
- pos++;
85
- return inner;
86
- }
87
- return null;
88
- };
89
- const term = () => {
90
- var _a;
91
- let v = factor();
92
- if (v === null) return null;
93
- while (((_a = peek()) == null ? void 0 : _a.kind) === "op" && (peek().op === "*" || peek().op === "/")) {
94
- const op = peek().op;
95
- pos++;
96
- const r = factor();
97
- if (r === null) return null;
98
- v = op === "*" ? v * r : v / r;
99
- }
100
- return v;
101
- };
102
- const expr = () => {
103
- var _a;
104
- let v = term();
105
- if (v === null) return null;
106
- while (((_a = peek()) == null ? void 0 : _a.kind) === "op" && (peek().op === "+" || peek().op === "-")) {
107
- const op = peek().op;
108
- pos++;
109
- const r = term();
110
- if (r === null) return null;
111
- v = op === "+" ? v + r : v - r;
112
- }
113
- return v;
114
- };
115
- const result = expr();
116
- if (result === null || pos !== tokens.length) return null;
117
- return result;
118
- }
119
- var ZERO = {
120
- level: 1,
121
- class_level: 1,
122
- prof: 2,
123
- str_mod: 0,
124
- dex_mod: 0,
125
- con_mod: 0,
126
- int_mod: 0,
127
- wis_mod: 0,
128
- cha_mod: 0,
129
- columns: {}
130
- };
131
- function isValidMaxFormula(formula) {
132
- const toks = tokenize(formula);
133
- if (!toks) return false;
134
- return evalTokens(toks, ZERO) !== null;
135
- }
136
- function evaluateMaxFormula(formula, bindings) {
137
- const toks = tokenize(formula);
138
- if (!toks) throw new Error(`invalid max_formula: ${JSON.stringify(formula)}`);
139
- const v = evalTokens(toks, bindings);
140
- if (v === null) throw new Error(`invalid max_formula: ${JSON.stringify(formula)}`);
141
- return v;
142
- }
143
- function resolveMaxAt(totalLevel, resource) {
144
- var _a;
145
- let chosen = resource.max_formula;
146
- let best = 0;
147
- for (const step of (_a = resource.scales_at) != null ? _a : []) {
148
- if (step.level <= totalLevel && step.level > best) {
149
- best = step.level;
150
- chosen = step.max;
151
- }
152
- }
153
- return chosen;
154
- }
1
+ import {
2
+ AT_WILL_MAX,
3
+ evaluateMaxFormula,
4
+ isValidMaxFormula,
5
+ resolveMaxAt,
6
+ resolveMaxCountAt
7
+ } from "../chunk-EVMUJYUD.js";
155
8
  export {
9
+ AT_WILL_MAX,
156
10
  evaluateMaxFormula,
157
11
  isValidMaxFormula,
158
- resolveMaxAt
12
+ resolveMaxAt,
13
+ resolveMaxCountAt
159
14
  };
@@ -2,9 +2,9 @@ import {
2
2
  STATIC_FALLBACK_CONTEXT,
3
3
  convertDescToTags,
4
4
  detectSpellcastingAbility
5
- } from "../chunk-RAWJTA45.js";
6
- import "../chunk-NAEPSZAM.js";
7
- import "../chunk-WP2CX2JO.js";
5
+ } from "../chunk-DEEHHLAP.js";
6
+ import "../chunk-PDJSUH24.js";
7
+ import "../chunk-WELIKLZF.js";
8
8
  export {
9
9
  STATIC_FALLBACK_CONTEXT,
10
10
  convertDescToTags,
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-QYZV2MQA.js";
5
5
  import {
6
6
  ABILITY_KEYS
7
- } from "../chunk-WP2CX2JO.js";
7
+ } from "../chunk-WELIKLZF.js";
8
8
  export {
9
9
  ABILITY_KEYS as ABILITIES,
10
10
  normalizeTagType,
@@ -0,0 +1,2 @@
1
+ export declare function warnOnce(key: string, msg: string): void;
2
+ export declare function __resetWarnOnceForTests(): void;
@@ -0,0 +1,8 @@
1
+ import {
2
+ __resetWarnOnceForTests,
3
+ warnOnce
4
+ } from "../chunk-LVQLWRCU.js";
5
+ export {
6
+ __resetWarnOnceForTests,
7
+ warnOnce
8
+ };
@@ -2,8 +2,8 @@ import {
2
2
  resolveBaseItem,
3
3
  resolveBaseItemOfType,
4
4
  vaultPathToSlug
5
- } from "../chunk-4LZJXJJS.js";
6
- import "../chunk-LV4SNWZQ.js";
5
+ } from "../chunk-WXWPK3LI.js";
6
+ import "../chunk-6X4KUDMU.js";
7
7
  export {
8
8
  resolveBaseItem,
9
9
  resolveBaseItemOfType,
@@ -10,3 +10,16 @@ export declare function slugify(name: string): string;
10
10
  * `parts.slice(2).join("_")`. Tolerates a nullish slug (degrades to "") so a
11
11
  * malformed entity can't hard-crash the builder; the resolver backfills real slugs. */
12
12
  export declare function bareEntitySlug(slug: string | null | undefined): string;
13
+ /** "[[SRD 2024/Classes/Fighter]]" to "fighter" (tail segment, slugified).
14
+ *
15
+ * The tail goes through the SAME `slugify` that mints registry slugs, so an apostrophe is DELETED rather than
16
+ * hyphenated ("Mage's Bane" to "mages-bane", where the old hand-rolled regex minted "mage-s-bane" and missed
17
+ * every time). Only ever call this on a human NAME tail, never on a full `<prefix>_<name>` slug, which slugify
18
+ * would destroy: `pc.decision-engine.ts`'s `resolveEntityRef` is the function that strips a `[[wikilink]]`
19
+ * WITHOUT slugifying, and it is the one to use for a STORED slug.
20
+ *
21
+ * MOVED here from `pc.decision-engine.ts` in R4-G5 T1, beside `bareEntitySlug`, so `pc.pools.ts` can use it
22
+ * without importing the decision engine: the engine imports `pc.pools` for `strandedPicks`, and the old
23
+ * direction would be a cycle. The engine keeps a re-export, so its three plugin importers and its own test
24
+ * are untouched. */
25
+ export declare function wikilinkTailSlug(link: string): string;
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  bareEntitySlug,
3
- slugify
4
- } from "../chunk-LV4SNWZQ.js";
3
+ slugify,
4
+ wikilinkTailSlug
5
+ } from "../chunk-6X4KUDMU.js";
5
6
  export {
6
7
  bareEntitySlug,
7
- slugify
8
+ slugify,
9
+ wikilinkTailSlug
8
10
  };
@@ -0,0 +1,3 @@
1
+ /** The human name of a wikilink target: `[[Player's Handbook (2024)/OptionalFeatures/Pact of the Blade]]` →
2
+ * "Pact of the Blade"; `[[path|Alias]]` → "Alias". The converter emits vault PATHS with no alias. */
3
+ export declare function wikilinkDisplayName(link: string): string;
@@ -0,0 +1,10 @@
1
+ // src/entities/wikilink-display.ts
2
+ function wikilinkDisplayName(link) {
3
+ const inner = link.trim().replace(/^\[\[/, "").replace(/\]\]$/, "");
4
+ const afterAlias = inner.includes("|") ? inner.slice(inner.lastIndexOf("|") + 1) : inner;
5
+ const afterSlash = afterAlias.includes("/") ? afterAlias.slice(afterAlias.lastIndexOf("/") + 1) : afterAlias;
6
+ return afterSlash.trim();
7
+ }
8
+ export {
9
+ wikilinkDisplayName
10
+ };
@@ -0,0 +1,32 @@
1
+ import type { FeatCategory } from "../types/choice.js";
2
+ /** 5etools feat `category` codes, as DATA beside the schema (never a renderer switch). The converter emits the raw
3
+ * code on `exclusive-feat-category` and `feat-category` prerequisites (today only "D", on 14 documents; the G7
4
+ * handoff asks it for a name). `FS:P` and `FS:R` are the Paladin- and Ranger-scoped fighting-style categories.
5
+ * Measured in data/feats.json: G 80, EB 29, O 25, D 13, FS 10, DG 9, FS:P 1, FS:R 1. Unknown codes fall back to
6
+ * the code itself. */
7
+ export declare const FEAT_CATEGORY_CODE_LABELS: Readonly<Record<string, string>>;
8
+ export declare function featCategoryLabel(code: string): string;
9
+ /** 5etools `feat_progression.category` codes to the ENGINE's `FeatCategory` vocabulary (R4-G5 §3.2.5,
10
+ * invariant 3): the DATA table the class-side progression reader maps through, beside the label table above.
11
+ * MEASURED over the converted corpus 2026-09-07: 18 `feat_progression` rows across 15 documents, codes
12
+ * {EB 13, FS 4, FS:P 1, FS:R 1, O 1}. The FS four are Fighter 2024 (level 1), Paladin 2024 and Ranger 2024
13
+ * (level 2, each carrying `["FS","FS:P"]` / `["FS","FS:R"]`) and the Champion 2024 SUBCLASS (level 7); the 13
14
+ * EB rows are the 12 PHB 2024 classes plus the Eberron Artificer, all at level 19. The single `O` row sits on
15
+ * an OPTIONAL FEATURE (Lessons of the First Ones) with a `"*"` progression key, outside the class walk, so `O`
16
+ * and `G` have zero live class-side carriers and are declared for completeness. `D` (Dragonmark) and `DG`
17
+ * (Dark Gift) map to NOTHING on purpose: they are not player-choosable feat categories in this vocabulary, and
18
+ * a row carrying only those emits no decision and warns once at the reader. G7 handoff (spec §11): the
19
+ * converter re-emits these codes in engine vocabulary and this table retires. */
20
+ export declare const FEAT_PROGRESSION_CATEGORY: Readonly<Record<string, FeatCategory>>;
21
+ /** The ONE `FeatCategory` to optional-feature `feature_type` pairing (R4-G5 §3.2.5). The shape-B suppression
22
+ * reads it to decide that a class carrying a fighting-style `feat_progression` row must NOT also be offered the
23
+ * recognizer's `fighting_style` optional-feature synthetic at that level. `Partial<Record<...>>` because three
24
+ * of the four categories pair with nothing, which is also what makes the lookup return `string | undefined`
25
+ * without an own-property guard. */
26
+ export declare const FEATURE_TYPE_FOR_CATEGORY: Readonly<Partial<Record<FeatCategory, string>>>;
27
+ /** Reduce a `feat_progression` entry's `category` ARRAY to ONE engine category: the FIRST code that maps wins, so
28
+ * the shipped `["FS","FS:P"]` (Paladin) and `["FS","FS:R"]` (Ranger) each yield exactly ONE decision instead of
29
+ * two. OWN-property guarded (invariant 3): the codes come from converter data, so `constructor` would otherwise
30
+ * index a FUNCTION through the prototype chain. Returns undefined when nothing maps (an all-`D` row); the
31
+ * CALLER warns, because this stays pure. */
32
+ export declare function featProgressionCategory(codes: readonly string[] | undefined): FeatCategory | undefined;
@@ -0,0 +1,14 @@
1
+ import {
2
+ FEATURE_TYPE_FOR_CATEGORY,
3
+ FEAT_CATEGORY_CODE_LABELS,
4
+ FEAT_PROGRESSION_CATEGORY,
5
+ featCategoryLabel,
6
+ featProgressionCategory
7
+ } from "../chunk-YBLDE5CG.js";
8
+ export {
9
+ FEATURE_TYPE_FOR_CATEGORY,
10
+ FEAT_CATEGORY_CODE_LABELS,
11
+ FEAT_PROGRESSION_CATEGORY,
12
+ featCategoryLabel,
13
+ featProgressionCategory
14
+ };
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  featCodec
3
- } from "../chunk-73HOFQQ5.js";
4
- import "../chunk-XTIQWSFM.js";
5
- import "../chunk-Q5K6LWBZ.js";
6
- import "../chunk-NXSWH3AE.js";
7
- import "../chunk-NXR3BD2V.js";
3
+ } from "../chunk-ESS46GR4.js";
4
+ import "../chunk-D5U7GMPX.js";
5
+ import "../chunk-RIXI5D3R.js";
6
+ import "../chunk-YLL2QXY2.js";
7
+ import "../chunk-T26ZVCL5.js";
8
+ import "../chunk-WPWOHGPI.js";
9
+ import "../chunk-UA5KS2PL.js";
8
10
  export {
9
11
  featCodec
10
12
  };
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  parseFeat
3
- } from "../chunk-XTIQWSFM.js";
4
- import "../chunk-Q5K6LWBZ.js";
5
- import "../chunk-NXSWH3AE.js";
6
- import "../chunk-NXR3BD2V.js";
3
+ } from "../chunk-D5U7GMPX.js";
4
+ import "../chunk-RIXI5D3R.js";
5
+ import "../chunk-YLL2QXY2.js";
6
+ import "../chunk-T26ZVCL5.js";
7
+ import "../chunk-WPWOHGPI.js";
8
+ import "../chunk-UA5KS2PL.js";
7
9
  export {
8
10
  parseFeat
9
11
  };