@elite-dangerous-almanac/core 0.1.0-beta.1

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 (357) hide show
  1. package/LICENSE +48 -0
  2. package/PROVENANCE/SNAPSHOTS.md +36 -0
  3. package/PROVENANCE/astro/SOURCES.md +94 -0
  4. package/PROVENANCE/commodities/SOURCES.md +41 -0
  5. package/PROVENANCE/materials/SOURCES.md +69 -0
  6. package/PROVENANCE/ships/SOURCES.md +1580 -0
  7. package/README.md +148 -0
  8. package/THIRD_PARTY_NOTICES.md +202 -0
  9. package/dist/astro/codex-region-lookup.d.ts +169 -0
  10. package/dist/astro/codex-region-lookup.js +1 -0
  11. package/dist/astro/codex-region-lookup.js.map +1 -0
  12. package/dist/astro/codex-region.d.ts +149 -0
  13. package/dist/astro/codex-region.js +1 -0
  14. package/dist/astro/codex-region.js.map +1 -0
  15. package/dist/astro/galaxy-grid.d.ts +83 -0
  16. package/dist/astro/galaxy-grid.js +1 -0
  17. package/dist/astro/galaxy-grid.js.map +1 -0
  18. package/dist/astro/hand-authored-regions.d.ts +121 -0
  19. package/dist/astro/hand-authored-regions.js +1 -0
  20. package/dist/astro/hand-authored-regions.js.map +1 -0
  21. package/dist/astro/index.d.ts +140 -0
  22. package/dist/astro/index.js +1 -0
  23. package/dist/astro/index.js.map +1 -0
  24. package/dist/astro/mass-code.d.ts +55 -0
  25. package/dist/astro/mass-code.js +1 -0
  26. package/dist/astro/mass-code.js.map +1 -0
  27. package/dist/astro/naming-region-origins.d.ts +4 -0
  28. package/dist/astro/naming-region-origins.js +1 -0
  29. package/dist/astro/naming-region-origins.js.map +1 -0
  30. package/dist/astro/nebulae-all.d.ts +40 -0
  31. package/dist/astro/nebulae-all.js +1 -0
  32. package/dist/astro/nebulae-all.js.map +1 -0
  33. package/dist/astro/nebulae-planetary.d.ts +37 -0
  34. package/dist/astro/nebulae-planetary.js +1 -0
  35. package/dist/astro/nebulae-planetary.js.map +1 -0
  36. package/dist/astro/nebulae-procgen.d.ts +36 -0
  37. package/dist/astro/nebulae-procgen.js +1 -0
  38. package/dist/astro/nebulae-procgen.js.map +1 -0
  39. package/dist/astro/nebulae-real.d.ts +37 -0
  40. package/dist/astro/nebulae-real.js +1 -0
  41. package/dist/astro/nebulae-real.js.map +1 -0
  42. package/dist/astro/nebulae.d.ts +169 -0
  43. package/dist/astro/nebulae.js +1 -0
  44. package/dist/astro/nebulae.js.map +1 -0
  45. package/dist/astro/permit-locked-regions.d.ts +54 -0
  46. package/dist/astro/permit-locked-regions.js +1 -0
  47. package/dist/astro/permit-locked-regions.js.map +1 -0
  48. package/dist/astro/permit-locked-systems.d.ts +74 -0
  49. package/dist/astro/permit-locked-systems.js +1 -0
  50. package/dist/astro/permit-locked-systems.js.map +1 -0
  51. package/dist/astro/permit-locks.d.ts +130 -0
  52. package/dist/astro/permit-locks.js +1 -0
  53. package/dist/astro/permit-locks.js.map +1 -0
  54. package/dist/astro/procedural-system.d.ts +228 -0
  55. package/dist/astro/procedural-system.js +1 -0
  56. package/dist/astro/procedural-system.js.map +1 -0
  57. package/dist/astro/sector-name.d.ts +87 -0
  58. package/dist/astro/sector-name.js +1 -0
  59. package/dist/astro/sector-name.js.map +1 -0
  60. package/dist/astro/system-address-input.d.ts +76 -0
  61. package/dist/astro/system-address-input.js +1 -0
  62. package/dist/astro/system-address-input.js.map +1 -0
  63. package/dist/astro/system-address.d.ts +4 -0
  64. package/dist/astro/system-address.js +1 -0
  65. package/dist/astro/system-address.js.map +1 -0
  66. package/dist/astro/system-name.d.ts +187 -0
  67. package/dist/astro/system-name.js +1 -0
  68. package/dist/astro/system-name.js.map +1 -0
  69. package/dist/chunk-2FHHMYDC.js +1 -0
  70. package/dist/chunk-2FHHMYDC.js.map +1 -0
  71. package/dist/chunk-462OKHXR.js +1 -0
  72. package/dist/chunk-462OKHXR.js.map +1 -0
  73. package/dist/chunk-5H74FKY7.js +1 -0
  74. package/dist/chunk-5H74FKY7.js.map +1 -0
  75. package/dist/chunk-6LUEKHO5.js +1 -0
  76. package/dist/chunk-6LUEKHO5.js.map +1 -0
  77. package/dist/chunk-76XKY2Y2.js +1 -0
  78. package/dist/chunk-76XKY2Y2.js.map +1 -0
  79. package/dist/chunk-7EE5VIHR.js +1 -0
  80. package/dist/chunk-7EE5VIHR.js.map +1 -0
  81. package/dist/chunk-A3NM7SAJ.js +1 -0
  82. package/dist/chunk-A3NM7SAJ.js.map +1 -0
  83. package/dist/chunk-A6XPYSVR.js +1 -0
  84. package/dist/chunk-A6XPYSVR.js.map +1 -0
  85. package/dist/chunk-AA3K5XUE.js +1 -0
  86. package/dist/chunk-AA3K5XUE.js.map +1 -0
  87. package/dist/chunk-AVZI6VKA.js +1 -0
  88. package/dist/chunk-AVZI6VKA.js.map +1 -0
  89. package/dist/chunk-B5TT26XE.js +1 -0
  90. package/dist/chunk-B5TT26XE.js.map +1 -0
  91. package/dist/chunk-BNILZXJD.js +1 -0
  92. package/dist/chunk-BNILZXJD.js.map +1 -0
  93. package/dist/chunk-BRDQUJXV.js +1 -0
  94. package/dist/chunk-BRDQUJXV.js.map +1 -0
  95. package/dist/chunk-BTPV5CIF.js +1 -0
  96. package/dist/chunk-BTPV5CIF.js.map +1 -0
  97. package/dist/chunk-CI3ACMRA.js +1 -0
  98. package/dist/chunk-CI3ACMRA.js.map +1 -0
  99. package/dist/chunk-COE5ADYJ.js +1 -0
  100. package/dist/chunk-COE5ADYJ.js.map +1 -0
  101. package/dist/chunk-CWAX3BSH.js +1 -0
  102. package/dist/chunk-CWAX3BSH.js.map +1 -0
  103. package/dist/chunk-DACZE3HX.js +1 -0
  104. package/dist/chunk-DACZE3HX.js.map +1 -0
  105. package/dist/chunk-DURKO7JU.js +1 -0
  106. package/dist/chunk-DURKO7JU.js.map +1 -0
  107. package/dist/chunk-E45EDCYH.js +1 -0
  108. package/dist/chunk-E45EDCYH.js.map +1 -0
  109. package/dist/chunk-ELIZXUMI.js +1 -0
  110. package/dist/chunk-ELIZXUMI.js.map +1 -0
  111. package/dist/chunk-EYGVDJ2I.js +1 -0
  112. package/dist/chunk-EYGVDJ2I.js.map +1 -0
  113. package/dist/chunk-FGXWVFN6.js +1 -0
  114. package/dist/chunk-FGXWVFN6.js.map +1 -0
  115. package/dist/chunk-GJCB2Z72.js +1 -0
  116. package/dist/chunk-GJCB2Z72.js.map +1 -0
  117. package/dist/chunk-HI7FCS3G.js +1 -0
  118. package/dist/chunk-HI7FCS3G.js.map +1 -0
  119. package/dist/chunk-HKXXFIRI.js +1 -0
  120. package/dist/chunk-HKXXFIRI.js.map +1 -0
  121. package/dist/chunk-HTB52N6S.js +1 -0
  122. package/dist/chunk-HTB52N6S.js.map +1 -0
  123. package/dist/chunk-I7PIDRMU.js +1 -0
  124. package/dist/chunk-I7PIDRMU.js.map +1 -0
  125. package/dist/chunk-IH4NXKVW.js +1 -0
  126. package/dist/chunk-IH4NXKVW.js.map +1 -0
  127. package/dist/chunk-INNFH37U.js +1 -0
  128. package/dist/chunk-INNFH37U.js.map +1 -0
  129. package/dist/chunk-J2PNZQY4.js +1 -0
  130. package/dist/chunk-J2PNZQY4.js.map +1 -0
  131. package/dist/chunk-JQG4C67D.js +1 -0
  132. package/dist/chunk-JQG4C67D.js.map +1 -0
  133. package/dist/chunk-JTQVWIGL.js +1 -0
  134. package/dist/chunk-JTQVWIGL.js.map +1 -0
  135. package/dist/chunk-JWJ7RSZC.js +1 -0
  136. package/dist/chunk-JWJ7RSZC.js.map +1 -0
  137. package/dist/chunk-K3AMV27L.js +1 -0
  138. package/dist/chunk-K3AMV27L.js.map +1 -0
  139. package/dist/chunk-K7F6WC4T.js +1 -0
  140. package/dist/chunk-K7F6WC4T.js.map +1 -0
  141. package/dist/chunk-K7L7SMIR.js +1 -0
  142. package/dist/chunk-K7L7SMIR.js.map +1 -0
  143. package/dist/chunk-KG4EWDZZ.js +1 -0
  144. package/dist/chunk-KG4EWDZZ.js.map +1 -0
  145. package/dist/chunk-KMFUCORC.js +1 -0
  146. package/dist/chunk-KMFUCORC.js.map +1 -0
  147. package/dist/chunk-L2ZZXEZM.js +1 -0
  148. package/dist/chunk-L2ZZXEZM.js.map +1 -0
  149. package/dist/chunk-L747RVPO.js +1 -0
  150. package/dist/chunk-L747RVPO.js.map +1 -0
  151. package/dist/chunk-MFV4VZFP.js +1 -0
  152. package/dist/chunk-MFV4VZFP.js.map +1 -0
  153. package/dist/chunk-NWPK6Q3S.js +1 -0
  154. package/dist/chunk-NWPK6Q3S.js.map +1 -0
  155. package/dist/chunk-PMJG7PHU.js +1 -0
  156. package/dist/chunk-PMJG7PHU.js.map +1 -0
  157. package/dist/chunk-PP2VSA6M.js +1 -0
  158. package/dist/chunk-PP2VSA6M.js.map +1 -0
  159. package/dist/chunk-Q22LXT53.js +1 -0
  160. package/dist/chunk-Q22LXT53.js.map +1 -0
  161. package/dist/chunk-Q5MR36RW.js +1 -0
  162. package/dist/chunk-Q5MR36RW.js.map +1 -0
  163. package/dist/chunk-Q5XOGATC.js +1 -0
  164. package/dist/chunk-Q5XOGATC.js.map +1 -0
  165. package/dist/chunk-QTDBO7R2.js +1 -0
  166. package/dist/chunk-QTDBO7R2.js.map +1 -0
  167. package/dist/chunk-R4OD62HV.js +1 -0
  168. package/dist/chunk-R4OD62HV.js.map +1 -0
  169. package/dist/chunk-RD73TFGS.js +1 -0
  170. package/dist/chunk-RD73TFGS.js.map +1 -0
  171. package/dist/chunk-RIT6MOO5.js +1 -0
  172. package/dist/chunk-RIT6MOO5.js.map +1 -0
  173. package/dist/chunk-RVSXKQHE.js +1 -0
  174. package/dist/chunk-RVSXKQHE.js.map +1 -0
  175. package/dist/chunk-S4DBNX2B.js +1 -0
  176. package/dist/chunk-S4DBNX2B.js.map +1 -0
  177. package/dist/chunk-S4UWCHL6.js +1 -0
  178. package/dist/chunk-S4UWCHL6.js.map +1 -0
  179. package/dist/chunk-SQS7672E.js +1 -0
  180. package/dist/chunk-SQS7672E.js.map +1 -0
  181. package/dist/chunk-TLNHETGC.js +1 -0
  182. package/dist/chunk-TLNHETGC.js.map +1 -0
  183. package/dist/chunk-U6TMCYA6.js +1 -0
  184. package/dist/chunk-U6TMCYA6.js.map +1 -0
  185. package/dist/chunk-V4C6FIE2.js +1 -0
  186. package/dist/chunk-V4C6FIE2.js.map +1 -0
  187. package/dist/chunk-VXVUEF5U.js +1 -0
  188. package/dist/chunk-VXVUEF5U.js.map +1 -0
  189. package/dist/chunk-VZXL5KBR.js +1 -0
  190. package/dist/chunk-VZXL5KBR.js.map +1 -0
  191. package/dist/chunk-VZZ2XIRE.js +1 -0
  192. package/dist/chunk-VZZ2XIRE.js.map +1 -0
  193. package/dist/chunk-WV5YM7H5.js +1 -0
  194. package/dist/chunk-WV5YM7H5.js.map +1 -0
  195. package/dist/chunk-Y2MUIE5W.js +1 -0
  196. package/dist/chunk-Y2MUIE5W.js.map +1 -0
  197. package/dist/chunk-Z43DN4PY.js +1 -0
  198. package/dist/chunk-Z43DN4PY.js.map +1 -0
  199. package/dist/chunk-Z4GTTB7I.js +1 -0
  200. package/dist/chunk-Z4GTTB7I.js.map +1 -0
  201. package/dist/chunk-Z4OUB4SJ.js +1 -0
  202. package/dist/chunk-Z4OUB4SJ.js.map +1 -0
  203. package/dist/chunk-ZFT56QFR.js +1 -0
  204. package/dist/chunk-ZFT56QFR.js.map +1 -0
  205. package/dist/chunk-ZNCXENNB.js +1 -0
  206. package/dist/chunk-ZNCXENNB.js.map +1 -0
  207. package/dist/commodities/commodities-all.d.ts +25 -0
  208. package/dist/commodities/commodities-all.js +1 -0
  209. package/dist/commodities/commodities-all.js.map +1 -0
  210. package/dist/commodities/commodities-rare.d.ts +34 -0
  211. package/dist/commodities/commodities-rare.js +1 -0
  212. package/dist/commodities/commodities-rare.js.map +1 -0
  213. package/dist/commodities/commodities-standard.d.ts +34 -0
  214. package/dist/commodities/commodities-standard.js +1 -0
  215. package/dist/commodities/commodities-standard.js.map +1 -0
  216. package/dist/commodities/commodities.d.ts +142 -0
  217. package/dist/commodities/commodities.js +1 -0
  218. package/dist/commodities/commodities.js.map +1 -0
  219. package/dist/commodities/index.d.ts +30 -0
  220. package/dist/commodities/index.js +1 -0
  221. package/dist/commodities/index.js.map +1 -0
  222. package/dist/galactic-position-shLkm4Qg.d.ts +30 -0
  223. package/dist/materials/index.d.ts +47 -0
  224. package/dist/materials/index.js +1 -0
  225. package/dist/materials/index.js.map +1 -0
  226. package/dist/materials/materials-all.d.ts +26 -0
  227. package/dist/materials/materials-all.js +1 -0
  228. package/dist/materials/materials-all.js.map +1 -0
  229. package/dist/materials/materials-encoded.d.ts +31 -0
  230. package/dist/materials/materials-encoded.js +1 -0
  231. package/dist/materials/materials-encoded.js.map +1 -0
  232. package/dist/materials/materials-manufactured.d.ts +32 -0
  233. package/dist/materials/materials-manufactured.js +1 -0
  234. package/dist/materials/materials-manufactured.js.map +1 -0
  235. package/dist/materials/materials-raw.d.ts +31 -0
  236. package/dist/materials/materials-raw.js +1 -0
  237. package/dist/materials/materials-raw.js.map +1 -0
  238. package/dist/materials/materials.d.ts +277 -0
  239. package/dist/materials/materials.js +1 -0
  240. package/dist/materials/materials.js.map +1 -0
  241. package/dist/materials/micro-resources-all.d.ts +28 -0
  242. package/dist/materials/micro-resources-all.js +1 -0
  243. package/dist/materials/micro-resources-all.js.map +1 -0
  244. package/dist/materials/micro-resources-component.d.ts +29 -0
  245. package/dist/materials/micro-resources-component.js +1 -0
  246. package/dist/materials/micro-resources-component.js.map +1 -0
  247. package/dist/materials/micro-resources-consumable.d.ts +29 -0
  248. package/dist/materials/micro-resources-consumable.js +1 -0
  249. package/dist/materials/micro-resources-consumable.js.map +1 -0
  250. package/dist/materials/micro-resources-data.d.ts +29 -0
  251. package/dist/materials/micro-resources-data.js +1 -0
  252. package/dist/materials/micro-resources-data.js.map +1 -0
  253. package/dist/materials/micro-resources-item.d.ts +29 -0
  254. package/dist/materials/micro-resources-item.js +1 -0
  255. package/dist/materials/micro-resources-item.js.map +1 -0
  256. package/dist/materials/micro-resources.d.ts +136 -0
  257. package/dist/materials/micro-resources.js +1 -0
  258. package/dist/materials/micro-resources.js.map +1 -0
  259. package/dist/ship-loadout-Ba63RDf-.d.ts +1059 -0
  260. package/dist/ships/ammunition.d.ts +107 -0
  261. package/dist/ships/ammunition.js +1 -0
  262. package/dist/ships/ammunition.js.map +1 -0
  263. package/dist/ships/armour.d.ts +134 -0
  264. package/dist/ships/armour.js +1 -0
  265. package/dist/ships/armour.js.map +1 -0
  266. package/dist/ships/blueprint-costs.d.ts +128 -0
  267. package/dist/ships/blueprint-costs.js +1 -0
  268. package/dist/ships/blueprint-costs.js.map +1 -0
  269. package/dist/ships/blueprint-journal.d.ts +116 -0
  270. package/dist/ships/blueprint-journal.js +1 -0
  271. package/dist/ships/blueprint-journal.js.map +1 -0
  272. package/dist/ships/blueprints.d.ts +103 -0
  273. package/dist/ships/blueprints.js +1 -0
  274. package/dist/ships/blueprints.js.map +1 -0
  275. package/dist/ships/decorative-modifications.d.ts +176 -0
  276. package/dist/ships/decorative-modifications.js +1 -0
  277. package/dist/ships/decorative-modifications.js.map +1 -0
  278. package/dist/ships/engineering-options.d.ts +246 -0
  279. package/dist/ships/engineering-options.js +1 -0
  280. package/dist/ships/engineering-options.js.map +1 -0
  281. package/dist/ships/engineering.d.ts +229 -0
  282. package/dist/ships/engineering.js +1 -0
  283. package/dist/ships/engineering.js.map +1 -0
  284. package/dist/ships/experimental-effect-costs.d.ts +56 -0
  285. package/dist/ships/experimental-effect-costs.js +1 -0
  286. package/dist/ships/experimental-effect-costs.js.map +1 -0
  287. package/dist/ships/experimental-effects.d.ts +62 -0
  288. package/dist/ships/experimental-effects.js +1 -0
  289. package/dist/ships/experimental-effects.js.map +1 -0
  290. package/dist/ships/index.d.ts +205 -0
  291. package/dist/ships/index.js +1 -0
  292. package/dist/ships/index.js.map +1 -0
  293. package/dist/ships/jump-range.d.ts +115 -0
  294. package/dist/ships/jump-range.js +1 -0
  295. package/dist/ships/jump-range.js.map +1 -0
  296. package/dist/ships/loadout-calculations.d.ts +109 -0
  297. package/dist/ships/loadout-calculations.js +1 -0
  298. package/dist/ships/loadout-calculations.js.map +1 -0
  299. package/dist/ships/loadout-validation.d.ts +80 -0
  300. package/dist/ships/loadout-validation.js +1 -0
  301. package/dist/ships/loadout-validation.js.map +1 -0
  302. package/dist/ships/module-capabilities.d.ts +205 -0
  303. package/dist/ships/module-capabilities.js +1 -0
  304. package/dist/ships/module-capabilities.js.map +1 -0
  305. package/dist/ships/modules-all.d.ts +43 -0
  306. package/dist/ships/modules-all.js +1 -0
  307. package/dist/ships/modules-all.js.map +1 -0
  308. package/dist/ships/modules-core.d.ts +40 -0
  309. package/dist/ships/modules-core.js +1 -0
  310. package/dist/ships/modules-core.js.map +1 -0
  311. package/dist/ships/modules-hardpoint.d.ts +42 -0
  312. package/dist/ships/modules-hardpoint.js +1 -0
  313. package/dist/ships/modules-hardpoint.js.map +1 -0
  314. package/dist/ships/modules-internal.d.ts +40 -0
  315. package/dist/ships/modules-internal.js +1 -0
  316. package/dist/ships/modules-internal.js.map +1 -0
  317. package/dist/ships/modules-utility.d.ts +40 -0
  318. package/dist/ships/modules-utility.js +1 -0
  319. package/dist/ships/modules-utility.js.map +1 -0
  320. package/dist/ships/modules.d.ts +755 -0
  321. package/dist/ships/modules.js +1 -0
  322. package/dist/ships/modules.js.map +1 -0
  323. package/dist/ships/power.d.ts +168 -0
  324. package/dist/ships/power.js +1 -0
  325. package/dist/ships/power.js.map +1 -0
  326. package/dist/ships/pre-engineered-stats.d.ts +147 -0
  327. package/dist/ships/pre-engineered-stats.js +1 -0
  328. package/dist/ships/pre-engineered-stats.js.map +1 -0
  329. package/dist/ships/pre-engineered.d.ts +215 -0
  330. package/dist/ships/pre-engineered.js +1 -0
  331. package/dist/ships/pre-engineered.js.map +1 -0
  332. package/dist/ships/resistances.d.ts +208 -0
  333. package/dist/ships/resistances.js +1 -0
  334. package/dist/ships/resistances.js.map +1 -0
  335. package/dist/ships/shields.d.ts +221 -0
  336. package/dist/ships/shields.js +1 -0
  337. package/dist/ships/shields.js.map +1 -0
  338. package/dist/ships/ship-loadout.d.ts +16 -0
  339. package/dist/ships/ship-loadout.js +1 -0
  340. package/dist/ships/ship-loadout.js.map +1 -0
  341. package/dist/ships/ships.d.ts +191 -0
  342. package/dist/ships/ships.js +1 -0
  343. package/dist/ships/ships.js.map +1 -0
  344. package/dist/ships/slef.d.ts +318 -0
  345. package/dist/ships/slef.js +1 -0
  346. package/dist/ships/slef.js.map +1 -0
  347. package/dist/ships/slots.d.ts +418 -0
  348. package/dist/ships/slots.js +1 -0
  349. package/dist/ships/slots.js.map +1 -0
  350. package/dist/ships/source-purchase.d.ts +132 -0
  351. package/dist/ships/source-purchase.js +1 -0
  352. package/dist/ships/source-purchase.js.map +1 -0
  353. package/dist/ships/weapons.d.ts +380 -0
  354. package/dist/ships/weapons.js +1 -0
  355. package/dist/ships/weapons.js.map +1 -0
  356. package/dist/system-address-DYsN1qOT.d.ts +313 -0
  357. package/package.json +375 -0
@@ -0,0 +1,755 @@
1
+ import { EngineeringGroupId } from './engineering-options.js';
2
+ import { ModuleSlot, SlotRestriction } from './slots.js';
3
+
4
+ /**
5
+ * Outfitting-module types and lookups.
6
+ *
7
+ * Elite Dangerous has ~1200 fittable modules. This module holds the
8
+ * {@link OutfittingModule} record shape — a module's **identity and its stats**
9
+ * together — and the functions that find one ({@link getModuleBySymbol},
10
+ * {@link getModulesByName}, {@link getBulkheadsForShip}).
11
+ *
12
+ * **Every lookup searches all 1199 modules by default.** A journal `Item` string does
13
+ * not identify its outfitting category, so callers need no category for lookup:
14
+ *
15
+ * ```ts
16
+ * getModuleBySymbol('Hpt_PulseLaser_Fixed_Small')?.name; // -> 'Pulse Laser'
17
+ * ```
18
+ *
19
+ * Each lookup takes an optional second argument to **narrow** the search to a
20
+ * subset — any array you have filtered yourself. The catalogue is also exported split
21
+ * by Frontier's four outfitting categories:
22
+ *
23
+ * | Module | Export | Entries |
24
+ * | --- | --- | --- |
25
+ * | `./modules-core` | `CORE_MODULES` | 521 |
26
+ * | `./modules-internal` | `INTERNAL_MODULES` | 484 |
27
+ * | `./modules-hardpoint` | `HARDPOINT_MODULES` | 159 |
28
+ * | `./modules-utility` | `UTILITY_MODULES` | 35 |
29
+ * | `./modules-all` | `ALL_MODULES` | 1199 (the default) |
30
+ *
31
+ * Those four are for **listing** a category — an outfitting screen's hardpoint tab.
32
+ * They make poor narrowing arguments: no module symbol or display name is shared
33
+ * across categories, so passing one to a lookup can only make it miss.
34
+ *
35
+ * The record shape is intentionally sparse. Use the data-free guards in
36
+ * `./module-capabilities` to narrow a lookup result before reading a complete stat group:
37
+ *
38
+ * ```ts
39
+ * const module = getModuleBySymbol(journalItem);
40
+ * if (hasFrameShiftDriveJumpStats(module)) module.maxFuel; // required here, in tonnes
41
+ * if (hasWeaponDamageStats(module)) weaponMetrics(module);
42
+ * ```
43
+ *
44
+ * @remarks
45
+ * **This is the one default that costs real bundle weight.** A lookup imported from
46
+ * here pulls all four catalogues — 311.9 KiB minified (30.5 KiB gzipped) — since
47
+ * that is what it falls back to, and passing an explicit catalogue does not undo it.
48
+ * A build that must carry only one category should import that catalogue and search
49
+ * it directly:
50
+ *
51
+ * ```ts
52
+ * // Catalogue symbols are mixed-case; a journal's are not.
53
+ * UTILITY_MODULES.find((m) => m.symbol.toLowerCase() === wanted);
54
+ * ```
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
59
+ *
60
+ * getModuleBySymbol('hpt_pulselaser_fixed_small')?.name; // -> 'Pulse Laser'
61
+ * ```
62
+ *
63
+ * @packageDocumentation
64
+ */
65
+
66
+ /**
67
+ * Frontier's outfitting category — which kind of slot a module fits.
68
+ *
69
+ * - `core` — the core internals every hull must fit (armour, power plant, thrusters,
70
+ * frame shift drive, life support, power distributor, sensors, fuel tank). Frontier's
71
+ * own registry calls this category "standard"; it is named for the slots it fills,
72
+ * which are the same seven {@link CoreSlotType}s plus armour. A fuel tank is a `core`
73
+ * module that also fits an optional slot.
74
+ * - `internal` — optional internals (cargo racks, shield generators, fuel scoops,
75
+ * passenger cabins, limpet and planetary controllers, …).
76
+ * - `hardpoint` — the weapons and tools mounted on a hardpoint.
77
+ * - `utility` — the small utility-mount fittings (chaff, heat sinks, point defence,
78
+ * shield boosters, scanners).
79
+ */
80
+ type ModuleCategory = 'core' | 'internal' | 'hardpoint' | 'utility';
81
+ /** How a hardpoint weapon is aimed. Only hardpoint modules carry a mount. */
82
+ type ModuleMount = 'Fixed' | 'Gimballed' | 'Turreted';
83
+ /** A missile/torpedo hardpoint's guidance. Only some hardpoints carry one. */
84
+ type ModuleGuidance = 'Dumbfire' | 'Seeker' | 'Swarm';
85
+ /** A module's grade letter, best (`A`) to worst; `I` is the armour placeholder. */
86
+ type ModuleRating = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I';
87
+ /**
88
+ * How a weapon's damage splits across the damage types, as fractions of one shot.
89
+ *
90
+ * @remarks
91
+ * The conventional shares — {@link DamageDistribution.kinetic | kinetic},
92
+ * {@link DamageDistribution.thermal | thermal},
93
+ * {@link DamageDistribution.explosive | explosive},
94
+ * {@link DamageDistribution.absolute | absolute}, and any
95
+ * {@link DamageDistribution.unclassified | unclassified} share — partition the damage and sum to
96
+ * `1`; a type a weapon does not deal is absent rather than `0`. Kinetic, thermal and
97
+ * explosive damage meet the defender's resistance of the same name. No shield or hull
98
+ * resistance reduces absolute damage; the type and mitigation of unclassified damage
99
+ * are not established by in-game verification.
100
+ *
101
+ * {@link DamageDistribution.antiXeno | antiXeno} is different: it **overlays** the
102
+ * conventional split instead of partitioning it, flagging the portion that is effective
103
+ * against Thargoid targets. It is expressed relative to conventional damage and can
104
+ * exceed `1`, so a distribution's values can sum past `1`.
105
+ */
106
+ interface DamageDistribution {
107
+ /** Kinetic share of one shot's damage, `0`–`1`. */
108
+ readonly kinetic?: number;
109
+ /** Thermal share of one shot's damage, `0`–`1`. */
110
+ readonly thermal?: number;
111
+ /** Explosive share of one shot's damage, `0`–`1`. */
112
+ readonly explosive?: number;
113
+ /** Absolute share — damage no resistance reduces — of one shot's damage, `0`–`1`. */
114
+ readonly absolute?: number;
115
+ /** Share unclassified by in-game verification, `0`–`1`. */
116
+ readonly unclassified?: number;
117
+ /**
118
+ * Anti-xeno ratio: the amount effective against Thargoids divided by conventional
119
+ * damage. Non-negative and potentially greater than `1`; see the type's remarks.
120
+ */
121
+ readonly antiXeno?: number;
122
+ }
123
+ /**
124
+ * Exact damage amounts carried by one round, or one second of continuous fire.
125
+ *
126
+ * @remarks
127
+ * Every amount is non-negative. Kinetic, thermal, explosive, absolute and all
128
+ * `unclassified` entries sum to the module's conventional {@link OutfittingModule.damage}.
129
+ * `antiXeno` overlays that conventional amount and is not added to it. The exact amounts
130
+ * are authoritative when present; {@link DamageDistribution} remains the compatible
131
+ * fractional projection.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import type { DamageComponents } from '@elite-dangerous-almanac/core/ships/modules';
136
+ *
137
+ * const components: DamageComponents = { explosive: 27, antiXeno: 43 };
138
+ * ```
139
+ */
140
+ interface DamageComponents {
141
+ /** Non-negative kinetic damage. */
142
+ readonly kinetic?: number;
143
+ /** Non-negative thermal damage. */
144
+ readonly thermal?: number;
145
+ /** Non-negative explosive damage. */
146
+ readonly explosive?: number;
147
+ /** Non-negative absolute damage, which no resistance reduces. */
148
+ readonly absolute?: number;
149
+ /** Non-negative damage effective against Thargoid targets, overlaid on conventional damage. */
150
+ readonly antiXeno?: number;
151
+ /** Non-negative damage amounts unclassified by in-game verification. */
152
+ readonly unclassified?: readonly number[];
153
+ }
154
+ /**
155
+ * In-game projectile boundary parameters that are not effective weapon ranges.
156
+ *
157
+ * @remarks
158
+ * Values are non-negative boundary parameters. They are deliberately not stated in
159
+ * metres and must not be passed to a range attenuation calculation: projectile reach
160
+ * depends on projectile behavior not represented by these two numbers.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * import type { ProjectileRangeBoundaries } from '@elite-dangerous-almanac/core/ships/modules';
165
+ *
166
+ * const boundaries: ProjectileRangeBoundaries = {
167
+ * maximumBoundary: 0,
168
+ * falloffBoundary: 100000,
169
+ * };
170
+ * ```
171
+ */
172
+ interface ProjectileRangeBoundaries {
173
+ /** Non-negative maximum boundary parameter observed in-game, when present. */
174
+ readonly maximumBoundary?: number;
175
+ /** Non-negative falloff boundary parameter observed in-game. */
176
+ readonly falloffBoundary: number;
177
+ }
178
+ /**
179
+ * Identity, classification, fit constraints, and price for one outfitting module.
180
+ *
181
+ * @remarks
182
+ * The core identity fields (`symbol`, `name`, `category`, `class`, and `rating`) are
183
+ * always present. Optional fields describe fit restrictions, purchase entitlement, and
184
+ * price. Performance data belongs to {@link OutfittingModuleStats}; the complete flat
185
+ * catalogue record is {@link OutfittingModule}.
186
+ *
187
+ * @example
188
+ * ```ts
189
+ * import type { OutfittingModuleIdentity } from '@elite-dangerous-almanac/core/ships/modules';
190
+ *
191
+ * const identity: OutfittingModuleIdentity = {
192
+ * symbol: 'CustomCargoRack',
193
+ * category: 'internal',
194
+ * engineeringGroup: 'cargoRacks',
195
+ * name: 'Custom Cargo Rack',
196
+ * class: 2,
197
+ * rating: 'E',
198
+ * };
199
+ * ```
200
+ */
201
+ interface OutfittingModuleIdentity {
202
+ /** Internal identifier, e.g. `"Hpt_PulseLaser_Fixed_Small"`. Unique — the module's key. */
203
+ readonly symbol: string;
204
+ /**
205
+ * Which kind of slot the module fits.
206
+ *
207
+ * @remarks
208
+ * Derived from the catalogue the record was read from rather than stored on it —
209
+ * `CORE_MODULES` is what makes a record `'core'` — so it is always present and
210
+ * always agrees with the catalogue you found the module in — it is written after
211
+ * the record's own fields, so the file wins outright. That also makes it the last
212
+ * key on the record, which is worth knowing only if you serialize one and compare
213
+ * the resulting string.
214
+ */
215
+ readonly category: ModuleCategory;
216
+ /**
217
+ * Stable engineering-menu family, or `null` when no source classifies this module.
218
+ * See {@link EngineeringGroupId}.
219
+ */
220
+ readonly engineeringGroup: EngineeringGroupId | null;
221
+ /**
222
+ * The one fixed mount this module fills, when it fills one: `'armour'` or one of
223
+ * the seven {@link CoreSlotType} core functions.
224
+ *
225
+ * @remarks
226
+ * Present on every `core` module, and on the fifteen Guardian Hybrid power plants
227
+ * and power distributors — which Frontier files under `internal`, but which go in
228
+ * a core mount. Absent on everything else, because there is no one mount to name:
229
+ * a weapon, a utility fitting or an ordinary optional internal fits any mount of
230
+ * its kind that is large enough.
231
+ *
232
+ * This is the module's half of the fit rule and `BuildSlot.core` is the mount's
233
+ * half; `ShipLoadout.setModule` matches the two. Read it rather than inferring a
234
+ * mount from the symbol — `Int_Engine_*` being thrusters is a naming habit, not a
235
+ * guarantee, and the Python Mk II's `Int_MkIIAgileBoost_*` thrusters already break
236
+ * it.
237
+ *
238
+ * A `fuelTank` is the one module that fits somewhere else as well: its own core
239
+ * mount *and* any optional slot large enough.
240
+ *
241
+ * **The rule is read off the record, not off the symbol** — the same way
242
+ * {@link OutfittingModule.restrictedToShips} behaves, and with the same
243
+ * consequence: a record you assemble yourself from a journal `Item` string, with
244
+ * no `slot` on it, will not go into a core mount. Resolve records from a catalogue
245
+ * ({@link getModuleBySymbol}) and the question does not arise.
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
250
+ *
251
+ * getModuleBySymbol('Int_Hyperdrive_Size5_Class5')?.slot; // -> 'frameShiftDrive'
252
+ * getModuleBySymbol('Anaconda_Armour_Grade1')?.slot; // -> 'armour'
253
+ * getModuleBySymbol('Int_CargoRack_Size4_Class1')?.slot; // -> undefined
254
+ * ```
255
+ */
256
+ readonly slot?: ModuleSlot;
257
+ /**
258
+ * Stable, descriptive English name, e.g. `"Pulse Laser"`.
259
+ *
260
+ * @remarks
261
+ * This is a canonical library label, not a byte-exact copy of the game's
262
+ * localized UI text: abbreviations such as FSD and AFM are expanded for readability.
263
+ * It is not localized and is not suitable as a localization key.
264
+ *
265
+ * **Not unique** — the game shows most modules at several sizes and ratings, and
266
+ * every hull's armour shares the same five names. Use {@link OutfittingModule.symbol}
267
+ * as the key; {@link getModulesByName} returns every match.
268
+ */
269
+ readonly name: string;
270
+ /**
271
+ * The module size, `0`–`8` — the number in the "5A" the outfitting screen shows.
272
+ *
273
+ * @remarks
274
+ * Frontier calls this the module *class*; it is the slot-size number, not the
275
+ * grade letter (that is {@link OutfittingModule.rating}). Named `class` to match
276
+ * the source registry.
277
+ */
278
+ readonly class: number;
279
+ /** The grade letter, `A`–`I` — the letter in the "5A" the screen shows. */
280
+ readonly rating: ModuleRating;
281
+ /**
282
+ * How the weapon is aimed. Present only on hardpoint weapons that have a mount
283
+ * variant; absent on every other module.
284
+ */
285
+ readonly mount?: ModuleMount;
286
+ /**
287
+ * A missile/torpedo hardpoint's guidance. Present only on the launchers that
288
+ * have one; absent on everything else.
289
+ */
290
+ readonly guidance?: ModuleGuidance;
291
+ /**
292
+ * The hull an armour variant belongs to, e.g. `"Anaconda"`. Present only on the
293
+ * `core`-category armour modules, which are the one ship-specific module;
294
+ * absent on every generic module.
295
+ */
296
+ readonly ship?: string;
297
+ /**
298
+ * Frontier's DLC / purchase-grant entitlement token, e.g.
299
+ * `"ELITE_HORIZONS_V_PLANETARY_LANDINGS"`. Present only on gated modules.
300
+ */
301
+ readonly entitlement?: string;
302
+ /**
303
+ * The hull symbol(s) a module is restricted to, when it is ship-specific — e.g.
304
+ * `["Explorer_NX"]` for the Python Mk II's MkII Gravity Optimised thrusters.
305
+ *
306
+ * @remarks
307
+ * Present only on the handful of non-armour modules limited to particular hulls.
308
+ * Armour is ship-specific too, but that restriction lives in
309
+ * {@link OutfittingModule.ship} / {@link getBulkheadsForShip}, not here. Symbols
310
+ * match {@link Ship.symbol}.
311
+ */
312
+ readonly restrictedToShips?: readonly string[];
313
+ /**
314
+ * The slot restriction a module **requires** — it fits only mounts carrying this
315
+ * {@link SlotRestriction}, and no unrestricted mount at all.
316
+ *
317
+ * @remarks
318
+ * Not to be confused with {@link OutfittingModule.slot}, which names *one* mount
319
+ * the module fills; this narrows a whole family of them, and the two never appear
320
+ * on the same record.
321
+ *
322
+ * The mirror image of `BuildSlot.restriction`, and the other half of the same
323
+ * rule: a mount's restriction says which modules it takes, this says which mounts
324
+ * a module goes in. Most restricted families bind one way only — a cargo rack fits
325
+ * a `cargo` mount *and* any unrestricted optional — so this is present on just the
326
+ * five records the game sells for one kind of mount and nowhere else:
327
+ *
328
+ * | Module | Requires |
329
+ * | --- | --- |
330
+ * | `Int_PlanetApproachSuite`, `Int_PlanetApproachSuite_Advanced` | `planetaryApproachSuite` |
331
+ * | `Int_LargeCargoRack_Size7_Class1`, `Int_LargeCargoRack_Size8_class1` (Mk II Cargo Rack) | `cargo` |
332
+ * | `Int_MultiDroneControl_MiningV2_Size5_Class5` (Mk II Mining Multi-Limpet Controller) | `limpetController` |
333
+ *
334
+ * It composes with {@link OutfittingModule.restrictedToShips} rather than
335
+ * replacing it: the Mk II racks name both the hull that can buy them and the kind
336
+ * of mount they go in, and a build must satisfy both. Where a module names a hull
337
+ * and nothing else — the Mk II Vessel Hangars, say — it fits that hull's ordinary
338
+ * optionals like anything else.
339
+ *
340
+ * **The rule is read off the record, not off the symbol.** A module you assemble
341
+ * yourself — from a journal `Item` string, say — is refused only if you give it
342
+ * this field, exactly as `restrictedToShips` behaves. Resolve records from a
343
+ * catalogue and the question does not arise.
344
+ * @example
345
+ * ```ts
346
+ * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
347
+ * import { INTERNAL_MODULES } from '@elite-dangerous-almanac/core/ships/modules-internal';
348
+ * import { ShipLoadout } from '@elite-dangerous-almanac/core/ships/ship-loadout';
349
+ *
350
+ * const rack = getModuleBySymbol('Int_LargeCargoRack_Size8_class1', INTERNAL_MODULES)!;
351
+ * rack.restrictedToSlot; // -> 'cargo'
352
+ * ShipLoadout.empty('PantherMkII').setModule('Cargo01', rack); // fits
353
+ * try {
354
+ * ShipLoadout.empty('PantherMkII').setModule('Slot01_Size8', rack);
355
+ * } catch (error) {
356
+ * if (error instanceof TypeError) error.message;
357
+ * // -> 'ShipLoadout.setModule: Int_LargeCargoRack_Size8_class1 → Slot01_Size8: module only fits a mount that takes cargo racks and fuel tanks'
358
+ * }
359
+ * ```
360
+ */
361
+ readonly restrictedToSlot?: SlotRestriction;
362
+ /**
363
+ * Standard purchase price, in credits — the base list price before any station
364
+ * discount or markup, which is what an outfitting screen quotes at 0% discount.
365
+ *
366
+ * @remarks
367
+ * Absent on the handful of records no registry prices: the starter `*_free`
368
+ * variants, the size-8 frame shift drives, and a few internals no outfitting
369
+ * registry carries a figure for — among them the two Corrosion Resistant Cargo
370
+ * Racks no station sells, which are not free. Treat `undefined` as "unknown", never
371
+ * as free — see [`data/ships/SOURCES.md`](https://github.com/DarkSession/Elite-Dangerous-Almanac/blob/main/data/ships/SOURCES.md).
372
+ */
373
+ readonly cost?: number;
374
+ }
375
+ /**
376
+ * Sparse performance and capability fields carried by an outfitting module.
377
+ *
378
+ * @remarks
379
+ * Every field is optional because no module family uses every stat. Use the guards in
380
+ * `./module-capabilities` when a calculation needs a complete group. The interface is
381
+ * also useful for functions that accept or return engineered stat snapshots without
382
+ * requiring a module's identity fields. Masses are tonnes, power is megawatts, jump
383
+ * ranges are light-years, and weapon ranges are metres.
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * import type { OutfittingModuleStats } from '@elite-dangerous-almanac/core/ships/modules';
388
+ *
389
+ * const engineered: OutfittingModuleStats = { mass: 18.4, powerDraw: 0.69 };
390
+ * ```
391
+ */
392
+ interface OutfittingModuleStats {
393
+ /** Mass, in tonnes. */
394
+ readonly mass?: number;
395
+ /** Integrity (hit points against module damage). */
396
+ readonly integrity?: number;
397
+ /** Power draw, in megawatts. */
398
+ readonly powerDraw?: number;
399
+ /** Boot time from power-on, in seconds. */
400
+ readonly bootTime?: number;
401
+ /** Optimised mass, in tonnes — thrusters, shield generators, and FSDs. */
402
+ readonly optMass?: number;
403
+ /** Minimum mass for the performance curve, in tonnes. */
404
+ readonly minMass?: number;
405
+ /** Maximum mass for the performance curve, in tonnes. */
406
+ readonly maxMass?: number;
407
+ /** Performance multiplier at `optMass` — thruster speed or shield strength. */
408
+ readonly optMultiplier?: number;
409
+ /** Minimum performance multiplier, reached at `maxMass`. */
410
+ readonly minMultiplier?: number;
411
+ /** Maximum performance multiplier, reached at `minMass`. */
412
+ readonly maxMultiplier?: number;
413
+ /**
414
+ * Thrusters: waste heat generated per second at top speed.
415
+ *
416
+ * @remarks
417
+ * The stat the journal calls `EngineHeatRate` — every Dirty Drive Tuning roll
418
+ * raises it and every Clean Drive roll lowers it. Frontier's own units: a bare
419
+ * number the ship's heat model consumes, not a temperature or a percentage.
420
+ */
421
+ readonly engineHeatRate?: number;
422
+ /** FSD: maximum fuel per jump, in tonnes. */
423
+ readonly maxFuel?: number;
424
+ /** FSD: rating (linear) fuel constant. */
425
+ readonly fuelMul?: number;
426
+ /** FSD: size (power) fuel constant. */
427
+ readonly fuelPower?: number;
428
+ /**
429
+ * Frame shift drive: waste heat generated per second while charging a jump.
430
+ *
431
+ * @remarks
432
+ * The journal's `FSDHeatRate`, in the same units as
433
+ * {@link OutfittingModule.engineHeatRate}. It is what Faster Boot Sequence trades
434
+ * away, what Shielded improves, and the whole of the Deep Charge / Thermal Spread
435
+ * (`special_fsd_cooled`) experimental effect. A drive's size sets it — every rating
436
+ * of a size shares one value, and a supercruise-assist (SCO) drive matches the plain
437
+ * drive of the same size.
438
+ */
439
+ readonly fsdHeatRate?: number;
440
+ /** Guardian FSD Booster: flat jump-range bonus, in light-years. */
441
+ readonly jumpBoost?: number;
442
+ /** Power plant: power generated, in megawatts. */
443
+ readonly powerCapacity?: number;
444
+ /** Power plant: heat efficiency (lower runs cooler). */
445
+ readonly heatEfficiency?: number;
446
+ /** Power distributor: WEP capacitor capacity. */
447
+ readonly weaponsCapacity?: number;
448
+ /** Power distributor: WEP recharge rate, per second. */
449
+ readonly weaponsRecharge?: number;
450
+ /** Power distributor: ENG capacitor capacity. */
451
+ readonly enginesCapacity?: number;
452
+ /** Power distributor: ENG recharge rate, per second. */
453
+ readonly enginesRecharge?: number;
454
+ /** Power distributor: SYS capacitor capacity. */
455
+ readonly systemsCapacity?: number;
456
+ /** Power distributor: SYS recharge rate, per second. */
457
+ readonly systemsRecharge?: number;
458
+ /** Fuel scoop: scoop rate, in tonnes per second. */
459
+ readonly refuelRate?: number;
460
+ /** Fuel tank: capacity, in tonnes. */
461
+ readonly fuelCapacity?: number;
462
+ /** Cargo rack: capacity, in tonnes. */
463
+ readonly cargoCapacity?: number;
464
+ /** Shield generator: regeneration rate, MJ per second. */
465
+ readonly shieldRegenRate?: number;
466
+ /** Shield generator: broken (down) regeneration rate, MJ per second. */
467
+ readonly shieldBrokenRegenRate?: number;
468
+ /** Shield booster: shield strength bonus, as a fraction (`0.04` = +4%). */
469
+ readonly shieldBoost?: number;
470
+ /** Shield cell bank: shield megajoules restored per second while a cell runs. */
471
+ readonly shieldBankReinforcement?: number;
472
+ /** Shield cell bank: waste heat generated by firing one cell. */
473
+ readonly shieldBankHeat?: number;
474
+ /** Shield cell bank: seconds between firing a cell and the shields starting to rise. */
475
+ readonly shieldBankSpinUp?: number;
476
+ /** Shield cell bank: seconds one cell keeps reinforcing for. */
477
+ readonly shieldBankDuration?: number;
478
+ /** Kinetic resistance, as a fraction (`0.4` = 40% resisted, `-0.2` = 20% weaker). */
479
+ readonly kineticResistance?: number;
480
+ /** Thermal resistance, as a fraction (negative is a weakness). */
481
+ readonly thermalResistance?: number;
482
+ /** Explosive resistance, as a fraction (negative is a weakness). */
483
+ readonly explosiveResistance?: number;
484
+ /** Caustic resistance, as a fraction (negative is a weakness). */
485
+ readonly causticResistance?: number;
486
+ /**
487
+ * Whether Anti-Guardian Zone Resistance protects this Guardian module from a
488
+ * Thargoid anti-Guardian field.
489
+ *
490
+ * @remarks
491
+ * A sparse capability flag rather than a percentage: `true` means the protection is
492
+ * inherent or the grade-1 blueprint grants it, while absence means it is not granted.
493
+ * The two Guardian Nanite Torpedo Pylons carry it inherently; read engineered
494
+ * protection from a fitted module's {@link FittedModule.effectiveStats}.
495
+ */
496
+ readonly guardianZoneResistance?: boolean;
497
+ /**
498
+ * Whether this particular resolved article accepts no further engineering.
499
+ *
500
+ * @remarks
501
+ * Stock module catalogues omit this field. `getPreEngineeredStats` sets it on final
502
+ * pre-engineered Guardian weapons so a fitted article exposes an empty engineering
503
+ * menu and rejects both blueprints and experimental effects.
504
+ */
505
+ readonly engineeringLocked?: boolean;
506
+ /**
507
+ * Armour: the hull hit points this bulkhead adds, as a fraction of the hull's
508
+ * {@link Ship.baseArmour} on top of it — `0.8` (lightweight alloy) means
509
+ * `baseArmour × 1.8`, `2.5` means `baseArmour × 3.5`.
510
+ *
511
+ * @remarks
512
+ * Carried by the ship-specific armour modules, which are the `core`-category
513
+ * records with a {@link OutfittingModule.ship}. List a hull's five (or, on the
514
+ * Caspian Explorer, six) options with {@link getBulkheadsForShip}.
515
+ */
516
+ readonly hullBoost?: number;
517
+ /** Hull reinforcement package: hull hit points added. */
518
+ readonly hullReinforcement?: number;
519
+ /** Guardian shield reinforcement package: shield megajoules added. */
520
+ readonly shieldAddition?: number;
521
+ /**
522
+ * Module reinforcement package: the fraction of module damage it absorbs
523
+ * (`0.3` = 30%). Protects the *modules*, not the hull.
524
+ */
525
+ readonly moduleProtection?: number;
526
+ /**
527
+ * Scan range, in **metres**.
528
+ *
529
+ * @remarks
530
+ * On a **utility scanner** it is the distance the scan reaches. On a **core sensor
531
+ * suite** it is the range at which a contact with typical emissions resolves — the
532
+ * "typical sensor range" the outfitting panel shows in kilometres and the journal
533
+ * reports in metres, not the suite's absolute detection ceiling.
534
+ *
535
+ * This is the sole distance field on utility scanners and sensor suites. A journal
536
+ * may spell its modifier `ScannerRange` or `Range`; both labels resolve here. Weapon
537
+ * distance is the separate {@link OutfittingModule.maximumRange} field.
538
+ */
539
+ readonly scannerRange?: number;
540
+ /**
541
+ * Scan cone half-angle, in degrees — how far off boresight a target can sit and
542
+ * still be scanned. Wide Angle raises it; Long Range and Lightweight narrow it.
543
+ */
544
+ readonly scanAngle?: number;
545
+ /** Seconds a scan takes to complete. Utility scanners only. */
546
+ readonly scanTime?: number;
547
+ /**
548
+ * Detailed Surface Scanner: probe radius, as a **percentage** (`20` = the stock
549
+ * 20%).
550
+ *
551
+ * @remarks
552
+ * Frontier stores this one as a percentage rather than a fraction, and the journal
553
+ * reports it that way too — a grade 4 Expanded Probe Scanning Radius roll takes the
554
+ * stock `20` to `28`. The journal spells the label `DSS_PatchRadius`; the blueprint
555
+ * recipe spells it `ProbeRadius`, and both resolve here.
556
+ */
557
+ readonly probeRadius?: number;
558
+ /** FSD interdictor: maximum target angle off boresight, in degrees. */
559
+ readonly interdictorFacingLimit?: number;
560
+ /**
561
+ * FSD interdictor: maximum target range, in **seconds to intercept** — the units the
562
+ * game measures a supercruise separation in, not a distance.
563
+ */
564
+ readonly interdictorRange?: number;
565
+ /**
566
+ * `true` when a hardpoint-mounted module draws its power continuously.
567
+ *
568
+ * @remarks
569
+ * Weapons and most utility fittings only draw power while the hardpoints are
570
+ * deployed; shield boosters, chaff, heat sinks, point defence, caustic sinks and
571
+ * shutdown field neutralisers draw theirs all the time, and carry this flag.
572
+ * Absent on every `core`/`internal` module, which are always powered anyway.
573
+ * See {@link powerDraw} and `./power`.
574
+ */
575
+ readonly alwaysPowered?: boolean;
576
+ /** Damage per round — or per second on a continuous-fire (beam) weapon. */
577
+ readonly damage?: number;
578
+ /** How `damage` splits across the damage types. */
579
+ readonly damageDistribution?: DamageDistribution;
580
+ /**
581
+ * Exact damage amounts when in-game verification exposes distinct components.
582
+ * On effective fitted stats these scale with engineered total damage, and are absent
583
+ * when engineering converts the weapon to a new fractional damage distribution.
584
+ */
585
+ readonly damageComponents?: DamageComponents;
586
+ /**
587
+ * Rounds fired per shot, for the weapons that fire several at once (fragment
588
+ * cannons, shard cannons). Absent means one round per shot.
589
+ */
590
+ readonly roundsPerShot?: number;
591
+ /**
592
+ * Shots per second with the burst pattern folded in — the journal's `RateOfFire`.
593
+ *
594
+ * @remarks
595
+ * Absent on continuous-fire weapons (beam and mining lasers), whose `damage` is
596
+ * already per second. Excludes charge and reload time; {@link OutfittingModule.chargeTime}
597
+ * is the delay before a shot lands, while {@link OutfittingModule.clipSize} and
598
+ * {@link OutfittingModule.reloadTime} give the sustained rate.
599
+ */
600
+ readonly rateOfFire?: number;
601
+ /** Seconds between shots — between *bursts* on a burst-fire weapon. */
602
+ readonly burstInterval?: number;
603
+ /** Shots in one burst. Absent (or `1`) on a weapon that does not fire in bursts. */
604
+ readonly burstRounds?: number;
605
+ /** Shots per second *within* a burst. */
606
+ readonly burstRateOfFire?: number;
607
+ /** Seconds spent charging before a shot (rail guns), if any. */
608
+ readonly chargeTime?: number;
609
+ /**
610
+ * Rounds in a clip before reloading. Absent on weapons that never reload.
611
+ *
612
+ * @remarks
613
+ * A capacity, and the largest `AmmoInClip` a journal can report for the module.
614
+ * `ammunitionCapacity` in `./ammunition` reads it together with
615
+ * {@link OutfittingModule.ammoMaximum}.
616
+ */
617
+ readonly clipSize?: number;
618
+ /**
619
+ * Reserve rounds to reload from — the magazine is *not* counted in it, exactly as the
620
+ * journal's `AmmoInHopper` does not count it. Absent beside a {@link clipSize} means
621
+ * nothing limits the refills (the mining Abrasion Blaster); absent beside no clip
622
+ * either means the module takes no ammunition at all, as the lasers do.
623
+ */
624
+ readonly ammoMaximum?: number;
625
+ /** Seconds to reload a clip. */
626
+ readonly reloadTime?: number;
627
+ /**
628
+ * Weapons-capacitor draw, in megawatts — per shot, or per second on a
629
+ * continuous-fire weapon. Shield generators carry it too, as the systems-capacitor
630
+ * cost of one MJ per second of regeneration — the stat the journal calls
631
+ * `EnergyPerRegen` rather than `DistributorDraw`, and the one Hi-Cap, Lo-draw and
632
+ * Force Block move.
633
+ */
634
+ readonly distributorDraw?: number;
635
+ /** Heat generated — per shot, or per second on a continuous-fire weapon. */
636
+ readonly thermalLoad?: number;
637
+ /**
638
+ * Armour piercing rating. Damage to a hull is scaled by
639
+ * `min(1, armourPiercing / hardness)` against a hull of that {@link Ship.hardness}.
640
+ */
641
+ readonly armourPiercing?: number;
642
+ /**
643
+ * Maximum effective range, in metres. A weapon does no damage beyond it; on a
644
+ * non-scanner utility module, it is the effect's reach.
645
+ */
646
+ readonly maximumRange?: number;
647
+ /** Range at which damage starts to drop off, in metres. */
648
+ readonly falloffRange?: number;
649
+ /** Projectile boundary parameters; these are not effective distances in metres. */
650
+ readonly projectileRange?: ProjectileRangeBoundaries;
651
+ /**
652
+ * Projectile speed, in metres per second.
653
+ *
654
+ * @remarks
655
+ * Absent on the weapons that have no projectile to speed up — the pulse, burst,
656
+ * beam and mining lasers, rail guns, Gauss cannons and mine launchers all hit (or
657
+ * drop) where they are aimed. No registry publishes a figure for them because there
658
+ * is none, so Long Range and Focused simply leave their shot speed alone; see
659
+ * {@link ShipLoadout.applyBlueprint}.
660
+ */
661
+ readonly shotSpeed?: number;
662
+ /** Maximum aim deviation, in degrees. */
663
+ readonly jitter?: number;
664
+ }
665
+ /**
666
+ * One fittable outfitting module: its identity and sparse stats in one flat record.
667
+ *
668
+ * @remarks
669
+ * Keeping the runtime record flat lets engineering modifiers address stat keys directly.
670
+ * Consumers that need a smaller contract can accept {@link OutfittingModuleIdentity},
671
+ * {@link OutfittingModuleStats}, or one of the required groups in
672
+ * `./module-capabilities`.
673
+ *
674
+ * @example
675
+ * ```ts
676
+ * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
677
+ *
678
+ * getModuleBySymbol('Int_Hyperdrive_Size5_Class5')?.engineeringGroup;
679
+ * // -> 'frameShiftDrives'
680
+ * ```
681
+ */
682
+ interface OutfittingModule extends OutfittingModuleIdentity, OutfittingModuleStats {
683
+ }
684
+ /**
685
+ * Look up a module by its internal symbol, case-insensitively.
686
+ *
687
+ * @param symbol - The internal identifier, e.g. `"Hpt_PulseLaser_Fixed_Small"`.
688
+ * Leading/trailing whitespace and case are ignored, so the journal's lower-cased
689
+ * form resolves too.
690
+ * @param modules - Optional subset to search instead of all 1199 modules —
691
+ * `CORE_MODULES`, `INTERNAL_MODULES`, `HARDPOINT_MODULES`, `UTILITY_MODULES`, or any
692
+ * array you have filtered yourself. Omit it unless you specifically want to exclude
693
+ * the other categories; a symbol is unique across all four.
694
+ * @returns The matching {@link OutfittingModule}, or `null` if no module has that
695
+ * symbol.
696
+ * @throws {TypeError} If `symbol` is present and not a string. A nullish
697
+ * `symbol` is a miss, answered the way an unrecognised one is.
698
+ * @example
699
+ * ```ts
700
+ * import { getModuleBySymbol } from '@elite-dangerous-almanac/core/ships/modules';
701
+ *
702
+ * getModuleBySymbol('hpt_pulselaser_fixed_small')?.class; // -> 1
703
+ * ```
704
+ */
705
+ declare function getModuleBySymbol(symbol: string, modules?: readonly OutfittingModule[]): OutfittingModule | null;
706
+ /**
707
+ * Every module with a given display name, in catalogue order.
708
+ *
709
+ * @param name - The display name as the registry spells it, e.g. `"Pulse Laser"`.
710
+ * Leading/trailing whitespace and case are ignored, but matching is otherwise exact.
711
+ * @param modules - Optional subset to search (see {@link getModuleBySymbol}).
712
+ * @returns All matching modules — the name is shared across sizes, ratings and (for
713
+ * armour) hulls, so this returns an array. Empty if none match. The input array is
714
+ * not modified.
715
+ * @throws {TypeError} If `name` is present and not a string. A nullish
716
+ * `name` is a miss, answered the way an unrecognised one is.
717
+ * @example
718
+ * ```ts
719
+ * import { getModulesByName } from '@elite-dangerous-almanac/core/ships/modules';
720
+ *
721
+ * getModulesByName('pulse laser').length; // -> every size/mount variant
722
+ * ```
723
+ */
724
+ declare function getModulesByName(name: string, modules?: readonly OutfittingModule[]): OutfittingModule[];
725
+ /**
726
+ * Every bulkhead (armour) variant a given hull can be fitted with, in catalogue order.
727
+ *
728
+ * @param ship - The hull's display name as the registry spells it, e.g.
729
+ * `"Anaconda"`. Leading/trailing whitespace and case are ignored, but matching is
730
+ * otherwise exact.
731
+ * @param modules - Optional subset to search (see {@link getModuleBySymbol}). Bulkheads
732
+ * live in `CORE_MODULES`; narrowing to any other category returns an empty array.
733
+ * @returns The hull's bulkhead modules — five variants, or six on the Caspian Explorer —
734
+ * or an empty array if none are carried for that hull. The input array is not modified.
735
+ * @remarks
736
+ * Bulkheads are the only hull-specific module; everything else fits by slot size, so
737
+ * this does *not* answer "what else can this hull carry" — that is slot layout, which the
738
+ * hull's own record carries. Reach it with {@link getShipByName}, which takes the same
739
+ * display name as this function; {@link getShipSlots} is the same layout keyed by
740
+ * {@link Ship.symbol} instead, and the two differ for most hulls (`"Viper MkIII"` is the
741
+ * record `"Viper"`).
742
+ * @throws {TypeError} If `ship` is present and not a string. A nullish
743
+ * `ship` is a miss, answered the way an unrecognised one is.
744
+ * @example
745
+ * ```ts
746
+ * import { getBulkheadsForShip } from '@elite-dangerous-almanac/core/ships/modules';
747
+ *
748
+ * getBulkheadsForShip('Anaconda').map((m) => m.name);
749
+ * // -> [ 'Lightweight Alloy', 'Reinforced Alloy', 'Military Grade Composite',
750
+ * // 'Mirrored Surface Composite', 'Reactive Surface Composite' ]
751
+ * ```
752
+ */
753
+ declare function getBulkheadsForShip(ship: string, modules?: readonly OutfittingModule[]): OutfittingModule[];
754
+
755
+ export { type DamageComponents, type DamageDistribution, type ModuleCategory, type ModuleGuidance, type ModuleMount, type ModuleRating, type OutfittingModule, type OutfittingModuleIdentity, type OutfittingModuleStats, type ProjectileRangeBoundaries, getBulkheadsForShip, getModuleBySymbol, getModulesByName };