@nekosuneprojects/warframe-api 0.0.0-stage → 1.0.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +298 -3
  3. package/data/characters/amesha.json +226 -0
  4. package/data/characters/ash.json +1354 -0
  5. package/data/characters/ashprime.json +1994 -0
  6. package/data/characters/atlas.json +916 -0
  7. package/data/characters/atlasprime.json +1632 -0
  8. package/data/characters/banshee.json +1036 -0
  9. package/data/characters/bansheeprime.json +1750 -0
  10. package/data/characters/baruuk.json +555 -0
  11. package/data/characters/baruukprime.json +1298 -0
  12. package/data/characters/bonewidow.json +354 -0
  13. package/data/characters/caliban.json +434 -0
  14. package/data/characters/calibanprime.json +683 -0
  15. package/data/characters/chroma.json +1344 -0
  16. package/data/characters/chromaprime.json +1977 -0
  17. package/data/characters/citrine.json +220 -0
  18. package/data/characters/citrineprime.json +62 -0
  19. package/data/characters/cyte09.json +218 -0
  20. package/data/characters/dagath.json +194 -0
  21. package/data/characters/dante.json +223 -0
  22. package/data/characters/elytron.json +300 -0
  23. package/data/characters/ember.json +1209 -0
  24. package/data/characters/emberprime.json +1678 -0
  25. package/data/characters/equinox.json +958 -0
  26. package/data/characters/equinoxprime.json +1644 -0
  27. package/data/characters/excalibur.json +1603 -0
  28. package/data/characters/excaliburprime.json +1524 -0
  29. package/data/characters/excaliburumbra.json +397 -0
  30. package/data/characters/follie.json +246 -0
  31. package/data/characters/frost.json +1330 -0
  32. package/data/characters/frostprime.json +1686 -0
  33. package/data/characters/gara.json +925 -0
  34. package/data/characters/garaprime.json +1494 -0
  35. package/data/characters/garuda.json +851 -0
  36. package/data/characters/garudaprime.json +1449 -0
  37. package/data/characters/gauss.json +506 -0
  38. package/data/characters/gaussprime.json +1077 -0
  39. package/data/characters/grendel.json +463 -0
  40. package/data/characters/grendelprime.json +772 -0
  41. package/data/characters/gyre.json +381 -0
  42. package/data/characters/gyreprime.json +403 -0
  43. package/data/characters/harrow.json +916 -0
  44. package/data/characters/harrowprime.json +1700 -0
  45. package/data/characters/helminth.json +220 -0
  46. package/data/characters/hildryn.json +627 -0
  47. package/data/characters/hildrynprime.json +996 -0
  48. package/data/characters/hydroid.json +1028 -0
  49. package/data/characters/hydroidprime.json +1621 -0
  50. package/data/characters/inaros.json +976 -0
  51. package/data/characters/inarosprime.json +1721 -0
  52. package/data/characters/itzal.json +465 -0
  53. package/data/characters/ivara.json +1349 -0
  54. package/data/characters/ivaraprime.json +1921 -0
  55. package/data/characters/jade.json +240 -0
  56. package/data/characters/khora.json +804 -0
  57. package/data/characters/khoraprime.json +1403 -0
  58. package/data/characters/koumei.json +221 -0
  59. package/data/characters/kullervo.json +191 -0
  60. package/data/characters/lavos.json +511 -0
  61. package/data/characters/lavosprime.json +711 -0
  62. package/data/characters/limbo.json +1363 -0
  63. package/data/characters/limboprime.json +2084 -0
  64. package/data/characters/loki.json +1209 -0
  65. package/data/characters/lokiprime.json +1457 -0
  66. package/data/characters/mag.json +2089 -0
  67. package/data/characters/magprime.json +2532 -0
  68. package/data/characters/mesa.json +1195 -0
  69. package/data/characters/mesaprime.json +2059 -0
  70. package/data/characters/mirage.json +1561 -0
  71. package/data/characters/mirageprime.json +2257 -0
  72. package/data/characters/narin.json +43 -0
  73. package/data/characters/nekros.json +1235 -0
  74. package/data/characters/nekrosprime.json +1882 -0
  75. package/data/characters/nezha.json +868 -0
  76. package/data/characters/nezhaprime.json +1695 -0
  77. package/data/characters/nidus.json +1027 -0
  78. package/data/characters/nidusprime.json +1706 -0
  79. package/data/characters/nokko.json +308 -0
  80. package/data/characters/nova.json +1226 -0
  81. package/data/characters/novaprime.json +1667 -0
  82. package/data/characters/nyx.json +1610 -0
  83. package/data/characters/nyxprime.json +1967 -0
  84. package/data/characters/oberon.json +1249 -0
  85. package/data/characters/oberonprime.json +1872 -0
  86. package/data/characters/octavia.json +1049 -0
  87. package/data/characters/octaviaprime.json +1735 -0
  88. package/data/characters/odonata.json +336 -0
  89. package/data/characters/odonataprime.json +787 -0
  90. package/data/characters/oraxia.json +246 -0
  91. package/data/characters/orionsirius.json +134 -0
  92. package/data/characters/protea.json +521 -0
  93. package/data/characters/proteaprime.json +992 -0
  94. package/data/characters/qorvex.json +214 -0
  95. package/data/characters/revenant.json +931 -0
  96. package/data/characters/revenantprime.json +1494 -0
  97. package/data/characters/rhino.json +1344 -0
  98. package/data/characters/rhinoprime.json +1675 -0
  99. package/data/characters/saryn.json +1210 -0
  100. package/data/characters/sarynprime.json +2042 -0
  101. package/data/characters/sevagoth.json +478 -0
  102. package/data/characters/sevagothprime.json +910 -0
  103. package/data/characters/siriusorion.json +247 -0
  104. package/data/characters/styanax.json +320 -0
  105. package/data/characters/styanaxprime.json +321 -0
  106. package/data/characters/temple.json +271 -0
  107. package/data/characters/titania.json +1227 -0
  108. package/data/characters/titaniaprime.json +2000 -0
  109. package/data/characters/trinity.json +1097 -0
  110. package/data/characters/trinityprime.json +1849 -0
  111. package/data/characters/uriel.json +215 -0
  112. package/data/characters/valkyr.json +1165 -0
  113. package/data/characters/valkyrprime.json +1969 -0
  114. package/data/characters/vauban.json +1067 -0
  115. package/data/characters/vaubanprime.json +1614 -0
  116. package/data/characters/voidrig.json +355 -0
  117. package/data/characters/volt.json +1324 -0
  118. package/data/characters/voltprime.json +1843 -0
  119. package/data/characters/voruna.json +293 -0
  120. package/data/characters/vorunaprime.json +431 -0
  121. package/data/characters/wisp.json +824 -0
  122. package/data/characters/wispprime.json +908 -0
  123. package/data/characters/wukong.json +927 -0
  124. package/data/characters/wukongprime.json +1754 -0
  125. package/data/characters/xaku.json +620 -0
  126. package/data/characters/xakuprime.json +879 -0
  127. package/data/characters/yareli.json +409 -0
  128. package/data/characters/yareliprime.json +683 -0
  129. package/data/characters/zephyr.json +1149 -0
  130. package/data/characters/zephyrprime.json +1864 -0
  131. package/data/codes/warframe.json +20824 -0
  132. package/data/meta.json +14 -0
  133. package/index.js +11 -0
  134. package/package.json +50 -6
  135. package/src/catalog.js +105 -0
  136. package/src/client-error.js +9 -0
  137. package/src/data-source.js +7 -0
  138. package/src/data-store.js +138 -0
  139. package/src/provider.js +22 -0
  140. package/src/tracker.js +115 -0
  141. package/src/warframe-client.js +57 -0
  142. package/src/warframe-codes.js +82 -0
  143. package/src/wfcd.js +93 -0
package/src/wfcd.js ADDED
@@ -0,0 +1,93 @@
1
+ 'use strict';
2
+ const fs = require('node:fs/promises');
3
+ const path = require('node:path');
4
+ const os = require('node:os');
5
+ const crypto = require('node:crypto');
6
+
7
+ // Verified WFCD GitHub repositories. All URLs are internal, never caller-provided.
8
+ const SOURCES = Object.freeze({
9
+ drops:{repo:'warframe-drop-data',branch:'main',prefix:'data/',files:['all.json','info.json','missionRewards.json','relics.json','modLocations.json','blueprintLocations.json','cetusBountyRewards.json','solarisBountyRewards.json','sortieRewards.json','transientRewards.json','deimosRewards.json','entratiLabRewards.json','hexRewards.json','zarimanRewards.json','syndicates.json']},
10
+ items:{repo:'warframe-items',branch:'master',prefix:'data/json/',files:['Arcanes.json','Arch-Gun.json','Arch-Melee.json','Archwing.json','Components.json','Enemy.json','Fish.json','Gear.json','Glyphs.json','Honoria.json','Melee.json','Misc.json','Mods.json','Node.json','Pets.json','Primary.json','Quests.json','Railjack.json','Relics.json','Resources.json','Secondary.json','SentinelWeapons.json','Sentinels.json','Sigils.json','Skins.json','Warframes.json']},
11
+ relics:{repo:'warframe-relic-data',branch:'development',prefix:'data/',files:['Relics.json','Relics.min.json','hash.json','version.json']},
12
+ patchlogs:{repo:'warframe-patchlogs',branch:'master',prefix:'data/',files:['patchlogs.json']}
13
+ });
14
+ const FILE_LIMIT = 16 * 1024 * 1024;
15
+ function createWFCD({cacheDir,githubToken,fetchImpl=globalThis.fetch}={}){
16
+ if(typeof fetchImpl!=='function')throw new TypeError('fetch is required');
17
+ const root=path.resolve(cacheDir || path.join(os.homedir(),'.cache','nekosune-game-tracker','warframe'),'wfcd');
18
+ const headers={Accept:'application/vnd.github+json','User-Agent':'nekosuneprojects-warframe-api',...(githubToken?{Authorization:'Bearer '+githubToken}:{})};
19
+ const sha=buffer=>crypto.createHash('sha1').update('blob '+buffer.length+'\0').update(buffer).digest('hex');
20
+ async function get(source,name,{refresh=false,signal}={}){
21
+ const definition=SOURCES[source];
22
+ if(!definition)throw new RangeError('Unknown WFCD data source');
23
+ if(typeof name!=='string'||!definition.files.includes(name))throw new RangeError('Unsupported '+source+' JSON file');
24
+ const dest=path.join(root,source,name);
25
+ const manifest=dest+'.sha';
26
+ let local;
27
+ try{local=await fs.readFile(dest);JSON.parse(local.toString('utf8'))}
28
+ catch(error){if(error.code!=='ENOENT' && !(error instanceof SyntaxError))throw error;local=null}
29
+ if(local && !refresh)return JSON.parse(local.toString('utf8'));
30
+ const remotePath=definition.prefix+name;
31
+ const contentsUrl='https://api.github.com/repos/WFCD/'+definition.repo+'/contents/'+remotePath+'?ref='+definition.branch;
32
+ try {
33
+ const metadataResponse=await fetchImpl(contentsUrl,{headers,signal});
34
+ if(!metadataResponse.ok)throw new Error('WFCD metadata HTTP '+metadataResponse.status);
35
+ const metadata=await metadataResponse.json();
36
+ if(!/^[a-f0-9]{40}$/.test(metadata.sha||'') || metadata.type!=='file' ||
37
+ !Number.isSafeInteger(metadata.size) || metadata.size>FILE_LIMIT)throw new Error('Invalid WFCD source metadata');
38
+ if(local && sha(local)===metadata.sha)return JSON.parse(local.toString('utf8'));
39
+ const blobURL='https://api.github.com/repos/WFCD/'+definition.repo+'/git/blobs/'+metadata.sha;
40
+ const response=await fetchImpl(blobURL,{headers,signal});
41
+ if(!response.ok)throw new Error('WFCD blob HTTP '+response.status);
42
+ const blob=await response.json();
43
+ if(blob.encoding!=='base64'||typeof blob.content!=='string')throw new Error('Unexpected WFCD blob encoding');
44
+ const body=Buffer.from(blob.content.replace(/\s/g,''),'base64');
45
+ if(body.length>FILE_LIMIT||sha(body)!==metadata.sha)throw new Error('WFCD checksum/size mismatch');
46
+ const value=JSON.parse(body.toString('utf8'));
47
+ await fs.mkdir(path.dirname(dest),{recursive:true});
48
+ const tmp=dest+'.'+process.pid+'.'+crypto.randomBytes(6).toString('hex')+'.tmp';
49
+ try{await fs.writeFile(tmp,body);await fs.rename(tmp,dest);await fs.writeFile(manifest,metadata.sha)}
50
+ finally{await fs.rm(tmp,{force:true})}
51
+ return value;
52
+ } catch(error) {
53
+ if(local && !refresh)return JSON.parse(local.toString('utf8'));
54
+ throw error;
55
+ }
56
+ }
57
+ const groups=Object.fromEntries(Object.keys(SOURCES).map(source=>[source,Object.freeze({
58
+ files:Object.freeze([...SOURCES[source].files]),
59
+ get:(name,options)=>get(source,name,options),
60
+ refresh:(name,options)=>get(source,name,{...options,refresh:true}),
61
+ ...(source==='drops'?{all:opts=>get(source,'all.json',opts),info:opts=>get(source,'info.json',opts),
62
+ missionRewards:opts=>get(source,'missionRewards.json',opts)}:{}),
63
+ ...(source==='items'?{warframes:opts=>get(source,'Warframes.json',opts),
64
+ weapons:opts=>Promise.all(['Primary.json','Secondary.json','Melee.json'].map(name=>get(source,name,opts))).then(parts=>parts.flat()),
65
+ mods:opts=>get(source,'Mods.json',opts),arcanes:opts=>get(source,'Arcanes.json',opts)}:{}),
66
+ ...(source==='relics'?{all:opts=>get(source,'Relics.json',opts)}:{}),
67
+ ...(source==='patchlogs'?{all:opts=>get(source,'patchlogs.json',opts)}:{})
68
+ })]));
69
+ return Object.freeze({...groups,cacheDir:root});
70
+ }
71
+
72
+ async function createMarketQuery(options={}) {
73
+ let pkg;
74
+ try {pkg=await import('warframe-nexus-query');}
75
+ catch(error) {
76
+ if(error.code==='ERR_MODULE_NOT_FOUND')throw new Error('Install optional market integration: npm install warframe-nexus-query',{cause:error});
77
+ throw error;
78
+ }
79
+ const Constructor=pkg.default||pkg.PriceCheckQuerier;
80
+ if(typeof Constructor!=='function')throw new Error('Unsupported warframe-nexus-query export');
81
+ return new Constructor(options);
82
+ }
83
+ function createUpdateMonitor(options={}){
84
+ let Package;
85
+ try{Package=require('warframe-updates')}
86
+ catch(error){
87
+ if(error.code==='MODULE_NOT_FOUND')throw new Error('Install optional patch monitor: npm install warframe-updates',{cause:error});
88
+ throw error;
89
+ }
90
+ if(typeof Package!=='function')throw new Error('Unsupported warframe-updates export');
91
+ return new Package(options);
92
+ }
93
+ module.exports={createWFCD,createMarketQuery,createUpdateMonitor,SOURCES};