@nekosuneprojects/gaming-infos 2.1.0 → 2.4.0

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 (722) hide show
  1. package/.github/workflows/sync-api-cache.yml +73 -0
  2. package/CACHE_SYNC.md +202 -0
  3. package/CHANGELOG.md +54 -2
  4. package/README.md +304 -136
  5. package/data/.cache-manifest.json +112 -0
  6. package/data/arknightsendfield/characters/akekuri.json +75 -0
  7. package/data/arknightsendfield/characters/alesh.json +75 -0
  8. package/data/arknightsendfield/characters/antal.json +75 -0
  9. package/data/arknightsendfield/characters/arcane.json +75 -0
  10. package/data/arknightsendfield/characters/arclight.json +75 -0
  11. package/data/arknightsendfield/characters/ardelia.json +75 -0
  12. package/data/arknightsendfield/characters/avywenna.json +76 -0
  13. package/data/arknightsendfield/characters/camille.json +77 -0
  14. package/data/arknightsendfield/characters/catcher.json +76 -0
  15. package/data/arknightsendfield/characters/chenqianyu.json +76 -0
  16. package/data/arknightsendfield/characters/dapan.json +76 -0
  17. package/data/arknightsendfield/characters/ember.json +76 -0
  18. package/data/arknightsendfield/characters/endministrator.json +63 -0
  19. package/data/arknightsendfield/characters/estella.json +76 -0
  20. package/data/arknightsendfield/characters/fluorite.json +75 -0
  21. package/data/arknightsendfield/characters/gilberta.json +76 -0
  22. package/data/arknightsendfield/characters/laevatain.json +76 -0
  23. package/data/arknightsendfield/characters/lastrite.json +76 -0
  24. package/data/arknightsendfield/characters/lifeng.json +75 -0
  25. package/data/arknightsendfield/characters/liino.json +76 -0
  26. package/data/arknightsendfield/characters/mifu.json +75 -0
  27. package/data/arknightsendfield/characters/perlica.json +76 -0
  28. package/data/arknightsendfield/characters/pogranichnik.json +75 -0
  29. package/data/arknightsendfield/characters/purrchena.json +50 -0
  30. package/data/arknightsendfield/characters/rossi.json +77 -0
  31. package/data/arknightsendfield/characters/snowshine.json +75 -0
  32. package/data/arknightsendfield/characters/tangtang.json +76 -0
  33. package/data/arknightsendfield/characters/typhoeus.json +57 -0
  34. package/data/arknightsendfield/characters/wulfgard.json +77 -0
  35. package/data/arknightsendfield/characters/xaihi.json +76 -0
  36. package/data/arknightsendfield/characters/yvonne.json +76 -0
  37. package/data/arknightsendfield/characters/zhuangfangyi.json +76 -0
  38. package/data/arknightsendfield/meta.json +14 -0
  39. package/data/fortnite/characters/agentjones.json +43 -0
  40. package/data/fortnite/characters/agentpeely.json +43 -0
  41. package/data/fortnite/characters/andyfangerson.json +42 -0
  42. package/data/fortnite/characters/antonia.json +43 -0
  43. package/data/fortnite/characters/anwar.json +44 -0
  44. package/data/fortnite/characters/aphrodite.json +43 -0
  45. package/data/fortnite/characters/apollo.json +36 -0
  46. package/data/fortnite/characters/arcticassassin.json +43 -0
  47. package/data/fortnite/characters/artemis.json +36 -0
  48. package/data/fortnite/characters/battlewoodboulevard.json +43 -0
  49. package/data/fortnite/characters/beastmode.json +45 -0
  50. package/data/fortnite/characters/bogstick.json +43 -0
  51. package/data/fortnite/characters/boxy.json +44 -0
  52. package/data/fortnite/characters/bravoleader.json +44 -0
  53. package/data/fortnite/characters/briteraider.json +43 -0
  54. package/data/fortnite/characters/brutalboxcars.json +46 -0
  55. package/data/fortnite/characters/bryne.json +44 -0
  56. package/data/fortnite/characters/buildersbarracks.json +38 -0
  57. package/data/fortnite/characters/bullshark.json +43 -0
  58. package/data/fortnite/characters/bumpybay.json +44 -0
  59. package/data/fortnite/characters/burd.json +44 -0
  60. package/data/fortnite/characters/burdemart.json +39 -0
  61. package/data/fortnite/characters/burdtogo.json +42 -0
  62. package/data/fortnite/characters/busansanctuary.json +39 -0
  63. package/data/fortnite/characters/calamaricanyon.json +41 -0
  64. package/data/fortnite/characters/canyoncrossing.json +45 -0
  65. package/data/fortnite/characters/captainjones.json +40 -0
  66. package/data/fortnite/characters/centurion.json +44 -0
  67. package/data/fortnite/characters/cerberus.json +43 -0
  68. package/data/fortnite/characters/charon.json +43 -0
  69. package/data/fortnite/characters/chiara.json +43 -0
  70. package/data/fortnite/characters/clara.json +43 -0
  71. package/data/fortnite/characters/classifiedcanyon.json +43 -0
  72. package/data/fortnite/characters/clawsylodge.json +41 -0
  73. package/data/fortnite/characters/cletuscornhole.json +43 -0
  74. package/data/fortnite/characters/contractgiller.json +44 -0
  75. package/data/fortnite/characters/corruptioncorners.json +42 -0
  76. package/data/fortnite/characters/countessdaraku.json +45 -0
  77. package/data/fortnite/characters/crackshotscabin.json +40 -0
  78. package/data/fortnite/characters/creepycamps.json +36 -0
  79. package/data/fortnite/characters/crimecity.json +47 -0
  80. package/data/fortnite/characters/dara.json +42 -0
  81. package/data/fortnite/characters/darkruby.json +43 -0
  82. package/data/fortnite/characters/demonsdojo.json +46 -0
  83. package/data/fortnite/characters/diamonddiva.json +43 -0
  84. package/data/fortnite/characters/donutdistrict.json +43 -0
  85. package/data/fortnite/characters/drakonsteelrider.json +43 -0
  86. package/data/fortnite/characters/dummy.json +45 -0
  87. package/data/fortnite/characters/edwardscissorhands.json +39 -0
  88. package/data/fortnite/characters/etheria.json +43 -0
  89. package/data/fortnite/characters/evergreenterrace.json +41 -0
  90. package/data/fortnite/characters/fishmarket.json +39 -0
  91. package/data/fortnite/characters/fishstick.json +45 -0
  92. package/data/fortnite/characters/fishthicc.json +43 -0
  93. package/data/fortnite/characters/fortressofsolitude.json +37 -0
  94. package/data/fortnite/characters/foxyfloodgate.json +43 -0
  95. package/data/fortnite/characters/freakyfields.json +38 -0
  96. package/data/fortnite/characters/goldengrove.json +41 -0
  97. package/data/fortnite/characters/graveheart.json +43 -0
  98. package/data/fortnite/characters/grievouscampsite.json +38 -0
  99. package/data/fortnite/characters/guardianamara.json +44 -0
  100. package/data/fortnite/characters/gwenpool.json +41 -0
  101. package/data/fortnite/characters/hanamuracourtyard.json +27 -0
  102. package/data/fortnite/characters/heartbreakranger.json +42 -0
  103. package/data/fortnite/characters/heatwaveharbor.json +41 -0
  104. package/data/fortnite/characters/heroichope.json +44 -0
  105. package/data/fortnite/characters/hope.json +43 -0
  106. package/data/fortnite/characters/hopefulheights.json +46 -0
  107. package/data/fortnite/characters/humblehills.json +41 -0
  108. package/data/fortnite/characters/innovatorslone.json +43 -0
  109. package/data/fortnite/characters/insight.json +43 -0
  110. package/data/fortnite/characters/ironman.json +40 -0
  111. package/data/fortnite/characters/jittershock.json +44 -0
  112. package/data/fortnite/characters/joeycaliente.json +44 -0
  113. package/data/fortnite/characters/jonithescarlet.json +43 -0
  114. package/data/fortnite/characters/khabylame.json +40 -0
  115. package/data/fortnite/characters/khari.json +44 -0
  116. package/data/fortnite/characters/kitbash.json +44 -0
  117. package/data/fortnite/characters/kitesflight.json +41 -0
  118. package/data/fortnite/characters/knightmare.json +43 -0
  119. package/data/fortnite/characters/laguna.json +43 -0
  120. package/data/fortnite/characters/lakespringfield.json +35 -0
  121. package/data/fortnite/characters/lakespringfieldgasstation.json +39 -0
  122. package/data/fortnite/characters/lanallane.json +42 -0
  123. package/data/fortnite/characters/lattelanding.json +41 -0
  124. package/data/fortnite/characters/lavendra.json +44 -0
  125. package/data/fortnite/characters/leelah.json +45 -0
  126. package/data/fortnite/characters/liftylodge.json +42 -0
  127. package/data/fortnite/characters/lonewolflair.json +42 -0
  128. package/data/fortnite/characters/longshot.json +41 -0
  129. package/data/fortnite/characters/lostlake.json +44 -0
  130. package/data/fortnite/characters/loveranger.json +44 -0
  131. package/data/fortnite/characters/mae.json +43 -0
  132. package/data/fortnite/characters/magicmosses.json +42 -0
  133. package/data/fortnite/characters/maskedmeadows.json +48 -0
  134. package/data/fortnite/characters/mechateamshadow.json +44 -0
  135. package/data/fortnite/characters/medusa.json +43 -0
  136. package/data/fortnite/characters/meowscles.json +42 -0
  137. package/data/fortnite/characters/mephisto.json +41 -0
  138. package/data/fortnite/characters/metalmouth.json +44 -0
  139. package/data/fortnite/characters/mincemeat.json +43 -0
  140. package/data/fortnite/characters/munitionsexpert.json +42 -0
  141. package/data/fortnite/characters/myna.json +42 -0
  142. package/data/fortnite/characters/newsanctuary.json +43 -0
  143. package/data/fortnite/characters/nia.json +42 -0
  144. package/data/fortnite/characters/nightshiftforest.json +43 -0
  145. package/data/fortnite/characters/nisha.json +42 -0
  146. package/data/fortnite/characters/nolanchance.json +43 -0
  147. package/data/fortnite/characters/noorah.json +44 -0
  148. package/data/fortnite/characters/odyssey.json +43 -0
  149. package/data/fortnite/characters/openaironsen.json +40 -0
  150. package/data/fortnite/characters/oscar.json +42 -0
  151. package/data/fortnite/characters/outlawoasis.json +40 -0
  152. package/data/fortnite/characters/outpostenclave.json +27 -0
  153. package/data/fortnite/characters/oxrhq.json +37 -0
  154. package/data/fortnite/characters/paintedpalms.json +41 -0
  155. package/data/fortnite/characters/peabody.json +42 -0
  156. package/data/fortnite/characters/peelosopherbananocrates.json +43 -0
  157. package/data/fortnite/characters/peely.json +41 -0
  158. package/data/fortnite/characters/peelybone.json +43 -0
  159. package/data/fortnite/characters/persephone.json +42 -0
  160. package/data/fortnite/characters/phaedra.json +42 -0
  161. package/data/fortnite/characters/phantommeowscles.json +43 -0
  162. package/data/fortnite/characters/piperpace.json +43 -0
  163. package/data/fortnite/characters/poseidon.json +42 -0
  164. package/data/fortnite/characters/pumpedpower.json +40 -0
  165. package/data/fortnite/characters/pumpinpipes.json +39 -0
  166. package/data/fortnite/characters/pumpkinking.json +40 -0
  167. package/data/fortnite/characters/purradisemeowscles.json +43 -0
  168. package/data/fortnite/characters/rangersrest.json +36 -0
  169. package/data/fortnite/characters/rebeloutpost.json +37 -0
  170. package/data/fortnite/characters/renegadeshadow.json +44 -0
  171. package/data/fortnite/characters/resistancebase.json +27 -0
  172. package/data/fortnite/characters/rippedtides.json +43 -0
  173. package/data/fortnite/characters/rollingblossomsfarm.json +41 -0
  174. package/data/fortnite/characters/royalebomber.json +38 -0
  175. package/data/fortnite/characters/ruckus.json +42 -0
  176. package/data/fortnite/characters/rust.json +41 -0
  177. package/data/fortnite/characters/sakuracircle.json +36 -0
  178. package/data/fortnite/characters/sandystrip.json +44 -0
  179. package/data/fortnite/characters/saskia.json +45 -0
  180. package/data/fortnite/characters/seaportcity.json +48 -0
  181. package/data/fortnite/characters/shakensanctuary.json +42 -0
  182. package/data/fortnite/characters/shimmerdusk.json +41 -0
  183. package/data/fortnite/characters/shinyshafts.json +38 -0
  184. package/data/fortnite/characters/shogunssolitude.json +46 -0
  185. package/data/fortnite/characters/shuri.json +39 -0
  186. package/data/fortnite/characters/silashesk.json +42 -0
  187. package/data/fortnite/characters/slurproom.json +40 -0
  188. package/data/fortnite/characters/snowheart.json +44 -0
  189. package/data/fortnite/characters/snowstriker.json +43 -0
  190. package/data/fortnite/characters/solidsnake.json +39 -0
  191. package/data/fortnite/characters/sonor.json +46 -0
  192. package/data/fortnite/characters/spartanassassin.json +43 -0
  193. package/data/fortnite/characters/spirithuntersaeko.json +41 -0
  194. package/data/fortnite/characters/springfieldnationalparkstore.json +38 -0
  195. package/data/fortnite/characters/springfieldnuclearpowerplant.json +39 -0
  196. package/data/fortnite/characters/springfieldslurpworks.json +40 -0
  197. package/data/fortnite/characters/squibblyshores.json +42 -0
  198. package/data/fortnite/characters/squibblyspointlighthouse.json +37 -0
  199. package/data/fortnite/characters/starroakley.json +45 -0
  200. package/data/fortnite/characters/steelsight.json +43 -0
  201. package/data/fortnite/characters/sticks.json +35 -0
  202. package/data/fortnite/characters/sunkenshores.json +42 -0
  203. package/data/fortnite/characters/sunstrider.json +42 -0
  204. package/data/fortnite/characters/suntanspecialist.json +43 -0
  205. package/data/fortnite/characters/supernovaacademy.json +27 -0
  206. package/data/fortnite/characters/t60powerarmor.json +39 -0
  207. package/data/fortnite/characters/thebattlewoods.json +44 -0
  208. package/data/fortnite/characters/thegreatturtle.json +37 -0
  209. package/data/fortnite/characters/theoperator.json +42 -0
  210. package/data/fortnite/characters/thequackaway.json +39 -0
  211. package/data/fortnite/characters/thestabilizer.json +38 -0
  212. package/data/fortnite/characters/theunderwriter.json +36 -0
  213. package/data/fortnite/characters/toyboxdocks.json +40 -0
  214. package/data/fortnite/characters/trace.json +43 -0
  215. package/data/fortnite/characters/triagetrooper.json +43 -0
  216. package/data/fortnite/characters/triggerfish.json +44 -0
  217. package/data/fortnite/characters/trogscave.json +38 -0
  218. package/data/fortnite/characters/utopiacity.json +27 -0
  219. package/data/fortnite/characters/vengeancejones.json +42 -0
  220. package/data/fortnite/characters/victoriasaint.json +43 -0
  221. package/data/fortnite/characters/vineyshafts.json +35 -0
  222. package/data/fortnite/characters/volpez.json +41 -0
  223. package/data/fortnite/characters/warmachine.json +40 -0
  224. package/data/fortnite/characters/wastelandermagneto.json +40 -0
  225. package/data/fortnite/characters/wonkeeland.json +41 -0
  226. package/data/fortnite/meta.json +14 -0
  227. package/data/game-codes/.cache-manifest.json +148 -0
  228. package/data/game-codes/directory.json +1196 -0
  229. package/data/game-codes/games/arknights-endfield.json +958 -0
  230. package/data/game-codes/games/borderlands-1.json +414 -0
  231. package/data/game-codes/games/borderlands-2.json +340 -0
  232. package/data/game-codes/games/borderlands-3.json +1025 -0
  233. package/data/game-codes/games/borderlands-4.json +3128 -0
  234. package/data/game-codes/games/borderlands-the-pre-sequel.json +599 -0
  235. package/data/game-codes/games/brawl-stars.json +4766 -0
  236. package/data/game-codes/games/clash-of-clans.json +4766 -0
  237. package/data/game-codes/games/clash-royale.json +5 -0
  238. package/data/game-codes/games/cookie-run-kingdom.json +5366 -0
  239. package/data/game-codes/games/fortnite.json +897 -0
  240. package/data/game-codes/games/genshin-impact.json +8520 -0
  241. package/data/game-codes/games/honkai-impact-3rd.json +405 -0
  242. package/data/game-codes/games/honkai-star-rail.json +9863 -0
  243. package/data/game-codes/games/neverness-to-everness.json +1695 -0
  244. package/data/game-codes/games/roblox-doors.json +1842 -0
  245. package/data/game-codes/games/roblox-rivals.json +2919 -0
  246. package/data/game-codes/games/tower-of-fantasy.json +2839 -0
  247. package/data/game-codes/games/warframe.json +20824 -0
  248. package/data/game-codes/games/wuthering-waves.json +2477 -0
  249. package/data/game-codes/games/zenless-zone-zero.json +3681 -0
  250. package/data/game-codes/status.json +580 -0
  251. package/data/genshinimpact/characters/aino.json +34 -0
  252. package/data/genshinimpact/characters/albedo.json +10 -9
  253. package/data/genshinimpact/characters/alhaitham.json +11 -10
  254. package/data/genshinimpact/characters/aloy.json +10 -9
  255. package/data/genshinimpact/characters/alyosha.json +34 -0
  256. package/data/genshinimpact/characters/amber.json +11 -10
  257. package/data/genshinimpact/characters/aratakiitto.json +11 -10
  258. package/data/genshinimpact/characters/arlecchino.json +11 -10
  259. package/data/genshinimpact/characters/baizhu.json +11 -10
  260. package/data/genshinimpact/characters/barbara.json +10 -9
  261. package/data/genshinimpact/characters/beidou.json +11 -10
  262. package/data/genshinimpact/characters/bennett.json +10 -9
  263. package/data/genshinimpact/characters/candace.json +11 -10
  264. package/data/genshinimpact/characters/charlotte.json +10 -9
  265. package/data/genshinimpact/characters/chasca.json +10 -9
  266. package/data/genshinimpact/characters/chevreuse.json +10 -9
  267. package/data/genshinimpact/characters/chiori.json +10 -9
  268. package/data/genshinimpact/characters/chongyun.json +11 -10
  269. package/data/genshinimpact/characters/citlali.json +10 -9
  270. package/data/genshinimpact/characters/clorinde.json +10 -9
  271. package/data/genshinimpact/characters/collei.json +11 -10
  272. package/data/genshinimpact/characters/columbina.json +34 -0
  273. package/data/genshinimpact/characters/cyno.json +11 -10
  274. package/data/genshinimpact/characters/dahlia.json +9 -8
  275. package/data/genshinimpact/characters/dehya.json +10 -9
  276. package/data/genshinimpact/characters/diluc.json +10 -9
  277. package/data/genshinimpact/characters/diona.json +10 -9
  278. package/data/genshinimpact/characters/dori.json +10 -9
  279. package/data/genshinimpact/characters/durin.json +10 -9
  280. package/data/genshinimpact/characters/emilie.json +10 -9
  281. package/data/genshinimpact/characters/escoffier.json +11 -10
  282. package/data/genshinimpact/characters/eula.json +10 -9
  283. package/data/genshinimpact/characters/faruzan.json +11 -10
  284. package/data/genshinimpact/characters/fischl.json +11 -10
  285. package/data/genshinimpact/characters/flins.json +34 -0
  286. package/data/genshinimpact/characters/freminet.json +10 -9
  287. package/data/genshinimpact/characters/furina.json +11 -10
  288. package/data/genshinimpact/characters/gaming.json +10 -9
  289. package/data/genshinimpact/characters/ganyu.json +10 -9
  290. package/data/genshinimpact/characters/gorou.json +11 -10
  291. package/data/genshinimpact/characters/hutao.json +11 -10
  292. package/data/genshinimpact/characters/iansan.json +10 -9
  293. package/data/genshinimpact/characters/ifa.json +34 -0
  294. package/data/genshinimpact/characters/illuga.json +34 -0
  295. package/data/genshinimpact/characters/ineffa.json +10 -9
  296. package/data/genshinimpact/characters/jahoda.json +34 -0
  297. package/data/genshinimpact/characters/jean.json +10 -9
  298. package/data/genshinimpact/characters/kachina.json +10 -9
  299. package/data/genshinimpact/characters/kaedeharakazuha.json +11 -10
  300. package/data/genshinimpact/characters/kaeya.json +10 -9
  301. package/data/genshinimpact/characters/kamisatoayaka.json +10 -9
  302. package/data/genshinimpact/characters/kamisatoayato.json +10 -9
  303. package/data/genshinimpact/characters/kaveh.json +11 -10
  304. package/data/genshinimpact/characters/keqing.json +10 -9
  305. package/data/genshinimpact/characters/kinich.json +10 -9
  306. package/data/genshinimpact/characters/kirara.json +10 -9
  307. package/data/genshinimpact/characters/klee.json +10 -9
  308. package/data/genshinimpact/characters/kujousara.json +10 -9
  309. package/data/genshinimpact/characters/kukishinobu.json +11 -10
  310. package/data/genshinimpact/characters/lanyan.json +10 -9
  311. package/data/genshinimpact/characters/lauma.json +34 -0
  312. package/data/genshinimpact/characters/layla.json +10 -9
  313. package/data/genshinimpact/characters/linnea.json +34 -0
  314. package/data/genshinimpact/characters/lisa.json +11 -10
  315. package/data/genshinimpact/characters/lohen.json +10 -9
  316. package/data/genshinimpact/characters/lynette.json +10 -9
  317. package/data/genshinimpact/characters/lyney.json +10 -9
  318. package/data/genshinimpact/characters/mavuika.json +10 -9
  319. package/data/genshinimpact/characters/mika.json +34 -0
  320. package/data/genshinimpact/characters/mikaschmidt.json +10 -9
  321. package/data/genshinimpact/characters/mona.json +11 -10
  322. package/data/genshinimpact/characters/mualani.json +10 -9
  323. package/data/genshinimpact/characters/nahida.json +11 -10
  324. package/data/genshinimpact/characters/navia.json +10 -9
  325. package/data/genshinimpact/characters/nefer.json +34 -0
  326. package/data/genshinimpact/characters/neuvillette.json +10 -9
  327. package/data/genshinimpact/characters/nicole.json +34 -0
  328. package/data/genshinimpact/characters/nilou.json +10 -9
  329. package/data/genshinimpact/characters/ningguang.json +10 -9
  330. package/data/genshinimpact/characters/noelle.json +11 -10
  331. package/data/genshinimpact/characters/odette.json +34 -0
  332. package/data/genshinimpact/characters/ororon.json +10 -9
  333. package/data/genshinimpact/characters/prune.json +9 -8
  334. package/data/genshinimpact/characters/qiqi.json +11 -10
  335. package/data/genshinimpact/characters/raidenshogun.json +11 -10
  336. package/data/genshinimpact/characters/razor.json +10 -9
  337. package/data/genshinimpact/characters/rosaria.json +11 -10
  338. package/data/genshinimpact/characters/sandrone.json +34 -0
  339. package/data/genshinimpact/characters/sangonomiyakokomi.json +11 -10
  340. package/data/genshinimpact/characters/sayu.json +10 -9
  341. package/data/genshinimpact/characters/sethos.json +10 -9
  342. package/data/genshinimpact/characters/shenhe.json +11 -10
  343. package/data/genshinimpact/characters/shikanoinheizou.json +11 -10
  344. package/data/genshinimpact/characters/sigewinne.json +10 -9
  345. package/data/genshinimpact/characters/skirk.json +10 -9
  346. package/data/genshinimpact/characters/sucrose.json +11 -10
  347. package/data/genshinimpact/characters/tartaglia.json +34 -0
  348. package/data/genshinimpact/characters/thoma.json +10 -9
  349. package/data/genshinimpact/characters/tighnari.json +11 -10
  350. package/data/genshinimpact/characters/traveler.json +34 -0
  351. package/data/genshinimpact/characters/varesa.json +10 -9
  352. package/data/genshinimpact/characters/varka.json +9 -8
  353. package/data/genshinimpact/characters/venti.json +11 -10
  354. package/data/genshinimpact/characters/wanderer.json +10 -9
  355. package/data/genshinimpact/characters/wonderlandmanekin.json +34 -0
  356. package/data/genshinimpact/characters/wriothesley.json +11 -10
  357. package/data/genshinimpact/characters/xiangling.json +11 -10
  358. package/data/genshinimpact/characters/xianyun.json +10 -9
  359. package/data/genshinimpact/characters/xiao.json +10 -9
  360. package/data/genshinimpact/characters/xilonen.json +10 -9
  361. package/data/genshinimpact/characters/xingqiu.json +11 -10
  362. package/data/genshinimpact/characters/xinyan.json +11 -10
  363. package/data/genshinimpact/characters/yaemiko.json +11 -10
  364. package/data/genshinimpact/characters/yanfei.json +11 -10
  365. package/data/genshinimpact/characters/yaoyao.json +11 -10
  366. package/data/genshinimpact/characters/yelan.json +10 -9
  367. package/data/genshinimpact/characters/yoimiya.json +11 -10
  368. package/data/genshinimpact/characters/yumemizukimizuki.json +10 -9
  369. package/data/genshinimpact/characters/yunjin.json +10 -9
  370. package/data/genshinimpact/characters/zhongli.json +10 -9
  371. package/data/genshinimpact/characters/zibai.json +10 -9
  372. package/data/genshinimpact/meta.json +12 -12
  373. package/data/honkaistarrail/characters/acheron.json +11 -10
  374. package/data/honkaistarrail/characters/aglaea.json +11 -10
  375. package/data/honkaistarrail/characters/anaxa.json +11 -10
  376. package/data/honkaistarrail/characters/archer.json +29 -0
  377. package/data/honkaistarrail/characters/argenti.json +11 -10
  378. package/data/honkaistarrail/characters/arlan.json +11 -10
  379. package/data/honkaistarrail/characters/ashveil.json +11 -10
  380. package/data/honkaistarrail/characters/asta.json +11 -10
  381. package/data/honkaistarrail/characters/aventurine.json +11 -10
  382. package/data/honkaistarrail/characters/bailu.json +11 -10
  383. package/data/honkaistarrail/characters/blackswan.json +11 -10
  384. package/data/honkaistarrail/characters/blade.json +11 -10
  385. package/data/honkaistarrail/characters/boothill.json +11 -10
  386. package/data/honkaistarrail/characters/bronya.json +11 -10
  387. package/data/honkaistarrail/characters/castorice.json +11 -10
  388. package/data/honkaistarrail/characters/cerydra.json +11 -10
  389. package/data/honkaistarrail/characters/cipher.json +11 -10
  390. package/data/honkaistarrail/characters/clara.json +11 -10
  391. package/data/honkaistarrail/characters/constance.json +11 -10
  392. package/data/honkaistarrail/characters/cyrene.json +11 -10
  393. package/data/honkaistarrail/characters/danheng.json +11 -10
  394. package/data/honkaistarrail/characters/danhengimbibitorlunae.json +29 -0
  395. package/data/honkaistarrail/characters/danhengpermansorterrae.json +29 -0
  396. package/data/honkaistarrail/characters/drratio.json +11 -10
  397. package/data/honkaistarrail/characters/evanescia.json +29 -0
  398. package/data/honkaistarrail/characters/evernight.json +29 -0
  399. package/data/honkaistarrail/characters/feixiao.json +11 -10
  400. package/data/honkaistarrail/characters/firefly.json +11 -10
  401. package/data/honkaistarrail/characters/fugue.json +29 -0
  402. package/data/honkaistarrail/characters/fuxuan.json +11 -10
  403. package/data/honkaistarrail/characters/gallagher.json +11 -10
  404. package/data/honkaistarrail/characters/gepard.json +11 -10
  405. package/data/honkaistarrail/characters/gilgamesh.json +29 -0
  406. package/data/honkaistarrail/characters/guinaifen.json +11 -10
  407. package/data/honkaistarrail/characters/hanya.json +11 -10
  408. package/data/honkaistarrail/characters/herta.json +11 -10
  409. package/data/honkaistarrail/characters/himeko.json +12 -11
  410. package/data/honkaistarrail/characters/himekonova.json +29 -0
  411. package/data/honkaistarrail/characters/hook.json +11 -10
  412. package/data/honkaistarrail/characters/huohuo.json +11 -10
  413. package/data/honkaistarrail/characters/hyacine.json +11 -10
  414. package/data/honkaistarrail/characters/hysilens.json +11 -10
  415. package/data/honkaistarrail/characters/jade.json +11 -10
  416. package/data/honkaistarrail/characters/jiaoqiu.json +11 -10
  417. package/data/honkaistarrail/characters/jingliu.json +11 -10
  418. package/data/honkaistarrail/characters/jingyuan.json +11 -10
  419. package/data/honkaistarrail/characters/kafka.json +11 -10
  420. package/data/honkaistarrail/characters/lingsha.json +11 -10
  421. package/data/honkaistarrail/characters/lisushang.json +11 -10
  422. package/data/honkaistarrail/characters/luka.json +11 -10
  423. package/data/honkaistarrail/characters/luocha.json +11 -10
  424. package/data/honkaistarrail/characters/lynx.json +11 -10
  425. package/data/honkaistarrail/characters/march7th.json +11 -10
  426. package/data/honkaistarrail/characters/misha.json +11 -10
  427. package/data/honkaistarrail/characters/mortenaxblade.json +29 -0
  428. package/data/honkaistarrail/characters/moze.json +11 -10
  429. package/data/honkaistarrail/characters/mydei.json +11 -10
  430. package/data/honkaistarrail/characters/natasha.json +11 -10
  431. package/data/honkaistarrail/characters/pela.json +11 -10
  432. package/data/honkaistarrail/characters/phainon.json +11 -10
  433. package/data/honkaistarrail/characters/qingque.json +11 -10
  434. package/data/honkaistarrail/characters/rappa.json +11 -10
  435. package/data/honkaistarrail/characters/rintohsaka.json +29 -0
  436. package/data/honkaistarrail/characters/robin.json +11 -10
  437. package/data/honkaistarrail/characters/ruanmei.json +11 -10
  438. package/data/honkaistarrail/characters/saber.json +29 -0
  439. package/data/honkaistarrail/characters/sampo.json +11 -10
  440. package/data/honkaistarrail/characters/seele.json +11 -10
  441. package/data/honkaistarrail/characters/serval.json +11 -10
  442. package/data/honkaistarrail/characters/silverwolf.json +11 -10
  443. package/data/honkaistarrail/characters/silverwolflv999.json +29 -0
  444. package/data/honkaistarrail/characters/sparkle.json +11 -10
  445. package/data/honkaistarrail/characters/sparxie.json +29 -0
  446. package/data/honkaistarrail/characters/sunday.json +29 -0
  447. package/data/honkaistarrail/characters/thedahlia.json +29 -0
  448. package/data/honkaistarrail/characters/theherta.json +29 -0
  449. package/data/honkaistarrail/characters/tingyun.json +11 -10
  450. package/data/honkaistarrail/characters/topaz.json +11 -10
  451. package/data/honkaistarrail/characters/topaznumby.json +29 -0
  452. package/data/honkaistarrail/characters/trailblazer.json +29 -0
  453. package/data/honkaistarrail/characters/tribbie.json +11 -10
  454. package/data/honkaistarrail/characters/welt.json +29 -0
  455. package/data/honkaistarrail/characters/weltyang.json +11 -10
  456. package/data/honkaistarrail/characters/xueyi.json +11 -10
  457. package/data/honkaistarrail/characters/yanqing.json +11 -10
  458. package/data/honkaistarrail/characters/yaoguang.json +11 -10
  459. package/data/honkaistarrail/characters/yukong.json +11 -10
  460. package/data/honkaistarrail/characters/yunli.json +11 -10
  461. package/data/honkaistarrail/meta.json +12 -12
  462. package/data/nte/characters/adler.json +13 -12
  463. package/data/nte/characters/aurelia.json +13 -12
  464. package/data/nte/characters/baicang.json +13 -12
  465. package/data/nte/characters/chaos.json +13 -12
  466. package/data/nte/characters/chiz.json +13 -12
  467. package/data/nte/characters/daffodill.json +13 -12
  468. package/data/nte/characters/edgar.json +13 -12
  469. package/data/nte/characters/esperzero.json +13 -12
  470. package/data/nte/characters/fadia.json +13 -12
  471. package/data/nte/characters/haniel.json +13 -12
  472. package/data/nte/characters/hathor.json +13 -12
  473. package/data/nte/characters/hotori.json +13 -12
  474. package/data/nte/characters/iroi.json +13 -12
  475. package/data/nte/characters/jiuyuan.json +13 -12
  476. package/data/nte/characters/lacrimosa.json +13 -12
  477. package/data/nte/characters/mint.json +13 -12
  478. package/data/nte/characters/nanally.json +13 -12
  479. package/data/nte/characters/sakiri.json +13 -12
  480. package/data/nte/characters/shinku.json +13 -12
  481. package/data/nte/characters/skia.json +13 -12
  482. package/data/nte/characters/zankou.json +51 -0
  483. package/data/nte/meta.json +12 -12
  484. package/data/toweroffantasy/characters/alyss.json +27 -0
  485. package/data/toweroffantasy/characters/annabella.json +23 -0
  486. package/data/toweroffantasy/characters/asuka.json +23 -0
  487. package/data/toweroffantasy/characters/bailing.json +23 -0
  488. package/data/toweroffantasy/characters/brevey.json +23 -0
  489. package/data/toweroffantasy/characters/claudia.json +23 -0
  490. package/data/toweroffantasy/characters/cobaltb.json +23 -0
  491. package/data/toweroffantasy/characters/cocoritter.json +23 -0
  492. package/data/toweroffantasy/characters/crow.json +23 -0
  493. package/data/toweroffantasy/characters/echo.json +23 -0
  494. package/data/toweroffantasy/characters/ene.json +23 -0
  495. package/data/toweroffantasy/characters/feise.json +25 -0
  496. package/data/toweroffantasy/characters/fenrir.json +23 -0
  497. package/data/toweroffantasy/characters/fiona.json +25 -0
  498. package/data/toweroffantasy/characters/frigg.json +23 -0
  499. package/data/toweroffantasy/characters/gnonno.json +23 -0
  500. package/data/toweroffantasy/characters/hilda.json +23 -0
  501. package/data/toweroffantasy/characters/huang.json +25 -0
  502. package/data/toweroffantasy/characters/huma.json +25 -0
  503. package/data/toweroffantasy/characters/icarus.json +23 -0
  504. package/data/toweroffantasy/characters/jiyu.json +25 -0
  505. package/data/toweroffantasy/characters/king.json +23 -0
  506. package/data/toweroffantasy/characters/lan.json +25 -0
  507. package/data/toweroffantasy/characters/lin.json +23 -0
  508. package/data/toweroffantasy/characters/linghan.json +23 -0
  509. package/data/toweroffantasy/characters/liuhuo.json +23 -0
  510. package/data/toweroffantasy/characters/lyra.json +23 -0
  511. package/data/toweroffantasy/characters/meryl.json +23 -0
  512. package/data/toweroffantasy/characters/mingjing.json +27 -0
  513. package/data/toweroffantasy/characters/nanyin.json +25 -0
  514. package/data/toweroffantasy/characters/nemesis.json +25 -0
  515. package/data/toweroffantasy/characters/pepper.json +23 -0
  516. package/data/toweroffantasy/characters/plotti.json +25 -0
  517. package/data/toweroffantasy/characters/roslyn.json +23 -0
  518. package/data/toweroffantasy/characters/rubilia.json +25 -0
  519. package/data/toweroffantasy/characters/ruby.json +25 -0
  520. package/data/toweroffantasy/characters/sakifuwa.json +23 -0
  521. package/data/toweroffantasy/characters/samir.json +23 -0
  522. package/data/toweroffantasy/characters/shiro.json +25 -0
  523. package/data/toweroffantasy/characters/tianlang.json +25 -0
  524. package/data/toweroffantasy/characters/tsubasa.json +23 -0
  525. package/data/toweroffantasy/characters/umi.json +23 -0
  526. package/data/toweroffantasy/characters/yanmiao.json +25 -0
  527. package/data/toweroffantasy/characters/yanuo.json +23 -0
  528. package/data/toweroffantasy/characters/yulan.json +23 -0
  529. package/data/toweroffantasy/characters/zero.json +23 -0
  530. package/data/toweroffantasy/meta.json +14 -0
  531. package/data/vrchat/groups/clubzodiac.json +2 -1
  532. package/data/vrchat/groups/dirtydancers.json +2 -1
  533. package/data/vrchat/groups/drumsynews.json +2 -1
  534. package/data/vrchat/groups/ebola20.json +2 -1
  535. package/data/vrchat/groups/internationaldancersassociation.json +2 -1
  536. package/data/vrchat/groups/kannagang.json +2 -1
  537. package/data/vrchat/groups/lolisquad.json +2 -1
  538. package/data/vrchat/groups/nekonightsnightclub.json +2 -1
  539. package/data/vrchat/groups/nekostudios.json +2 -1
  540. package/data/vrchat/groups/nekosunecommunity.json +2 -1
  541. package/data/vrchat/groups/voidclub.json +2 -1
  542. package/data/vrchat/groups/voltdanceclub.json +2 -1
  543. package/data/vrchat/groups/vrceventsgroup.json +2 -1
  544. package/data/vrchat/groups/vrchatcommunitydiscord.json +2 -1
  545. package/data/vrchat/meta.json +11 -11
  546. package/data/vrchat/players/alto.json +8 -7
  547. package/data/vrchat/players/arcadum.json +9 -8
  548. package/data/vrchat/players/chairhandler.json +8 -7
  549. package/data/vrchat/players/chipz.json +8 -7
  550. package/data/vrchat/players/hikarivr.json +10 -9
  551. package/data/vrchat/players/kimple.json +9 -8
  552. package/data/vrchat/players/linkzr.json +7 -6
  553. package/data/vrchat/players/nagzz21.json +8 -7
  554. package/data/vrchat/players/nebride.json +9 -8
  555. package/data/vrchat/players/nekosunevr.json +10 -9
  556. package/data/vrchat/players/pokelawls.json +8 -7
  557. package/data/vrchat/players/qwonk.json +7 -6
  558. package/data/vrchat/players/roflgator.json +9 -8
  559. package/data/vrchat/players/scifri.json +6 -5
  560. package/data/vrchat/players/sodapoppin.json +8 -7
  561. package/data/vrchat/players/woops.json +8 -7
  562. package/data/vrchat/players/zentreya.json +9 -8
  563. package/data/vrchat/worlds/ascensionacademy.json +6 -5
  564. package/data/vrchat/worlds/clubvgl.json +7 -6
  565. package/data/vrchat/worlds/fightfinder.json +7 -6
  566. package/data/vrchat/worlds/hangouthouse.json +6 -5
  567. package/data/vrchat/worlds/nekostudioshalloween.json +8 -7
  568. package/data/vrchat/worlds/nekostudiosnewyear.json +8 -7
  569. package/data/vrchat/worlds/nekostudiosxmas.json +8 -7
  570. package/data/vrchat/worlds/nekosunevranimations2023.json +8 -7
  571. package/data/vrchat/worlds/nekosunevranimations20232024.json +10 -0
  572. package/data/vrchat/worlds/nekosunevranimations2024.json +8 -7
  573. package/data/vrchat/worlds/nekosunevranimations2025.json +8 -7
  574. package/data/vrchat/worlds/rumhamsavatarworld.json +6 -5
  575. package/data/vrchat/worlds/smewsranch.json +6 -5
  576. package/data/vrchat/worlds/smewsroom.json +6 -5
  577. package/data/vrchat/worlds/sunsetbar.json +6 -5
  578. package/data/vrchat/worlds/theblackcat.json +9 -8
  579. package/data/vrchat/worlds/thegreatpug.json +7 -6
  580. package/data/vrchat/worlds/themidnightbar.json +6 -5
  581. package/data/vrchat/worlds/voidclub.json +6 -5
  582. package/data/vrchat/worlds/voltdanceclub.json +6 -5
  583. package/data/vrchat/worlds/vrchathome.json +7 -6
  584. package/data/vrchat/worlds/whatalegend.json +5 -4
  585. package/data/warframe/characters/amesha.json +226 -0
  586. package/data/warframe/characters/ash.json +1354 -0
  587. package/data/warframe/characters/ashprime.json +1994 -0
  588. package/data/warframe/characters/atlas.json +916 -0
  589. package/data/warframe/characters/atlasprime.json +1632 -0
  590. package/data/warframe/characters/banshee.json +1036 -0
  591. package/data/warframe/characters/bansheeprime.json +1750 -0
  592. package/data/warframe/characters/baruuk.json +555 -0
  593. package/data/warframe/characters/baruukprime.json +1298 -0
  594. package/data/warframe/characters/bonewidow.json +354 -0
  595. package/data/warframe/characters/caliban.json +434 -0
  596. package/data/warframe/characters/calibanprime.json +683 -0
  597. package/data/warframe/characters/chroma.json +1344 -0
  598. package/data/warframe/characters/chromaprime.json +1977 -0
  599. package/data/warframe/characters/citrine.json +220 -0
  600. package/data/warframe/characters/cyte09.json +218 -0
  601. package/data/warframe/characters/dagath.json +194 -0
  602. package/data/warframe/characters/dante.json +223 -0
  603. package/data/warframe/characters/elytron.json +300 -0
  604. package/data/warframe/characters/ember.json +1209 -0
  605. package/data/warframe/characters/emberprime.json +1678 -0
  606. package/data/warframe/characters/equinox.json +958 -0
  607. package/data/warframe/characters/equinoxprime.json +1644 -0
  608. package/data/warframe/characters/excalibur.json +1603 -0
  609. package/data/warframe/characters/excaliburprime.json +1524 -0
  610. package/data/warframe/characters/excaliburumbra.json +397 -0
  611. package/data/warframe/characters/follie.json +246 -0
  612. package/data/warframe/characters/frost.json +1330 -0
  613. package/data/warframe/characters/frostprime.json +1686 -0
  614. package/data/warframe/characters/gara.json +925 -0
  615. package/data/warframe/characters/garaprime.json +1494 -0
  616. package/data/warframe/characters/garuda.json +851 -0
  617. package/data/warframe/characters/garudaprime.json +1449 -0
  618. package/data/warframe/characters/gauss.json +506 -0
  619. package/data/warframe/characters/gaussprime.json +1077 -0
  620. package/data/warframe/characters/grendel.json +463 -0
  621. package/data/warframe/characters/grendelprime.json +772 -0
  622. package/data/warframe/characters/gyre.json +381 -0
  623. package/data/warframe/characters/gyreprime.json +403 -0
  624. package/data/warframe/characters/harrow.json +916 -0
  625. package/data/warframe/characters/harrowprime.json +1700 -0
  626. package/data/warframe/characters/helminth.json +220 -0
  627. package/data/warframe/characters/hildryn.json +627 -0
  628. package/data/warframe/characters/hildrynprime.json +996 -0
  629. package/data/warframe/characters/hydroid.json +1028 -0
  630. package/data/warframe/characters/hydroidprime.json +1621 -0
  631. package/data/warframe/characters/inaros.json +976 -0
  632. package/data/warframe/characters/inarosprime.json +1721 -0
  633. package/data/warframe/characters/itzal.json +465 -0
  634. package/data/warframe/characters/ivara.json +1349 -0
  635. package/data/warframe/characters/ivaraprime.json +1921 -0
  636. package/data/warframe/characters/jade.json +240 -0
  637. package/data/warframe/characters/khora.json +804 -0
  638. package/data/warframe/characters/khoraprime.json +1403 -0
  639. package/data/warframe/characters/koumei.json +221 -0
  640. package/data/warframe/characters/kullervo.json +191 -0
  641. package/data/warframe/characters/lavos.json +511 -0
  642. package/data/warframe/characters/lavosprime.json +711 -0
  643. package/data/warframe/characters/limbo.json +1363 -0
  644. package/data/warframe/characters/limboprime.json +2084 -0
  645. package/data/warframe/characters/loki.json +1209 -0
  646. package/data/warframe/characters/lokiprime.json +1457 -0
  647. package/data/warframe/characters/mag.json +2089 -0
  648. package/data/warframe/characters/magprime.json +2532 -0
  649. package/data/warframe/characters/mesa.json +1195 -0
  650. package/data/warframe/characters/mesaprime.json +2059 -0
  651. package/data/warframe/characters/mirage.json +1561 -0
  652. package/data/warframe/characters/mirageprime.json +2257 -0
  653. package/data/warframe/characters/nekros.json +1235 -0
  654. package/data/warframe/characters/nekrosprime.json +1882 -0
  655. package/data/warframe/characters/nezha.json +868 -0
  656. package/data/warframe/characters/nezhaprime.json +1695 -0
  657. package/data/warframe/characters/nidus.json +1027 -0
  658. package/data/warframe/characters/nidusprime.json +1706 -0
  659. package/data/warframe/characters/nokko.json +308 -0
  660. package/data/warframe/characters/nova.json +1226 -0
  661. package/data/warframe/characters/novaprime.json +1667 -0
  662. package/data/warframe/characters/nyx.json +1610 -0
  663. package/data/warframe/characters/nyxprime.json +1967 -0
  664. package/data/warframe/characters/oberon.json +1249 -0
  665. package/data/warframe/characters/oberonprime.json +1872 -0
  666. package/data/warframe/characters/octavia.json +1049 -0
  667. package/data/warframe/characters/octaviaprime.json +1735 -0
  668. package/data/warframe/characters/odonata.json +336 -0
  669. package/data/warframe/characters/odonataprime.json +787 -0
  670. package/data/warframe/characters/oraxia.json +246 -0
  671. package/data/warframe/characters/orionsirius.json +134 -0
  672. package/data/warframe/characters/protea.json +521 -0
  673. package/data/warframe/characters/proteaprime.json +992 -0
  674. package/data/warframe/characters/qorvex.json +214 -0
  675. package/data/warframe/characters/revenant.json +931 -0
  676. package/data/warframe/characters/revenantprime.json +1494 -0
  677. package/data/warframe/characters/rhino.json +1344 -0
  678. package/data/warframe/characters/rhinoprime.json +1675 -0
  679. package/data/warframe/characters/saryn.json +1210 -0
  680. package/data/warframe/characters/sarynprime.json +2042 -0
  681. package/data/warframe/characters/sevagoth.json +478 -0
  682. package/data/warframe/characters/sevagothprime.json +910 -0
  683. package/data/warframe/characters/siriusorion.json +247 -0
  684. package/data/warframe/characters/styanax.json +320 -0
  685. package/data/warframe/characters/styanaxprime.json +321 -0
  686. package/data/warframe/characters/temple.json +271 -0
  687. package/data/warframe/characters/titania.json +1227 -0
  688. package/data/warframe/characters/titaniaprime.json +2000 -0
  689. package/data/warframe/characters/trinity.json +1097 -0
  690. package/data/warframe/characters/trinityprime.json +1849 -0
  691. package/data/warframe/characters/uriel.json +215 -0
  692. package/data/warframe/characters/valkyr.json +1165 -0
  693. package/data/warframe/characters/valkyrprime.json +1969 -0
  694. package/data/warframe/characters/vauban.json +1067 -0
  695. package/data/warframe/characters/vaubanprime.json +1614 -0
  696. package/data/warframe/characters/voidrig.json +355 -0
  697. package/data/warframe/characters/volt.json +1324 -0
  698. package/data/warframe/characters/voltprime.json +1843 -0
  699. package/data/warframe/characters/voruna.json +293 -0
  700. package/data/warframe/characters/vorunaprime.json +431 -0
  701. package/data/warframe/characters/wisp.json +824 -0
  702. package/data/warframe/characters/wispprime.json +908 -0
  703. package/data/warframe/characters/wukong.json +927 -0
  704. package/data/warframe/characters/wukongprime.json +1754 -0
  705. package/data/warframe/characters/xaku.json +620 -0
  706. package/data/warframe/characters/xakuprime.json +879 -0
  707. package/data/warframe/characters/yareli.json +409 -0
  708. package/data/warframe/characters/yareliprime.json +683 -0
  709. package/data/warframe/characters/zephyr.json +1149 -0
  710. package/data/warframe/characters/zephyrprime.json +1864 -0
  711. package/data/warframe/meta.json +14 -0
  712. package/data/wutheringwaves/meta.json +14 -0
  713. package/data/zenlesszonezero/meta.json +14 -0
  714. package/gamecodes.js +204 -0
  715. package/index.js +36 -17
  716. package/package.json +33 -3
  717. package/scripts/cache-sync-smoke.js +139 -0
  718. package/scripts/game-code-cache-smoke.js +125 -0
  719. package/scripts/sync-all-cache.js +18 -0
  720. package/scripts/sync-api-cache.js +368 -0
  721. package/scripts/sync-game-code-cache.js +283 -0
  722. package/scripts/sync-gaming-info-cache.js +46 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Warframe",
3
+ "title": "Cooperative science-fiction action game by Digital Extremes.",
4
+ "engine": "Evolution",
5
+ "Platform": "Windows, PlayStation, Xbox, Nintendo Switch, iOS, Android",
6
+ "Release date": "March 25, 2013",
7
+ "Genre": "Action RPG, Third-person shooter, Online co-op",
8
+ "developers": "Digital Extremes",
9
+ "publisher": "Digital Extremes",
10
+ "aliases": "WF, Warframe",
11
+ "image": "https://www.warframe.com/favicon.ico",
12
+ "description": "Online action game built around collectible Warframes, weapons, companions and the Origin System.",
13
+ "url": "https://www.warframe.com/"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Wuthering Waves",
3
+ "title": "Open-world action RPG by Kuro Games.",
4
+ "engine": "Unreal Engine 4",
5
+ "Platform": "Windows, PlayStation 5, iOS, Android, macOS",
6
+ "Release date": "May 22, 2024",
7
+ "Genre": "Action RPG, Open World, Gacha",
8
+ "developers": "Kuro Games",
9
+ "publisher": "Kuro Games",
10
+ "aliases": "WuWa, Wuthering Waves",
11
+ "image": "https://wutheringwaves.kurogames.com/favicon.ico",
12
+ "description": "Open-world action RPG featuring playable Resonators with distinct attributes and weapons.",
13
+ "url": "https://wutheringwaves.kurogames.com/"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Zenless Zone Zero",
3
+ "title": "Urban fantasy action RPG by HoYoverse.",
4
+ "engine": "Unity",
5
+ "Platform": "Windows, PlayStation 5, Xbox Series X/S, iOS, Android",
6
+ "Release date": "July 4, 2024",
7
+ "Genre": "Action RPG, Urban Fantasy, Gacha",
8
+ "developers": "miHoYo",
9
+ "publisher": "HoYoverse",
10
+ "aliases": "ZZZ, Zenless Zone Zero",
11
+ "image": "https://zenless.hoyoverse.com/favicon.ico",
12
+ "description": "Action RPG featuring playable Agents organized by faction, attribute and specialty.",
13
+ "url": "https://zenless.hoyoverse.com/"
14
+ }
package/gamecodes.js ADDED
@@ -0,0 +1,204 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const DATA_DIR = path.join(__dirname, 'data', 'game-codes');
7
+ const DEFAULT_API_BASE = 'https://api.nekosunevr.co.uk';
8
+ const DEFAULT_API_PATH = '/v5/games/api/codes';
9
+ const DEFAULT_TIMEOUT_MS = 4000;
10
+
11
+ function apiBase() {
12
+ return String(process.env.GAMING_INFOS_API_BASE || DEFAULT_API_BASE).replace(/\/+$/, '');
13
+ }
14
+
15
+ function apiPath() {
16
+ const raw = String(process.env.GAME_CODES_API_PATH || DEFAULT_API_PATH).trim();
17
+ return `/${raw.replace(/^\/+|\/+$/g, '')}`;
18
+ }
19
+
20
+ function apiDisabled() {
21
+ const raw = String(process.env.GAMING_INFOS_DISABLE_API || '').toLowerCase();
22
+ return raw === '1' || raw === 'true';
23
+ }
24
+
25
+ function timeoutMs() {
26
+ const raw = Number(process.env.GAMING_INFOS_TIMEOUT_MS);
27
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_TIMEOUT_MS;
28
+ }
29
+
30
+ async function apiGet(suffix = '', query = {}) {
31
+ if (apiDisabled() || typeof fetch !== 'function') return null;
32
+
33
+ const url = new URL(`${apiBase()}${apiPath()}${suffix}`);
34
+ for (const [key, value] of Object.entries(query)) {
35
+ if (value !== undefined && value !== null && value !== '') url.searchParams.set(key, String(value));
36
+ }
37
+
38
+ const controller = new AbortController();
39
+ const timer = setTimeout(() => controller.abort(), timeoutMs());
40
+ try {
41
+ const response = await fetch(url, { signal: controller.signal, headers: { Accept: 'application/json' } });
42
+ if (!response.ok) return null;
43
+ const body = await response.json();
44
+ if (!body || body.success === false) return null;
45
+ return body;
46
+ } catch (_) {
47
+ return null;
48
+ } finally {
49
+ clearTimeout(timer);
50
+ }
51
+ }
52
+
53
+ async function readJson(filePath) {
54
+ return JSON.parse(await fs.promises.readFile(filePath, 'utf8'));
55
+ }
56
+
57
+ function safeFallbackPath(fileName) {
58
+ const filePath = path.resolve(DATA_DIR, fileName);
59
+ const root = `${path.resolve(DATA_DIR)}${path.sep}`;
60
+ return filePath.startsWith(root) ? filePath : null;
61
+ }
62
+
63
+ async function readFallback(fileName) {
64
+ const filePath = safeFallbackPath(fileName);
65
+ if (!filePath || !fs.existsSync(filePath)) return null;
66
+ try {
67
+ return await readJson(filePath);
68
+ } catch (_) {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ function normalizeGame(game) {
74
+ const slug = String(game || '').trim().toLowerCase();
75
+ return /^[a-z0-9][a-z0-9-]*$/.test(slug) ? slug : null;
76
+ }
77
+
78
+ function normalizeCode(code) {
79
+ return String(code || '').trim().toLowerCase();
80
+ }
81
+
82
+ async function resolveCachedGame(game) {
83
+ const slug = normalizeGame(game);
84
+ if (!slug) return null;
85
+ if (await readFallback(path.join('games', `${slug}.json`))) return slug;
86
+
87
+ const cachedDirectory = await readFallback('directory.json');
88
+ const aliasTarget = cachedDirectory?.aliases?.[slug];
89
+ const canonical = normalizeGame(aliasTarget);
90
+ if (canonical && await readFallback(path.join('games', `${canonical}.json`))) return canonical;
91
+ return slug;
92
+ }
93
+
94
+ function hydrateCachedExpiry(entry) {
95
+ if (!entry || typeof entry !== 'object') return entry;
96
+ const output = { ...entry };
97
+ if (!entry.expiry || typeof entry.expiry !== 'object' || Array.isArray(entry.expiry)) return output;
98
+
99
+ const expiry = { ...entry.expiry };
100
+ const expiresAtMs = expiry.expiresAt ? new Date(expiry.expiresAt).getTime() : NaN;
101
+ if (!Number.isNaN(expiresAtMs)) {
102
+ const remainingSeconds = Math.max(0, Math.floor((expiresAtMs - Date.now()) / 1000));
103
+ expiry.expiresInSeconds = remainingSeconds;
104
+ expiry.expired = expiresAtMs <= Date.now();
105
+ expiry.countdown = {
106
+ days: Math.floor(remainingSeconds / 86400),
107
+ hours: Math.floor((remainingSeconds % 86400) / 3600),
108
+ minutes: Math.floor((remainingSeconds % 3600) / 60),
109
+ seconds: remainingSeconds % 60
110
+ };
111
+ } else {
112
+ expiry.expiresInSeconds = null;
113
+ expiry.countdown = null;
114
+ }
115
+ output.expiry = expiry;
116
+ return output;
117
+ }
118
+
119
+ function filterCachedList(data, options = {}) {
120
+ if (!data || typeof data !== 'object') return data;
121
+ const includeUnknown = Boolean(options.includeUnknown);
122
+ const category = options.category ? String(options.category).toLowerCase() : null;
123
+ const claimType = options.claimType ? String(options.claimType).toLowerCase() : null;
124
+
125
+ const matches = (entry) => {
126
+ if (!entry || typeof entry !== 'object') return false;
127
+ if (category && String(entry.category || '').toLowerCase() !== category) return false;
128
+ if (claimType && String(entry.claimType || '').toLowerCase() !== claimType) return false;
129
+ return true;
130
+ };
131
+
132
+ const hydrate = (rows) => Array.isArray(rows) ? rows.filter(matches).map(hydrateCachedExpiry) : [];
133
+ const result = {
134
+ Active: hydrate(data.Active),
135
+ Expired: hydrate(data.Expired)
136
+ };
137
+ if (includeUnknown) result.Unknown = hydrate(data.Unknown);
138
+ return result;
139
+ }
140
+
141
+ async function directory() {
142
+ const live = await apiGet('');
143
+ if (live) return live;
144
+ const cached = await readFallback('directory.json');
145
+ return cached || { success: false, error: 'Game code directory is unavailable and no fallback cache exists.' };
146
+ }
147
+
148
+ async function status() {
149
+ const live = await apiGet('/status');
150
+ if (live) return live.data !== undefined ? live.data : live;
151
+ const cached = await readFallback('status.json');
152
+ return cached || { error: 'Game code status is unavailable and no fallback cache exists.' };
153
+ }
154
+
155
+ async function gameCodes(game, options = {}) {
156
+ if (!game) return { error: 'game is required' };
157
+ const requested = normalizeGame(game);
158
+ if (!requested) return { error: 'invalid game slug' };
159
+ const query = {
160
+ timezone: options.timezone,
161
+ category: options.category,
162
+ claimType: options.claimType,
163
+ includeUnknown: options.includeUnknown ? 'true' : undefined
164
+ };
165
+ const live = await apiGet(`/${encodeURIComponent(requested)}`, query);
166
+ if (live) return live;
167
+
168
+ const cachedGame = await resolveCachedGame(requested);
169
+ const cached = cachedGame ? await readFallback(path.join('games', `${cachedGame}.json`)) : null;
170
+ if (!cached) return { error: `No cached game codes found for "${game}".` };
171
+ return filterCachedList(cached, options);
172
+ }
173
+
174
+ async function gameCode(game, code, options = {}) {
175
+ if (!game || !code) return { error: 'game and code are both required' };
176
+ const requested = normalizeGame(game);
177
+ if (!requested) return { error: 'invalid game slug' };
178
+ const codeValue = String(code).trim();
179
+ const live = await apiGet(`/${encodeURIComponent(requested)}/${encodeURIComponent(codeValue)}`, { timezone: options.timezone });
180
+ if (live) return live.data !== undefined ? live.data : live;
181
+
182
+ const cachedGame = await resolveCachedGame(requested);
183
+ const cached = cachedGame ? await readFallback(path.join('games', `${cachedGame}.json`)) : null;
184
+ if (!cached) return { error: `No cached game codes found for "${game}".` };
185
+ const wanted = normalizeCode(codeValue);
186
+ for (const bucket of ['Active', 'Expired', 'Unknown']) {
187
+ for (const entry of Array.isArray(cached[bucket]) ? cached[bucket] : []) {
188
+ if (normalizeCode(entry?.code) === wanted) return hydrateCachedExpiry(entry);
189
+ }
190
+ }
191
+ return { error: `Code "${code}" not found for game "${game}".` };
192
+ }
193
+
194
+ async function cacheInfo() {
195
+ return readFallback('.cache-manifest.json');
196
+ }
197
+
198
+ module.exports = {
199
+ Codes: () => directory(),
200
+ CodesStatus: () => status(),
201
+ GameCodes: (game, options) => gameCodes(game, options),
202
+ GameCode: (game, code, options) => gameCode(game, code, options),
203
+ CodeCacheInfo: () => cacheInfo()
204
+ };
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const gameCodes = require('./gamecodes');
3
4
 
4
5
  const DATA_DIR = path.join(__dirname, 'data');
5
6
  const DEFAULT_API_BASE = 'https://api.nekosunevr.co.uk';
@@ -23,9 +24,15 @@ function timeoutMs() {
23
24
  return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_TIMEOUT_MS;
24
25
  }
25
26
 
27
+ function withLocalSlug(filePath, data) {
28
+ if (!data || Array.isArray(data) || typeof data !== 'object' || data.slug) return data;
29
+ return { ...data, slug: path.basename(filePath, '.json').toLowerCase() };
30
+ }
31
+
26
32
  // Live lookups go through the NekoSuneVR V5 API first so data can be updated
27
33
  // in real time without republishing this package. The bundled JSON under
28
- // data/ is only the offline fallback for when the API can't be reached.
34
+ // data/ is the offline fallback and is mirrored from APINODE by the hourly
35
+ // change-watch workflow.
29
36
  async function fetchFromApi(game, type, name) {
30
37
  if (apiDisabled() || typeof fetch !== 'function') return null;
31
38
 
@@ -53,7 +60,7 @@ async function loadLocal(game, type, name) {
53
60
  }
54
61
 
55
62
  const raw = await fs.promises.readFile(filePath, 'utf8');
56
- return JSON.parse(raw);
63
+ return withLocalSlug(filePath, JSON.parse(raw));
57
64
  }
58
65
 
59
66
  async function fetchListFromApi(game, type) {
@@ -81,12 +88,12 @@ async function listLocal(game, type) {
81
88
  const dir = path.join(DATA_DIR, game.toLowerCase(), type);
82
89
  if (!fs.existsSync(dir)) return [];
83
90
 
84
- const files = await fs.promises.readdir(dir);
91
+ const files = (await fs.promises.readdir(dir)).filter((file) => file.endsWith('.json')).sort((a, b) => a.localeCompare(b));
85
92
  const items = [];
86
93
  for (const file of files) {
87
- if (!file.endsWith('.json')) continue;
88
- const raw = await fs.promises.readFile(path.join(dir, file), 'utf8');
89
- items.push(JSON.parse(raw));
94
+ const filePath = path.join(dir, file);
95
+ const raw = await fs.promises.readFile(filePath, 'utf8');
96
+ items.push(withLocalSlug(filePath, JSON.parse(raw)));
90
97
  }
91
98
  return items;
92
99
  }
@@ -131,7 +138,7 @@ async function listGamesLocal() {
131
138
  const entries = await fs.promises.readdir(DATA_DIR, { withFileTypes: true });
132
139
  const games = [];
133
140
  for (const entry of entries) {
134
- if (!entry.isDirectory()) continue;
141
+ if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'game-codes') continue;
135
142
  const metaPath = path.join(DATA_DIR, entry.name, 'meta.json');
136
143
  if (!fs.existsSync(metaPath)) continue;
137
144
  const raw = await fs.promises.readFile(metaPath, 'utf8');
@@ -185,23 +192,35 @@ async function loadGameMeta(game) {
185
192
  }
186
193
  }
187
194
 
195
+ async function cacheInfo() {
196
+ try {
197
+ const filePath = path.join(DATA_DIR, '.cache-manifest.json');
198
+ if (!fs.existsSync(filePath)) return null;
199
+ return JSON.parse(await fs.promises.readFile(filePath, 'utf8'));
200
+ } catch (err) {
201
+ return { error: err.message };
202
+ }
203
+ }
204
+
188
205
  module.exports = {
189
- // All supported games, e.g. Games() -> [data/vrchat/meta.json, data/genshinimpact/meta.json, ...]
190
206
  Games: () => games(),
191
-
192
- // Game-level info, e.g. Game('vrchat') -> data/vrchat/meta.json
193
207
  Game: (game) => loadGameMeta(game),
194
-
195
- // Entity lookups, scoped per game: Worlds('vrchat', 'theblackcat')
196
208
  Worlds: (game, name) => load(game, 'worlds', name),
197
209
  Groups: (game, name) => load(game, 'groups', name),
198
210
  Players: (game, name) => load(game, 'players', name),
211
+ Avatars: (game, name) => load(game, 'avatars', name),
199
212
  Characters: (game, name) => load(game, 'characters', name),
200
-
201
- // Generic escape hatch for entity types not covered above
202
- // (new games may need types other than worlds/groups/players/characters)
213
+ NPCs: (game, name) => load(game, 'npcs', name),
214
+ Maps: (game, name) => load(game, 'maps', name),
203
215
  Get: (game, type, name) => load(game, type, name),
204
-
205
- // Full listing of every entry of a given type for a game, e.g. List('genshinimpact', 'characters')
206
216
  List: (game, type) => list(game, type),
217
+ CacheInfo: () => cacheInfo(),
218
+
219
+ // Game redeem/creator code API helpers. These also use live APINODE first and
220
+ // automatically fall back to the hourly mirrored data/game-codes snapshot.
221
+ Codes: gameCodes.Codes,
222
+ CodesStatus: gameCodes.CodesStatus,
223
+ GameCodes: gameCodes.GameCodes,
224
+ GameCode: gameCodes.GameCode,
225
+ CodeCacheInfo: gameCodes.CodeCacheInfo
207
226
  };
package/package.json CHANGED
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "@nekosuneprojects/gaming-infos",
3
- "version": "2.1.0",
4
- "description": "Multi-game data loader (VRChat, Genshin Impact, and more to come) for worlds, groups, players, and characters. Fetches live from the NekoSuneVR V5 API with local JSON fallback.",
3
+ "version": "2.4.0",
4
+ "description": "Multi-game data loader for VRChat, Fortnite, Call of Duty, character/NPC/map datasets, and game redeem/creator codes. Uses the NekoSuneVR V5 API first with automatically mirrored local JSON fallbacks.",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
7
7
  "engines": {
8
8
  "node": ">=18"
9
9
  },
10
+ "scripts": {
11
+ "sync:cache": "node scripts/sync-all-cache.js",
12
+ "sync:gaming-infos-cache": "node scripts/sync-gaming-info-cache.js",
13
+ "sync:game-codes-cache": "node scripts/sync-game-code-cache.js",
14
+ "test:cache-sync": "node scripts/cache-sync-smoke.js",
15
+ "test:game-code-cache": "node scripts/game-code-cache-smoke.js",
16
+ "test:all-cache": "npm run test:cache-sync && npm run test:game-code-cache"
17
+ },
10
18
  "repository": {
11
19
  "type": "git",
12
20
  "url": "https://github.com/NekoSuneProjects/gaming-infos.git"
@@ -18,12 +26,34 @@
18
26
  "keywords": [
19
27
  "gaming",
20
28
  "infos",
29
+ "game-codes",
30
+ "redeem-codes",
31
+ "creator-codes",
21
32
  "vrchat",
22
33
  "genshin-impact",
34
+ "wuthering-waves",
35
+ "warframe",
36
+ "fortnite",
37
+ "fortnite-skins",
38
+ "fortnite-npcs",
39
+ "fortnite-maps",
40
+ "call-of-duty",
41
+ "cod",
42
+ "modern-warfare",
43
+ "black-ops",
44
+ "call-of-duty-maps",
45
+ "call-of-duty-characters",
46
+ "call-of-duty-npcs",
47
+ "zenless-zone-zero",
48
+ "tower-of-fantasy",
49
+ "arknights-endfield",
23
50
  "worlds",
24
51
  "groups",
25
52
  "players",
26
- "characters"
53
+ "avatars",
54
+ "characters",
55
+ "npcs",
56
+ "maps"
27
57
  ],
28
58
  "author": "NekoSuneVR",
29
59
  "license": "MIT"
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+
3
+ const assert = require('assert');
4
+ const fs = require('fs');
5
+ const http = require('http');
6
+ const os = require('os');
7
+ const path = require('path');
8
+ const {
9
+ DEFAULT_MANIFEST,
10
+ mergeDiscoveredManifest,
11
+ validateRootSyncState,
12
+ syncApiCache
13
+ } = require('./sync-api-cache');
14
+
15
+ function json(res, status, body) {
16
+ const payload = JSON.stringify(body);
17
+ res.writeHead(status, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(payload) });
18
+ res.end(payload);
19
+ }
20
+
21
+ async function main() {
22
+ assert.deepStrictEqual(
23
+ DEFAULT_MANIFEST.fortnite,
24
+ ['characters', 'npcs', 'maps'],
25
+ 'Fortnite fallback mirror must keep skins, NPCs, and maps as separate datasets'
26
+ );
27
+
28
+ const discovered = mergeDiscoveredManifest(
29
+ [
30
+ { slug: 'codblackops2', name: 'Call of Duty: Black Ops II', types: ['characters', 'npcs', 'maps'] },
31
+ { slug: 'futuregame', name: 'Future Public Game', types: ['characters', 'maps'] }
32
+ ],
33
+ { testgame: ['characters'] }
34
+ );
35
+ assert.deepStrictEqual(discovered.testgame, ['characters']);
36
+ assert.deepStrictEqual(discovered.codblackops2, ['characters', 'npcs', 'maps']);
37
+ assert.deepStrictEqual(discovered.futuregame, ['characters', 'maps']);
38
+
39
+ assert.doesNotThrow(() => validateRootSyncState({
40
+ sync: {
41
+ running: false,
42
+ syncSchemaVersion: 2,
43
+ state: { lastStatus: 'success', syncSchemaVersion: 2, requiredSyncSchemaVersion: 2 }
44
+ }
45
+ }));
46
+ assert.throws(() => validateRootSyncState({ sync: { running: true, state: { lastStatus: 'running' } } }), /currently running/);
47
+ assert.throws(() => validateRootSyncState({ sync: { running: false, state: { lastStatus: 'failed' } } }), /state is failed/);
48
+ assert.throws(() => validateRootSyncState({
49
+ sync: {
50
+ running: false,
51
+ syncSchemaVersion: 2,
52
+ state: { lastStatus: 'success', syncSchemaVersion: 1, requiredSyncSchemaVersion: 2 }
53
+ }
54
+ }), /schema catch-up is incomplete/);
55
+
56
+ const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'gaming-infos-cache-test-'));
57
+ const dataDir = path.join(root, 'data');
58
+ let failCharacters = false;
59
+ let characters = [
60
+ { slug: 'alpha', name: 'Alpha', value: 1 },
61
+ { slug: 'beta', name: 'Beta', value: 2 }
62
+ ];
63
+
64
+ const server = http.createServer((req, res) => {
65
+ if (req.url === '/v5/games/api/gaming-infos') {
66
+ return json(res, 200, {
67
+ success: true,
68
+ data: [{ slug: 'testgame', name: 'Test Game', types: ['characters'] }],
69
+ sync: {
70
+ running: false,
71
+ syncSchemaVersion: 2,
72
+ state: { lastStatus: 'success', syncSchemaVersion: 2, requiredSyncSchemaVersion: 2 }
73
+ }
74
+ });
75
+ }
76
+ if (req.url === '/v5/games/api/gaming-infos/testgame/meta/testgame') {
77
+ return json(res, 200, { success: true, data: { slug: 'testgame', name: 'Test Game', description: 'fixture', types: ['characters'] } });
78
+ }
79
+ if (req.url === '/v5/games/api/gaming-infos/testgame/characters') {
80
+ if (failCharacters) return json(res, 503, { success: false, error: 'fixture outage' });
81
+ return json(res, 200, { success: true, data: characters });
82
+ }
83
+ return json(res, 404, { success: false, error: 'not found' });
84
+ });
85
+
86
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
87
+ const address = server.address();
88
+ const apiBase = `http://127.0.0.1:${address.port}`;
89
+ const options = {
90
+ apiBase,
91
+ apiPath: '/v5/games/api/gaming-infos',
92
+ dataDir,
93
+ manifest: { testgame: ['characters'] },
94
+ retries: 0,
95
+ timeoutMs: 3000
96
+ };
97
+
98
+ try {
99
+ const first = await syncApiCache(options);
100
+ assert.strictEqual(first.changed, true);
101
+ assert.ok(fs.existsSync(path.join(dataDir, 'testgame', 'characters', 'alpha.json')));
102
+ assert.ok(fs.existsSync(path.join(dataDir, 'testgame', 'characters', 'beta.json')));
103
+
104
+ characters = [
105
+ { slug: 'alpha', name: 'Alpha', value: 99 },
106
+ { slug: 'gamma', name: 'Gamma', value: 3 }
107
+ ];
108
+ const second = await syncApiCache(options);
109
+ assert.strictEqual(second.changed, true);
110
+ assert.strictEqual(second.stats.added, 1);
111
+ assert.strictEqual(second.stats.removed, 1);
112
+ assert.ok(second.stats.changed >= 1);
113
+ assert.strictEqual(JSON.parse(await fs.promises.readFile(path.join(dataDir, 'testgame', 'characters', 'alpha.json'), 'utf8')).value, 99);
114
+ assert.ok(fs.existsSync(path.join(dataDir, 'testgame', 'characters', 'gamma.json')));
115
+ assert.ok(!fs.existsSync(path.join(dataDir, 'testgame', 'characters', 'beta.json')));
116
+
117
+ const beforeOutage = await fs.promises.readFile(path.join(dataDir, 'testgame', 'characters', 'alpha.json'), 'utf8');
118
+ failCharacters = true;
119
+ let failed = false;
120
+ try {
121
+ await syncApiCache(options);
122
+ } catch (_) {
123
+ failed = true;
124
+ }
125
+ assert.strictEqual(failed, true, 'partial API outage must fail the sync');
126
+ assert.strictEqual(await fs.promises.readFile(path.join(dataDir, 'testgame', 'characters', 'alpha.json'), 'utf8'), beforeOutage);
127
+ assert.ok(fs.existsSync(path.join(dataDir, 'testgame', 'characters', 'gamma.json')), 'last good snapshot must survive an outage');
128
+
129
+ console.log('gaming-infos cache sync smoke test passed');
130
+ } finally {
131
+ await new Promise((resolve) => server.close(resolve));
132
+ await fs.promises.rm(root, { recursive: true, force: true });
133
+ }
134
+ }
135
+
136
+ main().catch((error) => {
137
+ console.error(error);
138
+ process.exitCode = 1;
139
+ });
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ const assert = require('assert');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const { syncGameCodeCache } = require('./sync-game-code-cache');
8
+
9
+ function response(body, status = 200) {
10
+ return {
11
+ ok: status >= 200 && status < 300,
12
+ status,
13
+ async json() { return body; }
14
+ };
15
+ }
16
+
17
+ async function main() {
18
+ const temp = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'game-code-cache-'));
19
+ const dataDir = path.join(temp, 'game-codes');
20
+ let phase = 1;
21
+ let requestTick = 0;
22
+
23
+ const directory = () => ({
24
+ success: true,
25
+ games: [
26
+ { game: 'alpha', name: 'Alpha', counts: { total: 2 }, lastUpdatedAt: '2026-08-24T10:00:00.000Z' },
27
+ { game: 'beta', name: 'Beta', counts: { total: 1 }, lastUpdatedAt: '2026-08-24T10:05:00.000Z' }
28
+ ],
29
+ aliases: { a: 'alpha', b: 'beta' },
30
+ // Simulates APINODE's request-time directory timestamp. This must not make
31
+ // an otherwise-identical fallback snapshot look changed.
32
+ updatedAt: new Date(Date.UTC(2026, 7, 24, 10, 10, requestTick++)).toISOString()
33
+ });
34
+
35
+ function expiry() {
36
+ return {
37
+ type: 'fixed',
38
+ expiresAt: '2030-12-31T23:59:59.000Z',
39
+ local: '31/12/2030, 23:59:59 UTC',
40
+ expired: false,
41
+ // These are deliberately volatile and should be stripped by the mirror.
42
+ expiresInSeconds: 100000 - requestTick,
43
+ countdown: { days: 1, hours: 2, minutes: 3, seconds: 59 - (requestTick % 50) }
44
+ };
45
+ }
46
+
47
+ const datasets = () => ({
48
+ alpha: phase === 1
49
+ ? {
50
+ Active: [
51
+ { code: 'KEEP', status: 'active', expiry: expiry() },
52
+ { code: 'REMOVE', status: 'active', expiry: expiry() }
53
+ ],
54
+ Expired: [],
55
+ Unknown: []
56
+ }
57
+ : {
58
+ Active: [
59
+ { code: 'KEEP', status: 'active', notes: 'changed', expiry: expiry() },
60
+ { code: 'ADD', status: 'active', expiry: expiry() }
61
+ ],
62
+ Expired: [],
63
+ Unknown: []
64
+ },
65
+ beta: { Active: [], Expired: [{ code: 'OLD', status: 'expired', expiry: expiry() }], Unknown: [] }
66
+ });
67
+
68
+ const originalFetch = global.fetch;
69
+ global.fetch = async (input) => {
70
+ const url = new URL(String(input));
71
+ if (phase === 3 && url.pathname.endsWith('/beta')) return response({ success: false }, 503);
72
+ if (url.pathname.endsWith('/codes')) return response(directory());
73
+ if (url.pathname.endsWith('/codes/status')) return response({ success: true, data: { generic: { ok: true }, specialized: [] } });
74
+ const game = url.pathname.split('/').pop();
75
+ if (datasets()[game]) return response(datasets()[game]);
76
+ return response({ success: false }, 404);
77
+ };
78
+
79
+ try {
80
+ const first = await syncGameCodeCache({ dataDir, apiBase: 'https://example.test', apiPath: '/v5/games/api/codes', retries: 0, delayMs: 0 });
81
+ assert.equal(first.success, true);
82
+ assert.equal(fs.existsSync(path.join(dataDir, 'games', 'alpha.json')), true);
83
+ let alpha = JSON.parse(await fs.promises.readFile(path.join(dataDir, 'games', 'alpha.json'), 'utf8'));
84
+ assert.deepEqual(alpha.Active.map((row) => row.code), ['KEEP', 'REMOVE']);
85
+ assert.equal(alpha.Active[0].expiry.expiresAt, '2030-12-31T23:59:59.000Z');
86
+ assert.equal('expiresInSeconds' in alpha.Active[0].expiry, false);
87
+ assert.equal('countdown' in alpha.Active[0].expiry, false);
88
+
89
+ phase = 2;
90
+ const second = await syncGameCodeCache({ dataDir, apiBase: 'https://example.test', apiPath: '/v5/games/api/codes', retries: 0, delayMs: 0 });
91
+ assert.equal(second.success, true);
92
+ assert.equal(second.changed, true);
93
+ alpha = JSON.parse(await fs.promises.readFile(path.join(dataDir, 'games', 'alpha.json'), 'utf8'));
94
+ assert.deepEqual(alpha.Active.map((row) => row.code), ['KEEP', 'ADD']);
95
+ assert.equal(alpha.Active[0].notes, 'changed');
96
+ assert.equal(alpha.Active.some((row) => row.code === 'REMOVE'), false);
97
+
98
+ // Same real API data, but request-time updatedAt/countdowns change again.
99
+ // The stable snapshot must detect this as unchanged.
100
+ const unchanged = await syncGameCodeCache({ dataDir, apiBase: 'https://example.test', apiPath: '/v5/games/api/codes', retries: 0, delayMs: 0 });
101
+ assert.equal(unchanged.success, true);
102
+ assert.equal(unchanged.changed, false, 'volatile countdown/request timestamps must not create cache commits');
103
+
104
+ const beforeFailure = await fs.promises.readFile(path.join(dataDir, 'games', 'alpha.json'), 'utf8');
105
+ phase = 3;
106
+ await assert.rejects(
107
+ () => syncGameCodeCache({ dataDir, apiBase: 'https://example.test', apiPath: '/v5/games/api/codes', retries: 0, delayMs: 0 }),
108
+ /503/
109
+ );
110
+ const afterFailure = await fs.promises.readFile(path.join(dataDir, 'games', 'alpha.json'), 'utf8');
111
+ assert.equal(afterFailure, beforeFailure, 'failed partial sync must preserve last-good code cache');
112
+
113
+ const status = JSON.parse(await fs.promises.readFile(path.join(dataDir, 'status.json'), 'utf8'));
114
+ assert.equal(status.generic.ok, true);
115
+ console.log('game-code-cache smoke test passed');
116
+ } finally {
117
+ global.fetch = originalFetch;
118
+ await fs.promises.rm(temp, { recursive: true, force: true });
119
+ }
120
+ }
121
+
122
+ main().catch((error) => {
123
+ console.error(error);
124
+ process.exitCode = 1;
125
+ });