@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
@@ -2,15 +2,24 @@ import type { FeatureEffect } from "./feature-effect.js";
2
2
  import type { ActionCost, ResetTrigger, ResourceConsumption } from "./resource.js";
3
3
  import type { Duration } from "../schemas/duration-schema.js";
4
4
  import type { Edition } from "./edition.js";
5
+ import type { AdditionalSpellsEntry, ProgressionEntry } from "../schemas/entity-extras-schema.js";
5
6
  /**
6
7
  * Documentation-only union of the canonical SRD feature types. The
7
8
  * `feature_type` field is now an open string (see {@link OptionalFeatureEntity})
8
9
  * so homebrew categories parse; this union is kept purely as a reference list.
9
10
  */
10
11
  export type OptionalFeatureKind = "invocation" | "fighting_style" | "metamagic" | "maneuver" | "infusion";
12
+ /** A class/subclass named reference carried by a level prerequisite; mirrors namedRefSchema. */
13
+ export type OptionalFeatureNamedRef = {
14
+ name: string;
15
+ source?: string;
16
+ visible_stats?: boolean;
17
+ };
11
18
  export type OptionalFeaturePrerequisite = {
12
19
  kind: "level";
13
20
  min: number;
21
+ class?: OptionalFeatureNamedRef;
22
+ subclass?: OptionalFeatureNamedRef;
14
23
  } | {
15
24
  kind: "spell-known";
16
25
  spell: string;
@@ -27,6 +36,14 @@ export type OptionalFeaturePrerequisite = {
27
36
  } | {
28
37
  kind: "other";
29
38
  detail: string;
39
+ } | {
40
+ kind: "optionalfeature";
41
+ optionalfeature: string;
42
+ } | {
43
+ kind: "spell-choose";
44
+ choose: string;
45
+ entry?: string;
46
+ entry_summary?: string;
30
47
  };
31
48
  export interface OptionalFeatureEntity {
32
49
  slug: string;
@@ -57,4 +74,15 @@ export interface OptionalFeatureEntity {
57
74
  /** Phase 3 activatable buffs: when true, the boon's effects fold only while its
58
75
  * slug is present in `Character.state.active_buffs` (toggled in the PoolTab). */
59
76
  activatable?: boolean;
77
+ /** Converter extras declared by optional-feature.schema.ts §2.5; all optional.
78
+ * `rendering_hint` is the ONE of these whose VALUES are load-bearing (88 records carry a
79
+ * real marker such as `dice-pool`); reading it is G4's row, declaring it is this one's. */
80
+ rendering_hint?: string;
81
+ additional_spells?: AdditionalSpellsEntry[];
82
+ is_class_feature_variant?: boolean;
83
+ has_fluff_images?: boolean;
84
+ feat_progression?: ProgressionEntry[];
85
+ optionalfeature_progression?: ProgressionEntry[];
86
+ /** One wikilink, or an array of them for the multi-fluff-image emit. */
87
+ image?: string | string[];
60
88
  }
@@ -1,4 +1,6 @@
1
- export type ResetTrigger = "short-rest" | "long-rest" | "dawn" | "dusk" | "turn" | "round" | "custom";
1
+ /** `either` = recovered by a short OR a long rest (R4-G3a §8.2; the 43 "short or
2
+ * long rest" rows). Kept in step with `resetTriggerEnum` in schemas/resource-schema.ts. */
3
+ export type ResetTrigger = "short-rest" | "long-rest" | "either" | "dawn" | "dusk" | "turn" | "round" | "custom";
2
4
  export type ActionCost = "action" | "bonus-action" | "reaction" | "free" | "special";
3
5
  export interface ResourceDie {
4
6
  base: string;
@@ -11,6 +13,18 @@ export interface ResourceRecovery {
11
13
  action?: ActionCost;
12
14
  uses?: number;
13
15
  reset: ResetTrigger;
16
+ /** The recovery KIND; absent = `uses` (R4-G4 §7, UR2), applied by the RESOLVER
17
+ * (pc/pc.resources.ts `resolveRecovery`) and never by a schema default, because
18
+ * the sheet reads RAW entities. The CARRIERS, re-measured 2026-09-05 after R4-G4
19
+ * T2b's regen shipped the bundle (this sentence was authored at T2, BEFORE that
20
+ * regen, and said ZERO, which it no longer is): `grep -rl "restores:" --include='*.md'`
21
+ * returns the TWO bundle Wizard notes (`SRD 2024/Classes/Wizard.md` and
22
+ * `SRD 5e/Classes/Wizard.md`), whose Arcane Recovery rows T2b's overlay stamps
23
+ * `spell-slots` and which §7.1 measured as the only spell-slot recoveries in shipped
24
+ * data; the embedded `.compendium-bundle/index.json` carries the key on one line for
25
+ * the same two rows; the converter corpus carries it on NO note. Kept in step with
26
+ * `resourceSchema.recovery` in schemas/resource-schema.ts. */
27
+ restores?: "uses" | "spell-slots";
14
28
  }
15
29
  export interface ResourceScaleStep {
16
30
  level: number;
@@ -34,7 +48,13 @@ export interface ResourceConsumption {
34
48
  source?: "resource" | "class-column" | "attack-dice";
35
49
  resource?: string;
36
50
  column?: string;
51
+ /** The MINIMUM spend when `amount_max` is present, otherwise the exact spend. */
37
52
  amount: number;
53
+ /** Upper bound of a RANGE spend (`amount` .. `amount_max`), int and positive.
54
+ * DECLARED only in R4-G3a §9: zero emitted carriers, zero readers; spending a
55
+ * range is G4's. Kept in step with `resourceConsumptionSchema` in
56
+ * schemas/resource-schema.ts, whose refine is undefined-safe. */
57
+ amount_max?: number;
38
58
  expend_condition?: "roll_succeeds" | "roll_fails" | "target_takes_damage" | "always";
39
59
  free_uses?: {
40
60
  amount: number;
@@ -24,8 +24,23 @@ export interface PoolGrant {
24
24
  at_level: number;
25
25
  }>;
26
26
  }
27
- /** Presentation hint for a data-declared tab. "text" reserved for later. */
28
- export type PoolLayout = "spell-like" | "blocks";
27
+ /** Presentation hint for a data-declared tab. `dice-pool` / `point-pool` are DERIVED from the
28
+ * members' `rendering_hint` at resolve time onto `ResolvedPool.layout` (pc/pool-layout.ts), and that
29
+ * derivation is UNCONDITIONAL: it runs whether or not the declaration carries a `layout`. R4-G4 T5
30
+ * LANDED the precedence in the plugin: `TabsContainer`'s dynamic-pool-tab loop reads the AUTHORED
31
+ * value first, then `ResolvedPool.layout`, then the `spell-like` default, and `PoolTab`'s `LAYOUTS`
32
+ * registry routes all four members (an unknown string degrades to `spell-like` and never throws).
33
+ * R4-G5 CLOSED that list, and NOT by adding values: `pool-selection` and `stance` never get a layout value
34
+ * (measured, spec §10: the three `stance`-hinted documents are field-identical to three unhinted siblings, and
35
+ * `pool-selection`'s meaning is delivered by `spell-like` plus the §5 head), and `granted-die-to-ally` is a
36
+ * PER-ROW affordance (`EntryAffordance` below), never a pool layout: mapping it here would bury four maneuvers
37
+ * under 39 on one vote of 43. This union keeps its four members and `RENDERING_HINT_LAYOUT` its two keys. */
38
+ export type PoolLayout = "spell-like" | "blocks" | "dice-pool" | "point-pool";
39
+ /** A PER-ENTRY affordance derived from ONE optional feature's own `rendering_hint` (R4-G5 §4.2.1). The sibling of
40
+ * `PoolLayout`, and deliberately not part of it: a layout is a POOL-level MAJORITY vote, so a hint carried by
41
+ * four of 43 maneuvers could never win one, and mapping it would be a monotone loss for the other 39. The table,
42
+ * the derivation and the caption live in `pc/pool-layout.ts`. ONE member today. */
43
+ export type EntryAffordance = "granted-die";
29
44
  /** A data-declared tab that renders a pool (one generic pool-tab per declaration). */
30
45
  export interface TabDecl {
31
46
  id: string;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  classifyWeaponRange
3
- } from "../chunk-353WXS6S.js";
4
- import "../chunk-LV4SNWZQ.js";
3
+ } from "../chunk-UIRMA5KM.js";
4
+ import "../chunk-6X4KUDMU.js";
5
5
  export {
6
6
  classifyWeaponRange
7
7
  };
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  weaponCodec
3
- } from "../chunk-5INR4NSL.js";
4
- import "../chunk-6NK2MUD3.js";
5
- import "../chunk-AFKTJXRA.js";
3
+ } from "../chunk-LDDCNPHD.js";
4
+ import "../chunk-HZA7OE7N.js";
5
+ import "../chunk-2DFENQIJ.js";
6
+ import "../chunk-UA5KS2PL.js";
6
7
  export {
7
8
  weaponCodec
8
9
  };
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  parseWeapon
3
- } from "../chunk-6NK2MUD3.js";
4
- import "../chunk-AFKTJXRA.js";
3
+ } from "../chunk-HZA7OE7N.js";
4
+ import "../chunk-2DFENQIJ.js";
5
+ import "../chunk-UA5KS2PL.js";
5
6
  export {
6
7
  parseWeapon
7
8
  };
@@ -30,5 +30,9 @@ export declare const weaponEntitySchema: z.ZodObject<{
30
30
  2024: "2024";
31
31
  }>;
32
32
  entries: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
33
+ rendering_hint: z.ZodOptional<z.ZodString>;
34
+ has_fluff: z.ZodOptional<z.ZodBoolean>;
35
+ has_fluff_images: z.ZodOptional<z.ZodBoolean>;
36
+ image: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
33
37
  raw: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
34
38
  }, z.core.$loose>;
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  weaponEntitySchema
3
- } from "../chunk-AFKTJXRA.js";
3
+ } from "../chunk-2DFENQIJ.js";
4
+ import "../chunk-UA5KS2PL.js";
4
5
  export {
5
6
  weaponEntitySchema
6
7
  };
@@ -1,4 +1,6 @@
1
1
  import type { Edition } from "@archivist-gg/dnd5e/types/edition";
2
+ import type { imageField } from "@archivist-gg/dnd5e/schemas/entity-extras-schema";
3
+ import type { z } from "zod";
2
4
  export type WeaponCategory = "simple-melee" | "simple-ranged" | "martial-melee" | "martial-ranged" | "natural" | string;
3
5
  export type DamageType = "acid" | "bludgeoning" | "cold" | "fire" | "force" | "lightning" | "necrotic" | "piercing" | "poison" | "psychic" | "radiant" | "slashing" | "thunder";
4
6
  export type ConditionalProperty = {
@@ -32,5 +34,11 @@ export interface WeaponEntity {
32
34
  page?: number;
33
35
  edition: Edition;
34
36
  entries?: unknown[];
37
+ /** Carried as the empty string on every converter weapon carrier today (98). */
38
+ rendering_hint?: string;
39
+ has_fluff?: boolean;
40
+ has_fluff_images?: boolean;
41
+ /** One wikilink, or an array for >= 2 fluff images (images-ON emit). */
42
+ image?: z.infer<typeof imageField>;
35
43
  raw?: Record<string, unknown>;
36
44
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@archivist-gg/dnd5e",
3
- "version": "0.3.1",
4
- "license": "MIT",
3
+ "version": "0.3.4",
4
+ "license": "MIT AND CC-BY-4.0",
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -1,30 +0,0 @@
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
-
27
- export {
28
- readTableColumn,
29
- numericColumnsAt
30
- };
@@ -1,158 +0,0 @@
1
- // src/monster/monster.parser.ts
2
- import { parseYaml, toStringSafe } from "@archivist-gg/core";
3
- var VALID_RECHARGE_TYPES = /* @__PURE__ */ new Set([
4
- "recharge_on_roll",
5
- "per_day",
6
- "per_short_rest",
7
- "per_long_rest"
8
- ]);
9
- function parseMonster(source) {
10
- var _a, _b, _c, _d, _e, _f, _g;
11
- const result = parseYaml(source, ["name"]);
12
- if (!result.success) return result;
13
- const raw = result.data;
14
- const monster = {
15
- name: toStringSafe(raw.name)
16
- };
17
- if (raw.size != null) monster.size = toStringSafe(raw.size);
18
- if (raw.type != null) monster.type = toStringSafe(raw.type);
19
- if (raw.subtype != null) monster.subtype = toStringSafe(raw.subtype);
20
- if (raw.alignment != null) monster.alignment = toStringSafe(raw.alignment);
21
- if (raw.cr != null) monster.cr = toStringSafe(raw.cr);
22
- if (Array.isArray(raw.ac)) {
23
- monster.ac = raw.ac.map((entry) => ({
24
- ac: Number(entry.ac),
25
- from: Array.isArray(entry.from) ? entry.from.map(String) : void 0
26
- }));
27
- }
28
- if (raw.hp && typeof raw.hp === "object") {
29
- const hp = raw.hp;
30
- monster.hp = {
31
- average: Number(hp.average),
32
- formula: hp.formula != null ? toStringSafe(hp.formula) : void 0
33
- };
34
- }
35
- if (raw.speed && typeof raw.speed === "object") {
36
- const speed = raw.speed;
37
- monster.speed = {};
38
- for (const key of ["walk", "fly", "swim", "climb", "burrow"]) {
39
- if (speed[key] != null) monster.speed[key] = Number(speed[key]);
40
- }
41
- }
42
- if (raw.abilities && typeof raw.abilities === "object") {
43
- const ab = raw.abilities;
44
- monster.abilities = {
45
- str: Number((_a = ab.str) != null ? _a : 10),
46
- dex: Number((_b = ab.dex) != null ? _b : 10),
47
- con: Number((_c = ab.con) != null ? _c : 10),
48
- int: Number((_d = ab.int) != null ? _d : 10),
49
- wis: Number((_e = ab.wis) != null ? _e : 10),
50
- cha: Number((_f = ab.cha) != null ? _f : 10)
51
- };
52
- }
53
- if (raw.saves && typeof raw.saves === "object") {
54
- const saves = {};
55
- for (const [key, val] of Object.entries(raw.saves)) {
56
- saves[key] = Number(val);
57
- }
58
- monster.saves = saves;
59
- }
60
- if (raw.skills && typeof raw.skills === "object") {
61
- const skills = {};
62
- for (const [key, val] of Object.entries(raw.skills)) {
63
- skills[key] = Number(val);
64
- }
65
- monster.skills = skills;
66
- }
67
- if (Array.isArray(raw.senses)) monster.senses = raw.senses.map(String);
68
- if (raw.passive_perception != null) monster.passive_perception = Number(raw.passive_perception);
69
- if (Array.isArray(raw.languages)) monster.languages = raw.languages.map(String);
70
- if (Array.isArray(raw.damage_vulnerabilities)) monster.damage_vulnerabilities = raw.damage_vulnerabilities.map(String);
71
- if (Array.isArray(raw.damage_resistances)) monster.damage_resistances = raw.damage_resistances.map(String);
72
- if (Array.isArray(raw.damage_immunities)) monster.damage_immunities = raw.damage_immunities.map(String);
73
- if (Array.isArray(raw.condition_immunities)) monster.condition_immunities = raw.condition_immunities.map(String);
74
- const parseAttack = (raw2) => {
75
- const a = {
76
- name: toStringSafe(raw2.name),
77
- type: toStringSafe(raw2.type)
78
- };
79
- if (typeof raw2.bonus === "number") a.bonus = raw2.bonus;
80
- if (typeof raw2.damage === "string") a.damage = raw2.damage;
81
- if (typeof raw2.damage_type === "string") a.damage_type = raw2.damage_type;
82
- if (typeof raw2.action === "string") a.action = raw2.action;
83
- if (Array.isArray(raw2.properties)) a.properties = raw2.properties.map(String);
84
- if (raw2.range && typeof raw2.range === "object") {
85
- const r = raw2.range;
86
- a.range = {};
87
- if (typeof r.normal === "number") a.range.normal = r.normal;
88
- if (typeof r.long === "number") a.range.long = r.long;
89
- if (typeof r.reach === "number") a.range.reach = r.reach;
90
- }
91
- if (raw2.extra_damage && typeof raw2.extra_damage === "object") {
92
- const ed = raw2.extra_damage;
93
- if (typeof ed.dice === "string" && typeof ed.type === "string") {
94
- a.extra_damage = { dice: ed.dice, type: ed.type };
95
- }
96
- }
97
- if (typeof raw2.condition === "string") a.condition = raw2.condition;
98
- return a;
99
- };
100
- const parseFeatures = (arr) => {
101
- if (!Array.isArray(arr)) return void 0;
102
- return arr.map((f) => {
103
- const feature = {
104
- name: toStringSafe(f.name),
105
- // Always set entries to an array so downstream renderers/editors
106
- // (which call .join/.map directly) keep working.
107
- entries: Array.isArray(f.entries) ? f.entries.map(String) : []
108
- };
109
- if (typeof f.description === "string") feature.description = f.description;
110
- if (Array.isArray(f.attacks)) {
111
- feature.attacks = f.attacks.map(parseAttack);
112
- }
113
- if (typeof f.action === "string") feature.action = f.action;
114
- if (f.recharge && typeof f.recharge === "object") {
115
- const r = f.recharge;
116
- if (typeof r.type === "string" && VALID_RECHARGE_TYPES.has(r.type) && typeof r.param === "number") {
117
- feature.recharge = {
118
- type: r.type,
119
- param: r.param
120
- };
121
- }
122
- }
123
- return feature;
124
- });
125
- };
126
- monster.traits = parseFeatures(raw.traits);
127
- monster.actions = parseFeatures(raw.actions);
128
- monster.reactions = parseFeatures(raw.reactions);
129
- const legendaryArrayRaw = Array.isArray(raw.legendary_actions) ? raw.legendary_actions : raw.legendary;
130
- monster.legendary_actions = parseFeatures(legendaryArrayRaw);
131
- if (raw.legendary_action_uses != null) {
132
- monster.legendary_action_uses = Number(raw.legendary_action_uses);
133
- } else if (raw.legendary_actions != null && !Array.isArray(raw.legendary_actions) && typeof raw.legendary_actions !== "object") {
134
- monster.legendary_action_uses = Number(raw.legendary_actions);
135
- }
136
- if (raw.legendary_resistance != null) monster.legendary_resistance = Number(raw.legendary_resistance);
137
- if (raw.columns != null) monster.columns = Number(raw.columns);
138
- if (!monster.legendary_resistance && monster.traits) {
139
- const lrIndex = monster.traits.findIndex(
140
- (t) => {
141
- var _a2;
142
- return /^Legendary Resistance\s*\(/i.test((_a2 = t.name) != null ? _a2 : "");
143
- }
144
- );
145
- if (lrIndex !== -1) {
146
- const match = (_g = monster.traits[lrIndex].name) == null ? void 0 : _g.match(/\((\d+)\/Day\)/i);
147
- if (match) {
148
- monster.legendary_resistance = parseInt(match[1], 10);
149
- monster.traits.splice(lrIndex, 1);
150
- }
151
- }
152
- }
153
- return { success: true, data: monster };
154
- }
155
-
156
- export {
157
- parseMonster
158
- };
@@ -1,49 +0,0 @@
1
- // src/types/choice.ts
2
- var ALL_SKILL_SLUGS = [
3
- "acrobatics",
4
- "animal-handling",
5
- "arcana",
6
- "athletics",
7
- "deception",
8
- "history",
9
- "insight",
10
- "intimidation",
11
- "investigation",
12
- "medicine",
13
- "nature",
14
- "perception",
15
- "performance",
16
- "persuasion",
17
- "religion",
18
- "sleight-of-hand",
19
- "stealth",
20
- "survival"
21
- ];
22
- var STANDARD_LANGUAGES = [
23
- "common",
24
- "dwarvish",
25
- "elvish",
26
- "giant",
27
- "gnomish",
28
- "goblin",
29
- "halfling",
30
- "orc"
31
- ];
32
- var EXOTIC_LANGUAGES = [
33
- "abyssal",
34
- "celestial",
35
- "deep-speech",
36
- "draconic",
37
- "infernal",
38
- "primordial",
39
- "sylvan",
40
- "undercommon"
41
- ];
42
- var ALL_LANGUAGES = [...STANDARD_LANGUAGES, ...EXOTIC_LANGUAGES];
43
-
44
- export {
45
- ALL_SKILL_SLUGS,
46
- STANDARD_LANGUAGES,
47
- EXOTIC_LANGUAGES,
48
- ALL_LANGUAGES
49
- };
@@ -1,143 +0,0 @@
1
- import {
2
- bareEntitySlug
3
- } from "./chunk-LV4SNWZQ.js";
4
- import {
5
- ABILITY_KEYS
6
- } from "./chunk-WP2CX2JO.js";
7
-
8
- // src/pc/pc.feature-effects.ts
9
- function emptyFeatureEffectTotals() {
10
- return {
11
- initiative_bonus: 0,
12
- hp_per_level_bonus: 0,
13
- hp_per_level_terms: [],
14
- speed_walk_bonus: 0,
15
- speed_walk_set: 0,
16
- senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
17
- ac_terms: [],
18
- resistances: [],
19
- condition_immunities: [],
20
- proficiencies: { skills: [], tools: [], languages: [], saves: [], armor: [], weapons: [] },
21
- weaponAbilities: [],
22
- rollModifiers: [],
23
- critRange: 20,
24
- extraAttack: 0,
25
- attackNotes: [],
26
- damageBonuses: []
27
- };
28
- }
29
- var ABILITY_NAME_TO_KEY = {
30
- strength: "str",
31
- dexterity: "dex",
32
- constitution: "con",
33
- intelligence: "int",
34
- wisdom: "wis",
35
- charisma: "cha"
36
- };
37
- function normalizeAbility(value) {
38
- var _a;
39
- const k = value.trim().toLowerCase();
40
- if (ABILITY_KEYS.includes(k)) return k;
41
- return (_a = ABILITY_NAME_TO_KEY[k]) != null ? _a : null;
42
- }
43
- function pushUnique(list, value) {
44
- const key = value.trim().toLowerCase();
45
- if (!key) return;
46
- if (!list.some((v) => v.trim().toLowerCase() === key)) list.push(value.trim());
47
- }
48
- function computeFeatureEffects(features, opts) {
49
- var _a, _b, _c;
50
- const out = emptyFeatureEffectTotals();
51
- for (const rf of features) {
52
- if (rf.feature.activatable === true) {
53
- const id = rf.feature.id;
54
- if (!id || !((_a = opts == null ? void 0 : opts.activeBuffs) == null ? void 0 : _a.has(id))) continue;
55
- }
56
- for (const eff of (_b = rf.feature.effects) != null ? _b : []) {
57
- applyEffect(out, eff, (_c = rf.feature.name) != null ? _c : "Feature");
58
- }
59
- }
60
- return out;
61
- }
62
- function applyEffect(out, eff, label) {
63
- var _a, _b;
64
- switch (eff.kind) {
65
- case "initiative-bonus":
66
- out.initiative_bonus += eff.value;
67
- break;
68
- case "hp-per-level-bonus":
69
- out.hp_per_level_bonus += eff.value;
70
- out.hp_per_level_terms.push({ label, value: eff.value });
71
- break;
72
- case "speed-bonus":
73
- if (eff.mode === "walk") {
74
- if (eff.set) out.speed_walk_set = Math.max(out.speed_walk_set, eff.value);
75
- else out.speed_walk_bonus += eff.value;
76
- }
77
- break;
78
- case "sense":
79
- out.senses[eff.type] = Math.max(out.senses[eff.type], eff.range);
80
- break;
81
- case "resistance":
82
- pushUnique(out.resistances, eff.damage_type);
83
- break;
84
- case "immune-condition":
85
- if (!eff.while) pushUnique(out.condition_immunities, eff.condition);
86
- break;
87
- case "proficiency": {
88
- if (eff.proficiency_type === "skill") {
89
- pushUnique(out.proficiencies.skills, eff.value.toLowerCase().replace(/\s+/g, "-"));
90
- } else if (eff.proficiency_type === "tool") {
91
- pushUnique(out.proficiencies.tools, eff.value);
92
- } else if (eff.proficiency_type === "language") {
93
- pushUnique(out.proficiencies.languages, eff.value);
94
- } else if (eff.proficiency_type === "armor") {
95
- pushUnique(out.proficiencies.armor, eff.value.toLowerCase());
96
- } else if (eff.proficiency_type === "weapon") {
97
- pushUnique(out.proficiencies.weapons, eff.value.toLowerCase());
98
- } else {
99
- const ab = normalizeAbility(eff.value);
100
- if (ab && !out.proficiencies.saves.includes(ab)) out.proficiencies.saves.push(ab);
101
- }
102
- break;
103
- }
104
- case "ac-bonus":
105
- out.ac_terms.push({ value: eff.value, requires_armor: eff.requires_armor === true, label });
106
- break;
107
- case "weapon-ability": {
108
- if (eff.ability !== "spellcasting") {
109
- const w = eff.weapons;
110
- const weaponSlugs = !w || w === "chosen" ? void 0 : Array.isArray(w) ? w.map(bareEntitySlug) : [bareEntitySlug(w)];
111
- out.weaponAbilities.push({ ability: eff.ability, weaponSlugs });
112
- }
113
- break;
114
- }
115
- case "roll-modifier":
116
- out.rollModifiers.push({ mode: eff.mode, roll: eff.roll, scope: eff.scope, condition: eff.condition, label });
117
- break;
118
- case "crit-range":
119
- if (((_a = eff.applies_to) != null ? _a : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
120
- break;
121
- case "extra-attack":
122
- out.extraAttack = Math.max(out.extraAttack, eff.count);
123
- break;
124
- case "reroll-damage":
125
- out.attackNotes.push(`Reroll ${eff.max_reroll}s${eff.once_per_die ? " (once/die)" : ""}`);
126
- break;
127
- case "attack-rule":
128
- if (eff.flag === "no-ranged-in-melee-disadvantage") out.attackNotes.push("No disadvantage firing in melee");
129
- break;
130
- case "damage-bonus":
131
- if (((_b = eff.applies_to) != null ? _b : "weapon") !== "spell") {
132
- out.damageBonuses.push({ amount: eff.amount, damage_type: eff.damage_type, source: label });
133
- }
134
- break;
135
- default:
136
- break;
137
- }
138
- }
139
-
140
- export {
141
- emptyFeatureEffectTotals,
142
- computeFeatureEffects
143
- };
@@ -1,68 +0,0 @@
1
- import {
2
- durationSchema
3
- } from "./chunk-WYGD2UGN.js";
4
- import {
5
- resourceConsumptionSchema,
6
- resourceSchema
7
- } from "./chunk-PZOWBI3B.js";
8
- import {
9
- choiceSchema
10
- } from "./chunk-NXSWH3AE.js";
11
- import {
12
- featureEffectSchema
13
- } from "./chunk-NXR3BD2V.js";
14
-
15
- // src/schemas/feature-schema.ts
16
- import { z as z2 } from "zod";
17
-
18
- // src/schemas/attack-schema.ts
19
- import { z } from "zod";
20
- var abilityEnum = z.enum(["str", "dex", "con", "int", "wis", "cha"]);
21
- var attackSchema = z.object({
22
- name: z.string().min(1),
23
- type: z.enum(["melee", "ranged", "spell"]),
24
- ability: z.union([abilityEnum, z.array(abilityEnum).nonempty()]).optional(),
25
- damage: z.string().optional(),
26
- damage_type: z.string().optional(),
27
- action: z.enum(["action", "bonus-action", "reaction"]).optional(),
28
- properties: z.array(z.string()).optional(),
29
- range: z.object({
30
- normal: z.number().int().nonnegative().optional(),
31
- long: z.number().int().nonnegative().optional(),
32
- reach: z.number().int().nonnegative().optional()
33
- }).optional(),
34
- condition: z.string().optional()
35
- });
36
-
37
- // src/schemas/feature-schema.ts
38
- var featureSchema = z2.lazy(
39
- () => z2.object({
40
- id: z2.string().min(1).optional(),
41
- name: z2.string().min(1),
42
- description: z2.string().optional(),
43
- entries: z2.array(z2.string()).optional(),
44
- choices: z2.array(choiceSchema).optional(),
45
- grants_resource: z2.string().optional(),
46
- consumes: resourceConsumptionSchema.optional(),
47
- attacks: z2.array(attackSchema).optional(),
48
- action: z2.enum(["action", "bonus-action", "reaction", "free", "special"]).optional(),
49
- trigger: z2.string().optional(),
50
- dc_formula: z2.string().optional(),
51
- effects: z2.array(featureEffectSchema).optional(),
52
- sub_features: z2.array(featureSchema).optional(),
53
- resources: z2.array(resourceSchema).optional(),
54
- // Phase 3 activatable buffs: a feature flagged `activatable` folds its effects
55
- // only while its id is present in state.active_buffs. `passive` renders a tag;
56
- // `duration` is a static label (no live countdown this phase).
57
- activatable: z2.boolean().optional(),
58
- passive: z2.boolean().optional(),
59
- duration: durationSchema.optional()
60
- }).refine(
61
- (f) => f.description !== void 0 || f.entries !== void 0 && f.entries.length > 0,
62
- { message: "feature requires either description or non-empty entries" }
63
- )
64
- );
65
-
66
- export {
67
- featureSchema
68
- };