@alpaca-software/40kdc-data 0.1.1 → 0.1.3

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 (424) hide show
  1. package/README.md +2 -0
  2. package/dist/abilities-resolver/index.d.ts +9 -0
  3. package/dist/abilities-resolver/index.d.ts.map +1 -0
  4. package/dist/abilities-resolver/index.js +9 -0
  5. package/dist/abilities-resolver/index.js.map +1 -0
  6. package/dist/abilities-resolver/resolver.d.ts +73 -0
  7. package/dist/abilities-resolver/resolver.d.ts.map +1 -0
  8. package/dist/abilities-resolver/resolver.js +142 -0
  9. package/dist/abilities-resolver/resolver.js.map +1 -0
  10. package/dist/audit-coverage.d.ts +78 -0
  11. package/dist/audit-coverage.d.ts.map +1 -0
  12. package/dist/audit-coverage.js +341 -0
  13. package/dist/audit-coverage.js.map +1 -0
  14. package/dist/author-batch.d.ts +147 -0
  15. package/dist/author-batch.d.ts.map +1 -0
  16. package/dist/author-batch.js +675 -0
  17. package/dist/author-batch.js.map +1 -0
  18. package/dist/author-input.d.ts +37 -0
  19. package/dist/author-input.d.ts.map +1 -0
  20. package/dist/author-input.js +162 -0
  21. package/dist/author-input.js.map +1 -0
  22. package/dist/bundle-schemas.d.ts +1 -0
  23. package/dist/bundle-schemas.d.ts.map +1 -0
  24. package/dist/bundle-schemas.js +1 -0
  25. package/dist/bundle-schemas.js.map +1 -0
  26. package/dist/cli.d.ts +2 -0
  27. package/dist/cli.d.ts.map +1 -0
  28. package/dist/cli.js +9 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/codegen-data.d.ts +1 -0
  31. package/dist/codegen-data.d.ts.map +1 -0
  32. package/dist/codegen-data.js +2 -0
  33. package/dist/codegen-data.js.map +1 -0
  34. package/dist/commands/import.d.ts +1 -0
  35. package/dist/commands/import.d.ts.map +1 -0
  36. package/dist/commands/import.js +1 -0
  37. package/dist/commands/import.js.map +1 -0
  38. package/dist/commands/translate.d.ts +1 -0
  39. package/dist/commands/translate.d.ts.map +1 -0
  40. package/dist/commands/translate.js +10 -4
  41. package/dist/commands/translate.js.map +1 -0
  42. package/dist/commands/validate-all.d.ts +1 -0
  43. package/dist/commands/validate-all.d.ts.map +1 -0
  44. package/dist/commands/validate-all.js +1 -0
  45. package/dist/commands/validate-all.js.map +1 -0
  46. package/dist/commands/validate-core.d.ts +1 -0
  47. package/dist/commands/validate-core.d.ts.map +1 -0
  48. package/dist/commands/validate-core.js +1 -0
  49. package/dist/commands/validate-core.js.map +1 -0
  50. package/dist/commands/validate-enrichment.d.ts +1 -0
  51. package/dist/commands/validate-enrichment.d.ts.map +1 -0
  52. package/dist/commands/validate-enrichment.js +1 -0
  53. package/dist/commands/validate-enrichment.js.map +1 -0
  54. package/dist/convert-faction.d.ts +1 -0
  55. package/dist/convert-faction.d.ts.map +1 -0
  56. package/dist/convert-faction.js +1 -0
  57. package/dist/convert-faction.js.map +1 -0
  58. package/dist/converters/configs/adepta-sororitas.d.ts +1 -0
  59. package/dist/converters/configs/adepta-sororitas.d.ts.map +1 -0
  60. package/dist/converters/configs/adepta-sororitas.js +1 -0
  61. package/dist/converters/configs/adepta-sororitas.js.map +1 -0
  62. package/dist/converters/configs/adeptus-astartes.d.ts +1 -0
  63. package/dist/converters/configs/adeptus-astartes.d.ts.map +1 -0
  64. package/dist/converters/configs/adeptus-astartes.js +1 -0
  65. package/dist/converters/configs/adeptus-astartes.js.map +1 -0
  66. package/dist/converters/configs/adeptus-custodes.d.ts +1 -0
  67. package/dist/converters/configs/adeptus-custodes.d.ts.map +1 -0
  68. package/dist/converters/configs/adeptus-custodes.js +1 -0
  69. package/dist/converters/configs/adeptus-custodes.js.map +1 -0
  70. package/dist/converters/configs/adeptus-mechanicus.d.ts +1 -0
  71. package/dist/converters/configs/adeptus-mechanicus.d.ts.map +1 -0
  72. package/dist/converters/configs/adeptus-mechanicus.js +1 -0
  73. package/dist/converters/configs/adeptus-mechanicus.js.map +1 -0
  74. package/dist/converters/configs/aeldari.d.ts +1 -0
  75. package/dist/converters/configs/aeldari.d.ts.map +1 -0
  76. package/dist/converters/configs/aeldari.js +1 -0
  77. package/dist/converters/configs/aeldari.js.map +1 -0
  78. package/dist/converters/configs/agents-of-the-imperium.d.ts +1 -0
  79. package/dist/converters/configs/agents-of-the-imperium.d.ts.map +1 -0
  80. package/dist/converters/configs/agents-of-the-imperium.js +1 -0
  81. package/dist/converters/configs/agents-of-the-imperium.js.map +1 -0
  82. package/dist/converters/configs/astra-militarum.d.ts +1 -0
  83. package/dist/converters/configs/astra-militarum.d.ts.map +1 -0
  84. package/dist/converters/configs/astra-militarum.js +1 -0
  85. package/dist/converters/configs/astra-militarum.js.map +1 -0
  86. package/dist/converters/configs/black-templars.d.ts +1 -0
  87. package/dist/converters/configs/black-templars.d.ts.map +1 -0
  88. package/dist/converters/configs/black-templars.js +1 -0
  89. package/dist/converters/configs/black-templars.js.map +1 -0
  90. package/dist/converters/configs/blood-angels.d.ts +1 -0
  91. package/dist/converters/configs/blood-angels.d.ts.map +1 -0
  92. package/dist/converters/configs/blood-angels.js +1 -0
  93. package/dist/converters/configs/blood-angels.js.map +1 -0
  94. package/dist/converters/configs/chaos-daemons.d.ts +1 -0
  95. package/dist/converters/configs/chaos-daemons.d.ts.map +1 -0
  96. package/dist/converters/configs/chaos-daemons.js +1 -0
  97. package/dist/converters/configs/chaos-daemons.js.map +1 -0
  98. package/dist/converters/configs/chaos-knights.d.ts +1 -0
  99. package/dist/converters/configs/chaos-knights.d.ts.map +1 -0
  100. package/dist/converters/configs/chaos-knights.js +1 -0
  101. package/dist/converters/configs/chaos-knights.js.map +1 -0
  102. package/dist/converters/configs/chaos-space-marines.d.ts +1 -0
  103. package/dist/converters/configs/chaos-space-marines.d.ts.map +1 -0
  104. package/dist/converters/configs/chaos-space-marines.js +1 -0
  105. package/dist/converters/configs/chaos-space-marines.js.map +1 -0
  106. package/dist/converters/configs/crimson-fists.d.ts +1 -0
  107. package/dist/converters/configs/crimson-fists.d.ts.map +1 -0
  108. package/dist/converters/configs/crimson-fists.js +1 -0
  109. package/dist/converters/configs/crimson-fists.js.map +1 -0
  110. package/dist/converters/configs/dark-angels.d.ts +1 -0
  111. package/dist/converters/configs/dark-angels.d.ts.map +1 -0
  112. package/dist/converters/configs/dark-angels.js +1 -0
  113. package/dist/converters/configs/dark-angels.js.map +1 -0
  114. package/dist/converters/configs/death-guard.d.ts +1 -0
  115. package/dist/converters/configs/death-guard.d.ts.map +1 -0
  116. package/dist/converters/configs/death-guard.js +1 -0
  117. package/dist/converters/configs/death-guard.js.map +1 -0
  118. package/dist/converters/configs/deathwatch.d.ts +1 -0
  119. package/dist/converters/configs/deathwatch.d.ts.map +1 -0
  120. package/dist/converters/configs/deathwatch.js +1 -0
  121. package/dist/converters/configs/deathwatch.js.map +1 -0
  122. package/dist/converters/configs/drukhari.d.ts +1 -0
  123. package/dist/converters/configs/drukhari.d.ts.map +1 -0
  124. package/dist/converters/configs/drukhari.js +1 -0
  125. package/dist/converters/configs/drukhari.js.map +1 -0
  126. package/dist/converters/configs/emperors-children.d.ts +1 -0
  127. package/dist/converters/configs/emperors-children.d.ts.map +1 -0
  128. package/dist/converters/configs/emperors-children.js +1 -0
  129. package/dist/converters/configs/emperors-children.js.map +1 -0
  130. package/dist/converters/configs/genestealer-cults.d.ts +1 -0
  131. package/dist/converters/configs/genestealer-cults.d.ts.map +1 -0
  132. package/dist/converters/configs/genestealer-cults.js +1 -0
  133. package/dist/converters/configs/genestealer-cults.js.map +1 -0
  134. package/dist/converters/configs/grey-knights.d.ts +1 -0
  135. package/dist/converters/configs/grey-knights.d.ts.map +1 -0
  136. package/dist/converters/configs/grey-knights.js +1 -0
  137. package/dist/converters/configs/grey-knights.js.map +1 -0
  138. package/dist/converters/configs/imperial-fists.d.ts +1 -0
  139. package/dist/converters/configs/imperial-fists.d.ts.map +1 -0
  140. package/dist/converters/configs/imperial-fists.js +1 -0
  141. package/dist/converters/configs/imperial-fists.js.map +1 -0
  142. package/dist/converters/configs/imperial-knights.d.ts +1 -0
  143. package/dist/converters/configs/imperial-knights.d.ts.map +1 -0
  144. package/dist/converters/configs/imperial-knights.js +1 -0
  145. package/dist/converters/configs/imperial-knights.js.map +1 -0
  146. package/dist/converters/configs/iron-hands.d.ts +1 -0
  147. package/dist/converters/configs/iron-hands.d.ts.map +1 -0
  148. package/dist/converters/configs/iron-hands.js +1 -0
  149. package/dist/converters/configs/iron-hands.js.map +1 -0
  150. package/dist/converters/configs/leagues-of-votann.d.ts +1 -0
  151. package/dist/converters/configs/leagues-of-votann.d.ts.map +1 -0
  152. package/dist/converters/configs/leagues-of-votann.js +1 -0
  153. package/dist/converters/configs/leagues-of-votann.js.map +1 -0
  154. package/dist/converters/configs/necrons.d.ts +1 -0
  155. package/dist/converters/configs/necrons.d.ts.map +1 -0
  156. package/dist/converters/configs/necrons.js +1 -0
  157. package/dist/converters/configs/necrons.js.map +1 -0
  158. package/dist/converters/configs/orks.d.ts +1 -0
  159. package/dist/converters/configs/orks.d.ts.map +1 -0
  160. package/dist/converters/configs/orks.js +1 -0
  161. package/dist/converters/configs/orks.js.map +1 -0
  162. package/dist/converters/configs/raven-guard.d.ts +1 -0
  163. package/dist/converters/configs/raven-guard.d.ts.map +1 -0
  164. package/dist/converters/configs/raven-guard.js +1 -0
  165. package/dist/converters/configs/raven-guard.js.map +1 -0
  166. package/dist/converters/configs/salamanders.d.ts +1 -0
  167. package/dist/converters/configs/salamanders.d.ts.map +1 -0
  168. package/dist/converters/configs/salamanders.js +1 -0
  169. package/dist/converters/configs/salamanders.js.map +1 -0
  170. package/dist/converters/configs/space-wolves.d.ts +1 -0
  171. package/dist/converters/configs/space-wolves.d.ts.map +1 -0
  172. package/dist/converters/configs/space-wolves.js +1 -0
  173. package/dist/converters/configs/space-wolves.js.map +1 -0
  174. package/dist/converters/configs/tau-empire.d.ts +1 -0
  175. package/dist/converters/configs/tau-empire.d.ts.map +1 -0
  176. package/dist/converters/configs/tau-empire.js +1 -0
  177. package/dist/converters/configs/tau-empire.js.map +1 -0
  178. package/dist/converters/configs/thousand-sons.d.ts +1 -0
  179. package/dist/converters/configs/thousand-sons.d.ts.map +1 -0
  180. package/dist/converters/configs/thousand-sons.js +1 -0
  181. package/dist/converters/configs/thousand-sons.js.map +1 -0
  182. package/dist/converters/configs/tyranids.d.ts +1 -0
  183. package/dist/converters/configs/tyranids.d.ts.map +1 -0
  184. package/dist/converters/configs/tyranids.js +1 -0
  185. package/dist/converters/configs/tyranids.js.map +1 -0
  186. package/dist/converters/configs/ultramarines.d.ts +1 -0
  187. package/dist/converters/configs/ultramarines.d.ts.map +1 -0
  188. package/dist/converters/configs/ultramarines.js +1 -0
  189. package/dist/converters/configs/ultramarines.js.map +1 -0
  190. package/dist/converters/configs/white-scars.d.ts +1 -0
  191. package/dist/converters/configs/white-scars.d.ts.map +1 -0
  192. package/dist/converters/configs/white-scars.js +1 -0
  193. package/dist/converters/configs/white-scars.js.map +1 -0
  194. package/dist/converters/configs/world-eaters.d.ts +1 -0
  195. package/dist/converters/configs/world-eaters.d.ts.map +1 -0
  196. package/dist/converters/configs/world-eaters.js +1 -0
  197. package/dist/converters/configs/world-eaters.js.map +1 -0
  198. package/dist/converters/faction-config.d.ts +1 -0
  199. package/dist/converters/faction-config.d.ts.map +1 -0
  200. package/dist/converters/faction-config.js +1 -0
  201. package/dist/converters/faction-config.js.map +1 -0
  202. package/dist/converters/id-generator.d.ts +1 -0
  203. package/dist/converters/id-generator.d.ts.map +1 -0
  204. package/dist/converters/id-generator.js +1 -0
  205. package/dist/converters/id-generator.js.map +1 -0
  206. package/dist/converters/keyword-filter.d.ts +1 -0
  207. package/dist/converters/keyword-filter.d.ts.map +1 -0
  208. package/dist/converters/keyword-filter.js +1 -0
  209. package/dist/converters/keyword-filter.js.map +1 -0
  210. package/dist/converters/stat-parser.d.ts +1 -0
  211. package/dist/converters/stat-parser.d.ts.map +1 -0
  212. package/dist/converters/stat-parser.js +1 -0
  213. package/dist/converters/stat-parser.js.map +1 -0
  214. package/dist/converters/view-selector.d.ts +1 -0
  215. package/dist/converters/view-selector.d.ts.map +1 -0
  216. package/dist/converters/view-selector.js +1 -0
  217. package/dist/converters/view-selector.js.map +1 -0
  218. package/dist/converters/weapon-dedup.d.ts +1 -0
  219. package/dist/converters/weapon-dedup.d.ts.map +1 -0
  220. package/dist/converters/weapon-dedup.js +1 -0
  221. package/dist/converters/weapon-dedup.js.map +1 -0
  222. package/dist/cruncher/attribution.d.ts +66 -0
  223. package/dist/cruncher/attribution.d.ts.map +1 -0
  224. package/dist/cruncher/attribution.js +88 -0
  225. package/dist/cruncher/attribution.js.map +1 -0
  226. package/dist/cruncher/buffs.d.ts +206 -0
  227. package/dist/cruncher/buffs.d.ts.map +1 -0
  228. package/dist/cruncher/buffs.js +150 -0
  229. package/dist/cruncher/buffs.js.map +1 -0
  230. package/dist/cruncher/engine.d.ts +50 -0
  231. package/dist/cruncher/engine.d.ts.map +1 -0
  232. package/dist/cruncher/engine.js +312 -0
  233. package/dist/cruncher/engine.js.map +1 -0
  234. package/dist/cruncher/from-dsl.d.ts +101 -0
  235. package/dist/cruncher/from-dsl.d.ts.map +1 -0
  236. package/dist/cruncher/from-dsl.js +968 -0
  237. package/dist/cruncher/from-dsl.js.map +1 -0
  238. package/dist/cruncher/from-keyword.d.ts +35 -0
  239. package/dist/cruncher/from-keyword.d.ts.map +1 -0
  240. package/dist/cruncher/from-keyword.js +159 -0
  241. package/dist/cruncher/from-keyword.js.map +1 -0
  242. package/dist/cruncher/get-buffs.d.ts +12 -0
  243. package/dist/cruncher/get-buffs.d.ts.map +1 -0
  244. package/dist/cruncher/get-buffs.js +7 -0
  245. package/dist/cruncher/get-buffs.js.map +1 -0
  246. package/dist/cruncher/index.d.ts +12 -0
  247. package/dist/cruncher/index.d.ts.map +1 -0
  248. package/dist/cruncher/index.js +12 -0
  249. package/dist/cruncher/index.js.map +1 -0
  250. package/dist/data/bundle.generated.d.ts +1 -0
  251. package/dist/data/bundle.generated.d.ts.map +1 -0
  252. package/dist/data/bundle.generated.js +2 -1
  253. package/dist/data/bundle.generated.js.map +1 -0
  254. package/dist/data/collection.d.ts +10 -0
  255. package/dist/data/collection.d.ts.map +1 -0
  256. package/dist/data/collection.js +15 -0
  257. package/dist/data/collection.js.map +1 -0
  258. package/dist/data/dataset.d.ts +132 -2
  259. package/dist/data/dataset.d.ts.map +1 -0
  260. package/dist/data/dataset.js +248 -1
  261. package/dist/data/dataset.js.map +1 -0
  262. package/dist/data/entities.d.ts +67 -2
  263. package/dist/data/entities.d.ts.map +1 -0
  264. package/dist/data/entities.js +122 -0
  265. package/dist/data/entities.js.map +1 -0
  266. package/dist/data/index.d.ts +10 -1
  267. package/dist/data/index.d.ts.map +1 -0
  268. package/dist/data/index.js +14 -1
  269. package/dist/data/index.js.map +1 -0
  270. package/dist/data/normalize.d.ts +1 -0
  271. package/dist/data/normalize.d.ts.map +1 -0
  272. package/dist/data/normalize.js +1 -0
  273. package/dist/data/normalize.js.map +1 -0
  274. package/dist/data/roster-resolve.d.ts +58 -0
  275. package/dist/data/roster-resolve.d.ts.map +1 -0
  276. package/dist/data/roster-resolve.js +82 -0
  277. package/dist/data/roster-resolve.js.map +1 -0
  278. package/dist/data/types.d.ts +4 -1
  279. package/dist/data/types.d.ts.map +1 -0
  280. package/dist/data/types.js +2 -0
  281. package/dist/data/types.js.map +1 -0
  282. package/dist/export/helpers.d.ts +33 -0
  283. package/dist/export/helpers.d.ts.map +1 -0
  284. package/dist/export/helpers.js +57 -0
  285. package/dist/export/helpers.js.map +1 -0
  286. package/dist/export/index.d.ts +22 -0
  287. package/dist/export/index.d.ts.map +1 -0
  288. package/dist/export/index.js +28 -0
  289. package/dist/export/index.js.map +1 -0
  290. package/dist/export/newrecruit-json.d.ts +3 -0
  291. package/dist/export/newrecruit-json.d.ts.map +1 -0
  292. package/dist/export/newrecruit-json.js +140 -0
  293. package/dist/export/newrecruit-json.js.map +1 -0
  294. package/dist/export/newrecruit-simple.d.ts +3 -0
  295. package/dist/export/newrecruit-simple.d.ts.map +1 -0
  296. package/dist/export/newrecruit-simple.js +76 -0
  297. package/dist/export/newrecruit-simple.js.map +1 -0
  298. package/dist/export/newrecruit-wtc.d.ts +4 -0
  299. package/dist/export/newrecruit-wtc.d.ts.map +1 -0
  300. package/dist/export/newrecruit-wtc.js +142 -0
  301. package/dist/export/newrecruit-wtc.js.map +1 -0
  302. package/dist/export/roster-json.d.ts +3 -0
  303. package/dist/export/roster-json.d.ts.map +1 -0
  304. package/dist/export/roster-json.js +8 -0
  305. package/dist/export/roster-json.js.map +1 -0
  306. package/dist/export/rosterizer.d.ts +3 -0
  307. package/dist/export/rosterizer.d.ts.map +1 -0
  308. package/dist/export/rosterizer.js +144 -0
  309. package/dist/export/rosterizer.js.map +1 -0
  310. package/dist/export/serializer.d.ts +27 -0
  311. package/dist/export/serializer.d.ts.map +1 -0
  312. package/dist/export/serializer.js +2 -0
  313. package/dist/export/serializer.js.map +1 -0
  314. package/dist/gen-conformance.d.ts +1 -0
  315. package/dist/gen-conformance.d.ts.map +1 -0
  316. package/dist/gen-conformance.js +274 -12
  317. package/dist/gen-conformance.js.map +1 -0
  318. package/dist/generated.d.ts +194 -118
  319. package/dist/generated.d.ts.map +1 -0
  320. package/dist/generated.js +1 -0
  321. package/dist/generated.js.map +1 -0
  322. package/dist/import/adapter.d.ts +4 -3
  323. package/dist/import/adapter.d.ts.map +1 -0
  324. package/dist/import/adapter.js +1 -0
  325. package/dist/import/adapter.js.map +1 -0
  326. package/dist/import/decode.d.ts +1 -0
  327. package/dist/import/decode.d.ts.map +1 -0
  328. package/dist/import/decode.js +1 -0
  329. package/dist/import/decode.js.map +1 -0
  330. package/dist/import/gw.d.ts +69 -0
  331. package/dist/import/gw.d.ts.map +1 -0
  332. package/dist/import/gw.js +245 -0
  333. package/dist/import/gw.js.map +1 -0
  334. package/dist/import/import-roster.d.ts +84 -0
  335. package/dist/import/import-roster.d.ts.map +1 -0
  336. package/dist/import/import-roster.js +207 -0
  337. package/dist/import/import-roster.js.map +1 -0
  338. package/dist/import/index.d.ts +7 -3
  339. package/dist/import/index.d.ts.map +1 -0
  340. package/dist/import/index.js +5 -1
  341. package/dist/import/index.js.map +1 -0
  342. package/dist/import/listforge.d.ts +1 -0
  343. package/dist/import/listforge.d.ts.map +1 -0
  344. package/dist/import/listforge.js +22 -2
  345. package/dist/import/listforge.js.map +1 -0
  346. package/dist/import/newrecruit-json.d.ts +31 -0
  347. package/dist/import/newrecruit-json.d.ts.map +1 -0
  348. package/dist/import/newrecruit-json.js +224 -0
  349. package/dist/import/newrecruit-json.js.map +1 -0
  350. package/dist/import/newrecruit-simple.d.ts +29 -0
  351. package/dist/import/newrecruit-simple.d.ts.map +1 -0
  352. package/dist/import/newrecruit-simple.js +200 -0
  353. package/dist/import/newrecruit-simple.js.map +1 -0
  354. package/dist/import/newrecruit-text.d.ts +51 -0
  355. package/dist/import/newrecruit-text.d.ts.map +1 -0
  356. package/dist/import/newrecruit-text.js +102 -0
  357. package/dist/import/newrecruit-text.js.map +1 -0
  358. package/dist/import/newrecruit-wtc.d.ts +36 -0
  359. package/dist/import/newrecruit-wtc.d.ts.map +1 -0
  360. package/dist/import/newrecruit-wtc.js +337 -0
  361. package/dist/import/newrecruit-wtc.js.map +1 -0
  362. package/dist/import/resolve.d.ts +3 -2
  363. package/dist/import/resolve.d.ts.map +1 -0
  364. package/dist/import/resolve.js +5 -2
  365. package/dist/import/resolve.js.map +1 -0
  366. package/dist/import/rosterizer.d.ts +70 -0
  367. package/dist/import/rosterizer.d.ts.map +1 -0
  368. package/dist/import/rosterizer.js +348 -0
  369. package/dist/import/rosterizer.js.map +1 -0
  370. package/dist/import/types.d.ts +11 -1
  371. package/dist/import/types.d.ts.map +1 -0
  372. package/dist/import/types.js +1 -0
  373. package/dist/import/types.js.map +1 -0
  374. package/dist/index.d.ts +5 -2
  375. package/dist/index.d.ts.map +1 -0
  376. package/dist/index.js +4 -1
  377. package/dist/index.js.map +1 -0
  378. package/dist/known-support-10e.d.ts +1 -0
  379. package/dist/known-support-10e.d.ts.map +1 -0
  380. package/dist/known-support-10e.js +1 -0
  381. package/dist/known-support-10e.js.map +1 -0
  382. package/dist/link-abilities.d.ts +41 -0
  383. package/dist/link-abilities.d.ts.map +1 -0
  384. package/dist/link-abilities.js +159 -0
  385. package/dist/link-abilities.js.map +1 -0
  386. package/dist/migrations/2026-weapon-keywords.d.ts +2 -0
  387. package/dist/migrations/2026-weapon-keywords.d.ts.map +1 -0
  388. package/dist/migrations/2026-weapon-keywords.js +247 -0
  389. package/dist/migrations/2026-weapon-keywords.js.map +1 -0
  390. package/dist/port-10e-faction.d.ts +1 -0
  391. package/dist/port-10e-faction.d.ts.map +1 -0
  392. package/dist/port-10e-faction.js +1 -0
  393. package/dist/port-10e-faction.js.map +1 -0
  394. package/dist/report.d.ts +1 -0
  395. package/dist/report.d.ts.map +1 -0
  396. package/dist/report.js +1 -0
  397. package/dist/report.js.map +1 -0
  398. package/dist/rube-goldberg.d.ts +3 -0
  399. package/dist/rube-goldberg.d.ts.map +1 -0
  400. package/dist/rube-goldberg.js +109 -0
  401. package/dist/rube-goldberg.js.map +1 -0
  402. package/dist/runner.d.ts +38 -0
  403. package/dist/runner.d.ts.map +1 -0
  404. package/dist/runner.js +492 -0
  405. package/dist/runner.js.map +1 -0
  406. package/dist/schema-loader.d.ts +1 -0
  407. package/dist/schema-loader.d.ts.map +1 -0
  408. package/dist/schema-loader.js +1 -0
  409. package/dist/schema-loader.js.map +1 -0
  410. package/dist/scrub-ip.d.ts +14 -0
  411. package/dist/scrub-ip.d.ts.map +1 -0
  412. package/dist/scrub-ip.js +88 -0
  413. package/dist/scrub-ip.js.map +1 -0
  414. package/dist/validate.d.ts +1 -0
  415. package/dist/validate.d.ts.map +1 -0
  416. package/dist/validate.js +2 -0
  417. package/dist/validate.js.map +1 -0
  418. package/package.json +15 -3
  419. package/schemas/core/roster.schema.json +19 -4
  420. package/schemas/core/weapon-keyword.schema.json +31 -0
  421. package/schemas/core/weapon.schema.json +22 -1
  422. package/schemas/enrichment/ability-dsl/effect.schema.json +23 -1
  423. package/dist/import/import-listforge.d.ts +0 -23
  424. package/dist/import/import-listforge.js +0 -32
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @alpaca-software/40kdc-data
2
2
 
3
+ Published by [Alpaca Software](https://alpacasoft.dev).
4
+
3
5
  The [40kdc](https://tabletop-developer-consortium.github.io) Warhammer 40,000
4
6
  dataset behind a **linked, typed API**. Find a unit, then walk straight to its
5
7
  weapons, abilities, the game phases those abilities act in, and its faction —
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The eligible-abilities resolver: given a unit (plus optional detachment,
3
+ * leader, supporting units), enumerate every ability that could apply to it
4
+ * in a chosen phase, tagged by source so the SPA can group them in the UI.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ export { resolveEligibleAbilities, type EligibilityInput, type EligibleAbility, type EligibleAbilitySource, } from "./resolver.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/abilities-resolver/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The eligible-abilities resolver: given a unit (plus optional detachment,
3
+ * leader, supporting units), enumerate every ability that could apply to it
4
+ * in a chosen phase, tagged by source so the SPA can group them in the UI.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ export { resolveEligibleAbilities, } from "./resolver.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/abilities-resolver/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,wBAAwB,GAIzB,MAAM,eAAe,CAAC","sourcesContent":["/**\n * The eligible-abilities resolver: given a unit (plus optional detachment,\n * leader, supporting units), enumerate every ability that could apply to it\n * in a chosen phase, tagged by source so the SPA can group them in the UI.\n *\n * @packageDocumentation\n */\nexport {\n resolveEligibleAbilities,\n type EligibilityInput,\n type EligibleAbility,\n type EligibleAbilitySource,\n} from \"./resolver.js\";\n"]}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Walks the dataset for every ability that could apply to a chosen unit in a
3
+ * chosen phase. The SPA passes the result through to the buff layer (each
4
+ * {@link EligibleAbility} carries `.getBuffs()`, the source is pre-tagged).
5
+ *
6
+ * Resolution order — stable for snapshot tests:
7
+ *
8
+ * 1. **army** — faction-scoped abilities whose `ability_type` is `"faction"`
9
+ * and whose `faction_id` matches the input.
10
+ * 2. **detachment** — abilities authored against the detachment
11
+ * (`ability_type` is `"detachment"`, `detachment_id` matches).
12
+ * 3. **detachment-stratagem** — stratagems on the detachment, each yielding
13
+ * the ability referenced by `stratagem.ability_id` (if any).
14
+ * 4. **unit** — abilities listed in `unit.ability_ids`.
15
+ * 5. **attached** — abilities of each attached member. A leader + bodyguard
16
+ * (and, in 11th, support) form one combined unit, so every member's
17
+ * abilities apply to the whole unit — pulled in full (not aura-filtered),
18
+ * whichever member is the selected/firing unit.
19
+ * 6. **support** — abilities on supporting units whose scope range is an
20
+ * aura (not `self` / `unit`).
21
+ *
22
+ * Each step phase-filters via the existing `Dataset.phasesFor` index. The
23
+ * resolver collects abilities first, *then* filters by phase, so the SPA can
24
+ * also ask "what abilities are eligible across all phases?" by passing every
25
+ * phase (today the API requires a single phase; if the SPA wants the wide
26
+ * view it can call the resolver once per phase).
27
+ */
28
+ import type { Phase } from "../generated.js";
29
+ import type { Dataset } from "../data/dataset.js";
30
+ import type { AbilityView } from "../data/entities.js";
31
+ export type EligibleAbilitySource = {
32
+ kind: "army";
33
+ } | {
34
+ kind: "detachment";
35
+ detachmentId: string;
36
+ } | {
37
+ kind: "detachment-stratagem";
38
+ stratagemId: string;
39
+ cpCost: number;
40
+ } | {
41
+ kind: "unit";
42
+ unitId: string;
43
+ } | {
44
+ kind: "attached";
45
+ unitId: string;
46
+ } | {
47
+ kind: "support";
48
+ sourceUnitId: string;
49
+ };
50
+ export type EligibilityInput = {
51
+ unitId: string;
52
+ /** Overrides the unit's own `faction_id` when given (for inheritance cases). */
53
+ factionId?: string;
54
+ detachmentId?: string;
55
+ /**
56
+ * Other members of the combined ("attached") unit — the attached leader, its
57
+ * bodyguard, or (11th) support attachments — whichever is *not* the selected
58
+ * `unitId`. Their abilities are pooled onto the combined unit. A list so
59
+ * multi-member attachments need no shape change; order is preserved.
60
+ */
61
+ attachedUnitIds?: string[];
62
+ /** Friendly units whose auras could apply (M2 walks only their aura-ranged abilities). */
63
+ supportingUnitIds?: string[];
64
+ };
65
+ export type EligibleAbility = {
66
+ ability: AbilityView;
67
+ source: EligibleAbilitySource;
68
+ /** The subset of `ability.phases` that intersect the requested phase. */
69
+ phases: Phase[];
70
+ };
71
+ /** Compute the sorted-by-source eligible-ability list for one (unit, phase). */
72
+ export declare function resolveEligibleAbilities(dataset: Dataset, input: EligibilityInput, phase: Phase): EligibleAbility[];
73
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/abilities-resolver/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,0FAA0F;IAC1F,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,yEAAyE;IACzE,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,KAAK,GACX,eAAe,EAAE,CAsGnB"}
@@ -0,0 +1,142 @@
1
+ /** Compute the sorted-by-source eligible-ability list for one (unit, phase). */
2
+ export function resolveEligibleAbilities(dataset, input, phase) {
3
+ // Resolve units within the faction when one is known. Unit ids are shared
4
+ // across factions (a shared chassis like `chaos-land-raider` lives under
5
+ // several Chaos factions), so a faction-blind `get` can return the wrong
6
+ // faction's copy — and with it the wrong intrinsic abilities/keywords.
7
+ const resolveUnit = (id, fid) => (fid ? dataset.units.getInFaction(id, fid) : undefined) ?? dataset.units.get(id);
8
+ const unit = resolveUnit(input.unitId, input.factionId);
9
+ if (!unit)
10
+ return [];
11
+ const factionId = input.factionId ?? unit.raw.faction_id;
12
+ const seen = new Set();
13
+ const out = [];
14
+ // 1. Army — faction-scoped abilities (faction rule + any other faction-typed).
15
+ for (const ability of dataset.abilities.byFaction(factionId)) {
16
+ if (ability.raw.ability_type !== "faction")
17
+ continue;
18
+ if (!phaseMatches(ability, phase))
19
+ continue;
20
+ pushUnique(out, seen, { ability, source: { kind: "army" }, phases: intersect(ability.phases, phase) });
21
+ }
22
+ // 2. Detachment abilities — abilities whose detachment_id matches.
23
+ if (input.detachmentId) {
24
+ for (const ability of dataset.abilities) {
25
+ if (ability.raw.ability_type !== "detachment")
26
+ continue;
27
+ if (ability.raw.detachment_id !== input.detachmentId)
28
+ continue;
29
+ if (!phaseMatches(ability, phase))
30
+ continue;
31
+ pushUnique(out, seen, {
32
+ ability,
33
+ source: { kind: "detachment", detachmentId: input.detachmentId },
34
+ phases: intersect(ability.phases, phase),
35
+ });
36
+ }
37
+ // 3. Detachment stratagems.
38
+ const detachment = dataset.detachments.get(input.detachmentId);
39
+ if (detachment) {
40
+ for (const stratId of detachment.stratagem_ids ?? []) {
41
+ const stratagem = dataset.stratagems.get(stratId);
42
+ if (!stratagem)
43
+ continue;
44
+ if (!stratagemPhaseMatches(stratagem, phase))
45
+ continue;
46
+ const ability = stratagem.ability_id !== null && stratagem.ability_id !== undefined
47
+ ? dataset.abilities.get(stratagem.ability_id)
48
+ : undefined;
49
+ if (!ability)
50
+ continue;
51
+ pushUnique(out, seen, {
52
+ ability,
53
+ source: {
54
+ kind: "detachment-stratagem",
55
+ stratagemId: stratagem.id,
56
+ cpCost: stratagem.cp_cost,
57
+ },
58
+ phases: [phase], // the stratagem's printed phase governs eligibility.
59
+ });
60
+ }
61
+ }
62
+ }
63
+ // 4. Unit's own abilities.
64
+ for (const ability of unit.abilities) {
65
+ if (!phaseMatches(ability, phase))
66
+ continue;
67
+ pushUnique(out, seen, {
68
+ ability,
69
+ source: { kind: "unit", unitId: input.unitId },
70
+ phases: intersect(ability.phases, phase),
71
+ });
72
+ }
73
+ // 5. Attached members — the combined unit pools every member's abilities,
74
+ // pulled in full (not aura-filtered like step 6), regardless of which member
75
+ // is the selected/firing unit.
76
+ for (const memberId of input.attachedUnitIds ?? []) {
77
+ const member = resolveUnit(memberId, factionId);
78
+ if (!member)
79
+ continue;
80
+ for (const ability of member.abilities) {
81
+ if (!phaseMatches(ability, phase))
82
+ continue;
83
+ pushUnique(out, seen, {
84
+ ability,
85
+ source: { kind: "attached", unitId: memberId },
86
+ phases: intersect(ability.phases, phase),
87
+ });
88
+ }
89
+ }
90
+ // 6. Supporting units — only aura-scoped abilities (otherwise the buff
91
+ // would describe a self-target effect that doesn't reach the input unit).
92
+ for (const supportId of input.supportingUnitIds ?? []) {
93
+ const supporter = resolveUnit(supportId, factionId);
94
+ if (!supporter)
95
+ continue;
96
+ for (const ability of supporter.abilities) {
97
+ if (!phaseMatches(ability, phase))
98
+ continue;
99
+ if (!isAuraScope(ability.raw.scope?.range))
100
+ continue;
101
+ pushUnique(out, seen, {
102
+ ability,
103
+ source: { kind: "support", sourceUnitId: supportId },
104
+ phases: intersect(ability.phases, phase),
105
+ });
106
+ }
107
+ }
108
+ return out;
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Helpers
112
+ // ---------------------------------------------------------------------------
113
+ function phaseMatches(ability, phase) {
114
+ const phases = ability.phases;
115
+ // An ability with no phase-mapping is permissive — surface it everywhere so
116
+ // the SPA can decide. M2's translator already gates conditional-on-phase
117
+ // effects internally, so this stays generous on purpose.
118
+ if (phases.length === 0)
119
+ return true;
120
+ return phases.includes(phase);
121
+ }
122
+ function stratagemPhaseMatches(stratagem, phase) {
123
+ if (!stratagem.phases || stratagem.phases.length === 0)
124
+ return false;
125
+ return stratagem.phases.includes(phase);
126
+ }
127
+ function intersect(phases, phase) {
128
+ return phases.includes(phase) ? [phase] : phases;
129
+ }
130
+ function isAuraScope(range) {
131
+ if (typeof range !== "string")
132
+ return false;
133
+ return range.startsWith("aura-") || range === "any-on-battlefield" || range === "any-visible";
134
+ }
135
+ function pushUnique(out, seen, entry) {
136
+ const key = `${entry.source.kind}::${entry.ability.id}`;
137
+ if (seen.has(key))
138
+ return;
139
+ seen.add(key);
140
+ out.push(entry);
141
+ }
142
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/abilities-resolver/resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Walks the dataset for every ability that could apply to a chosen unit in a\n * chosen phase. The SPA passes the result through to the buff layer (each\n * {@link EligibleAbility} carries `.getBuffs()`, the source is pre-tagged).\n *\n * Resolution order — stable for snapshot tests:\n *\n * 1. **army** — faction-scoped abilities whose `ability_type` is `\"faction\"`\n * and whose `faction_id` matches the input.\n * 2. **detachment** — abilities authored against the detachment\n * (`ability_type` is `\"detachment\"`, `detachment_id` matches).\n * 3. **detachment-stratagem** — stratagems on the detachment, each yielding\n * the ability referenced by `stratagem.ability_id` (if any).\n * 4. **unit** — abilities listed in `unit.ability_ids`.\n * 5. **attached** — abilities of each attached member. A leader + bodyguard\n * (and, in 11th, support) form one combined unit, so every member's\n * abilities apply to the whole unit — pulled in full (not aura-filtered),\n * whichever member is the selected/firing unit.\n * 6. **support** — abilities on supporting units whose scope range is an\n * aura (not `self` / `unit`).\n *\n * Each step phase-filters via the existing `Dataset.phasesFor` index. The\n * resolver collects abilities first, *then* filters by phase, so the SPA can\n * also ask \"what abilities are eligible across all phases?\" by passing every\n * phase (today the API requires a single phase; if the SPA wants the wide\n * view it can call the resolver once per phase).\n */\nimport type { Phase, Stratagem } from \"../generated.js\";\nimport type { Dataset } from \"../data/dataset.js\";\nimport type { AbilityView } from \"../data/entities.js\";\n\nexport type EligibleAbilitySource =\n | { kind: \"army\" }\n | { kind: \"detachment\"; detachmentId: string }\n | { kind: \"detachment-stratagem\"; stratagemId: string; cpCost: number }\n | { kind: \"unit\"; unitId: string }\n | { kind: \"attached\"; unitId: string }\n | { kind: \"support\"; sourceUnitId: string };\n\nexport type EligibilityInput = {\n unitId: string;\n /** Overrides the unit's own `faction_id` when given (for inheritance cases). */\n factionId?: string;\n detachmentId?: string;\n /**\n * Other members of the combined (\"attached\") unit — the attached leader, its\n * bodyguard, or (11th) support attachments — whichever is *not* the selected\n * `unitId`. Their abilities are pooled onto the combined unit. A list so\n * multi-member attachments need no shape change; order is preserved.\n */\n attachedUnitIds?: string[];\n /** Friendly units whose auras could apply (M2 walks only their aura-ranged abilities). */\n supportingUnitIds?: string[];\n};\n\nexport type EligibleAbility = {\n ability: AbilityView;\n source: EligibleAbilitySource;\n /** The subset of `ability.phases` that intersect the requested phase. */\n phases: Phase[];\n};\n\n/** Compute the sorted-by-source eligible-ability list for one (unit, phase). */\nexport function resolveEligibleAbilities(\n dataset: Dataset,\n input: EligibilityInput,\n phase: Phase,\n): EligibleAbility[] {\n // Resolve units within the faction when one is known. Unit ids are shared\n // across factions (a shared chassis like `chaos-land-raider` lives under\n // several Chaos factions), so a faction-blind `get` can return the wrong\n // faction's copy — and with it the wrong intrinsic abilities/keywords.\n const resolveUnit = (id: string, fid: string | undefined) =>\n (fid ? dataset.units.getInFaction(id, fid) : undefined) ?? dataset.units.get(id);\n\n const unit = resolveUnit(input.unitId, input.factionId);\n if (!unit) return [];\n const factionId = input.factionId ?? unit.raw.faction_id;\n const seen = new Set<string>();\n const out: EligibleAbility[] = [];\n\n // 1. Army — faction-scoped abilities (faction rule + any other faction-typed).\n for (const ability of dataset.abilities.byFaction(factionId)) {\n if (ability.raw.ability_type !== \"faction\") continue;\n if (!phaseMatches(ability, phase)) continue;\n pushUnique(out, seen, { ability, source: { kind: \"army\" }, phases: intersect(ability.phases, phase) });\n }\n\n // 2. Detachment abilities — abilities whose detachment_id matches.\n if (input.detachmentId) {\n for (const ability of dataset.abilities) {\n if (ability.raw.ability_type !== \"detachment\") continue;\n if (ability.raw.detachment_id !== input.detachmentId) continue;\n if (!phaseMatches(ability, phase)) continue;\n pushUnique(out, seen, {\n ability,\n source: { kind: \"detachment\", detachmentId: input.detachmentId },\n phases: intersect(ability.phases, phase),\n });\n }\n\n // 3. Detachment stratagems.\n const detachment = dataset.detachments.get(input.detachmentId);\n if (detachment) {\n for (const stratId of detachment.stratagem_ids ?? []) {\n const stratagem = dataset.stratagems.get(stratId);\n if (!stratagem) continue;\n if (!stratagemPhaseMatches(stratagem, phase)) continue;\n const ability =\n stratagem.ability_id !== null && stratagem.ability_id !== undefined\n ? dataset.abilities.get(stratagem.ability_id)\n : undefined;\n if (!ability) continue;\n pushUnique(out, seen, {\n ability,\n source: {\n kind: \"detachment-stratagem\",\n stratagemId: stratagem.id,\n cpCost: stratagem.cp_cost,\n },\n phases: [phase], // the stratagem's printed phase governs eligibility.\n });\n }\n }\n }\n\n // 4. Unit's own abilities.\n for (const ability of unit.abilities) {\n if (!phaseMatches(ability, phase)) continue;\n pushUnique(out, seen, {\n ability,\n source: { kind: \"unit\", unitId: input.unitId },\n phases: intersect(ability.phases, phase),\n });\n }\n\n // 5. Attached members — the combined unit pools every member's abilities,\n // pulled in full (not aura-filtered like step 6), regardless of which member\n // is the selected/firing unit.\n for (const memberId of input.attachedUnitIds ?? []) {\n const member = resolveUnit(memberId, factionId);\n if (!member) continue;\n for (const ability of member.abilities) {\n if (!phaseMatches(ability, phase)) continue;\n pushUnique(out, seen, {\n ability,\n source: { kind: \"attached\", unitId: memberId },\n phases: intersect(ability.phases, phase),\n });\n }\n }\n\n // 6. Supporting units — only aura-scoped abilities (otherwise the buff\n // would describe a self-target effect that doesn't reach the input unit).\n for (const supportId of input.supportingUnitIds ?? []) {\n const supporter = resolveUnit(supportId, factionId);\n if (!supporter) continue;\n for (const ability of supporter.abilities) {\n if (!phaseMatches(ability, phase)) continue;\n if (!isAuraScope(ability.raw.scope?.range)) continue;\n pushUnique(out, seen, {\n ability,\n source: { kind: \"support\", sourceUnitId: supportId },\n phases: intersect(ability.phases, phase),\n });\n }\n }\n\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction phaseMatches(ability: AbilityView, phase: Phase): boolean {\n const phases = ability.phases;\n // An ability with no phase-mapping is permissive — surface it everywhere so\n // the SPA can decide. M2's translator already gates conditional-on-phase\n // effects internally, so this stays generous on purpose.\n if (phases.length === 0) return true;\n return phases.includes(phase);\n}\n\nfunction stratagemPhaseMatches(stratagem: Stratagem, phase: Phase): boolean {\n if (!stratagem.phases || stratagem.phases.length === 0) return false;\n return (stratagem.phases as Phase[]).includes(phase);\n}\n\nfunction intersect(phases: Phase[], phase: Phase): Phase[] {\n return phases.includes(phase) ? [phase] : phases;\n}\n\nfunction isAuraScope(range: unknown): boolean {\n if (typeof range !== \"string\") return false;\n return range.startsWith(\"aura-\") || range === \"any-on-battlefield\" || range === \"any-visible\";\n}\n\nfunction pushUnique(\n out: EligibleAbility[],\n seen: Set<string>,\n entry: EligibleAbility,\n): void {\n const key = `${entry.source.kind}::${entry.ability.id}`;\n if (seen.has(key)) return;\n seen.add(key);\n out.push(entry);\n}\n"]}
@@ -0,0 +1,78 @@
1
+ /** Shape of an authored ability entry (only the fields this audit reads). */
2
+ interface AbilityEntry {
3
+ ability_id: string;
4
+ name?: string;
5
+ ability_type?: string;
6
+ community_notes?: string;
7
+ effect?: unknown;
8
+ }
9
+ export interface FactionCoverage {
10
+ faction: string;
11
+ total: number;
12
+ offensive: number;
13
+ defensive: number;
14
+ /** Produces neither an offensive nor a defensive buff. */
15
+ inert: number;
16
+ /** `community_notes` flags it an auto-generated stub / partial. */
17
+ stub: number;
18
+ /**
19
+ * Structurally a placeholder: the effect tree contains a modifier-bearing
20
+ * node with an empty `modifier: {}` (the original pass's untyped stub, e.g.
21
+ * `stat-modifier {}`). This — not `inert` — is the authoring worklist: an
22
+ * inert-but-correctly-typed ability (movement, objective control) is *done*;
23
+ * an empty-modifier node is a gap regardless of who consumes it.
24
+ */
25
+ stubStructural: number;
26
+ /** `community_notes` carries a verbatim GW `"Original:"` text dump (IP leak). */
27
+ gwTextLeak: number;
28
+ /** Explicitly tagged `"defensive ability (skipped for damage calc)"`. */
29
+ defensiveSkipped: number;
30
+ }
31
+ /**
32
+ * One named gap. Carries the ability's identity and current shape so the gap is
33
+ * self-describing on the consumer end (the fan-out joins the source rule; the
34
+ * editor / downstream tools can list "what's unauthored" without re-deriving).
35
+ */
36
+ export interface WorklistEntry {
37
+ faction: string;
38
+ ability_id: string;
39
+ name: string;
40
+ /** Top-level effect type as authored today (the "shape"), or `null` if absent. */
41
+ shape: string | null;
42
+ /** Has an empty-modifier placeholder node somewhere in its effect tree. */
43
+ stub: boolean;
44
+ offensive: boolean;
45
+ defensive: boolean;
46
+ /** Most-informative unsupported reason from the attacker walk, if any. */
47
+ gap: string | null;
48
+ }
49
+ export interface CoverageReport {
50
+ factions: FactionCoverage[];
51
+ totals: Omit<FactionCoverage, "faction">;
52
+ /** `unsupported.reason` (normalized) → count, descending. */
53
+ unsupportedReasons: {
54
+ reason: string;
55
+ count: number;
56
+ }[];
57
+ /** Per-ability named gaps — the authoring worklist. */
58
+ worklist: WorklistEntry[];
59
+ }
60
+ /**
61
+ * True if any node in the effect tree is a *parameter-requiring* leaf left with
62
+ * an empty `modifier: {}` — the original pass's untyped placeholder. Pure-flag
63
+ * effects (see {@link PARAMETERLESS_EFFECTS}) are exempt.
64
+ */
65
+ export declare function hasEmptyModifier(node: unknown): boolean;
66
+ /** Compute coverage for a set of factions. Pure — IO lives in the command. */
67
+ export declare function computeCoverage(input: {
68
+ faction: string;
69
+ abilities: AbilityEntry[];
70
+ }[]): CoverageReport;
71
+ export interface AuditCoverageOptions {
72
+ reporter?: "pretty" | "json";
73
+ write?: boolean;
74
+ }
75
+ /** Commander action: run the audit over the repo's enrichment data. */
76
+ export declare function auditCoverageCommand(opts?: AuditCoverageOptions): void;
77
+ export {};
78
+ //# sourceMappingURL=audit-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-coverage.d.ts","sourceRoot":"","sources":["../src/audit-coverage.ts"],"names":[],"mappings":"AA0CA,6EAA6E;AAC7E,UAAU,YAAY;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2EAA2E;IAC3E,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,0EAA0E;IAC1E,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IACzC,6DAA6D;IAC7D,kBAAkB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,uDAAuD;IACvD,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAuFD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAiBvD;AAWD,8EAA8E;AAC9E,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,YAAY,EAAE,CAAA;CAAE,EAAE,GACtD,cAAc,CA2EhB;AA+GD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAkB1E"}