@nekosuneprojects/gaming-infos 2.2.0 → 3.0.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.
- package/CHANGELOG.md +77 -3
- package/README.md +62 -495
- package/index.js +3 -223
- package/package.json +53 -46
- package/src/providers/vrchat.js +11 -0
- package/src/providers/warframe.js +7 -0
- package/src/registry.js +23 -0
- package/.github/workflows/npm-publish-github-packages.yml +0 -56
- package/.github/workflows/npm-publish.yml +0 -71
- package/.github/workflows/sync-api-cache.yml +0 -72
- package/CACHE_SYNC.md +0 -172
- package/CONTRIBUTING.md +0 -217
- package/DATA.md +0 -292
- package/data/arknightsendfield/meta.json +0 -14
- package/data/fortnite/meta.json +0 -14
- package/data/game-codes/.gitkeep +0 -0
- package/data/genshinimpact/characters/albedo.json +0 -11
- package/data/genshinimpact/characters/alhaitham.json +0 -12
- package/data/genshinimpact/characters/aloy.json +0 -11
- package/data/genshinimpact/characters/amber.json +0 -12
- package/data/genshinimpact/characters/aratakiitto.json +0 -12
- package/data/genshinimpact/characters/arlecchino.json +0 -12
- package/data/genshinimpact/characters/baizhu.json +0 -12
- package/data/genshinimpact/characters/barbara.json +0 -11
- package/data/genshinimpact/characters/beidou.json +0 -12
- package/data/genshinimpact/characters/bennett.json +0 -11
- package/data/genshinimpact/characters/candace.json +0 -12
- package/data/genshinimpact/characters/charlotte.json +0 -11
- package/data/genshinimpact/characters/chasca.json +0 -11
- package/data/genshinimpact/characters/chevreuse.json +0 -11
- package/data/genshinimpact/characters/chiori.json +0 -11
- package/data/genshinimpact/characters/chongyun.json +0 -12
- package/data/genshinimpact/characters/citlali.json +0 -11
- package/data/genshinimpact/characters/clorinde.json +0 -11
- package/data/genshinimpact/characters/collei.json +0 -12
- package/data/genshinimpact/characters/cyno.json +0 -12
- package/data/genshinimpact/characters/dahlia.json +0 -10
- package/data/genshinimpact/characters/dehya.json +0 -11
- package/data/genshinimpact/characters/diluc.json +0 -11
- package/data/genshinimpact/characters/diona.json +0 -11
- package/data/genshinimpact/characters/dori.json +0 -11
- package/data/genshinimpact/characters/durin.json +0 -11
- package/data/genshinimpact/characters/emilie.json +0 -11
- package/data/genshinimpact/characters/escoffier.json +0 -12
- package/data/genshinimpact/characters/eula.json +0 -11
- package/data/genshinimpact/characters/faruzan.json +0 -12
- package/data/genshinimpact/characters/fischl.json +0 -12
- package/data/genshinimpact/characters/freminet.json +0 -11
- package/data/genshinimpact/characters/furina.json +0 -12
- package/data/genshinimpact/characters/gaming.json +0 -11
- package/data/genshinimpact/characters/ganyu.json +0 -11
- package/data/genshinimpact/characters/gorou.json +0 -12
- package/data/genshinimpact/characters/hutao.json +0 -12
- package/data/genshinimpact/characters/iansan.json +0 -11
- package/data/genshinimpact/characters/ineffa.json +0 -11
- package/data/genshinimpact/characters/jean.json +0 -11
- package/data/genshinimpact/characters/kachina.json +0 -11
- package/data/genshinimpact/characters/kaedeharakazuha.json +0 -12
- package/data/genshinimpact/characters/kaeya.json +0 -11
- package/data/genshinimpact/characters/kamisatoayaka.json +0 -11
- package/data/genshinimpact/characters/kamisatoayato.json +0 -11
- package/data/genshinimpact/characters/kaveh.json +0 -12
- package/data/genshinimpact/characters/keqing.json +0 -11
- package/data/genshinimpact/characters/kinich.json +0 -11
- package/data/genshinimpact/characters/kirara.json +0 -11
- package/data/genshinimpact/characters/klee.json +0 -11
- package/data/genshinimpact/characters/kujousara.json +0 -11
- package/data/genshinimpact/characters/kukishinobu.json +0 -12
- package/data/genshinimpact/characters/lanyan.json +0 -11
- package/data/genshinimpact/characters/layla.json +0 -11
- package/data/genshinimpact/characters/lisa.json +0 -12
- package/data/genshinimpact/characters/lohen.json +0 -11
- package/data/genshinimpact/characters/lynette.json +0 -11
- package/data/genshinimpact/characters/lyney.json +0 -11
- package/data/genshinimpact/characters/mavuika.json +0 -11
- package/data/genshinimpact/characters/mikaschmidt.json +0 -11
- package/data/genshinimpact/characters/mona.json +0 -12
- package/data/genshinimpact/characters/mualani.json +0 -11
- package/data/genshinimpact/characters/nahida.json +0 -12
- package/data/genshinimpact/characters/navia.json +0 -11
- package/data/genshinimpact/characters/neuvillette.json +0 -11
- package/data/genshinimpact/characters/nilou.json +0 -11
- package/data/genshinimpact/characters/ningguang.json +0 -11
- package/data/genshinimpact/characters/noelle.json +0 -12
- package/data/genshinimpact/characters/ororon.json +0 -11
- package/data/genshinimpact/characters/prune.json +0 -10
- package/data/genshinimpact/characters/qiqi.json +0 -12
- package/data/genshinimpact/characters/raidenshogun.json +0 -12
- package/data/genshinimpact/characters/razor.json +0 -11
- package/data/genshinimpact/characters/rosaria.json +0 -12
- package/data/genshinimpact/characters/sangonomiyakokomi.json +0 -12
- package/data/genshinimpact/characters/sayu.json +0 -11
- package/data/genshinimpact/characters/sethos.json +0 -11
- package/data/genshinimpact/characters/shenhe.json +0 -12
- package/data/genshinimpact/characters/shikanoinheizou.json +0 -12
- package/data/genshinimpact/characters/sigewinne.json +0 -11
- package/data/genshinimpact/characters/skirk.json +0 -11
- package/data/genshinimpact/characters/sucrose.json +0 -12
- package/data/genshinimpact/characters/thoma.json +0 -11
- package/data/genshinimpact/characters/tighnari.json +0 -12
- package/data/genshinimpact/characters/varesa.json +0 -11
- package/data/genshinimpact/characters/varka.json +0 -10
- package/data/genshinimpact/characters/venti.json +0 -12
- package/data/genshinimpact/characters/wanderer.json +0 -11
- package/data/genshinimpact/characters/wriothesley.json +0 -12
- package/data/genshinimpact/characters/xiangling.json +0 -12
- package/data/genshinimpact/characters/xianyun.json +0 -11
- package/data/genshinimpact/characters/xiao.json +0 -11
- package/data/genshinimpact/characters/xilonen.json +0 -11
- package/data/genshinimpact/characters/xingqiu.json +0 -12
- package/data/genshinimpact/characters/xinyan.json +0 -12
- package/data/genshinimpact/characters/yaemiko.json +0 -12
- package/data/genshinimpact/characters/yanfei.json +0 -12
- package/data/genshinimpact/characters/yaoyao.json +0 -12
- package/data/genshinimpact/characters/yelan.json +0 -11
- package/data/genshinimpact/characters/yoimiya.json +0 -12
- package/data/genshinimpact/characters/yumemizukimizuki.json +0 -11
- package/data/genshinimpact/characters/yunjin.json +0 -11
- package/data/genshinimpact/characters/zhongli.json +0 -11
- package/data/genshinimpact/characters/zibai.json +0 -11
- package/data/genshinimpact/meta.json +0 -14
- package/data/honkaistarrail/characters/acheron.json +0 -12
- package/data/honkaistarrail/characters/aglaea.json +0 -12
- package/data/honkaistarrail/characters/anaxa.json +0 -12
- package/data/honkaistarrail/characters/argenti.json +0 -12
- package/data/honkaistarrail/characters/arlan.json +0 -12
- package/data/honkaistarrail/characters/ashveil.json +0 -12
- package/data/honkaistarrail/characters/asta.json +0 -12
- package/data/honkaistarrail/characters/aventurine.json +0 -12
- package/data/honkaistarrail/characters/bailu.json +0 -12
- package/data/honkaistarrail/characters/blackswan.json +0 -12
- package/data/honkaistarrail/characters/blade.json +0 -12
- package/data/honkaistarrail/characters/boothill.json +0 -12
- package/data/honkaistarrail/characters/bronya.json +0 -12
- package/data/honkaistarrail/characters/castorice.json +0 -12
- package/data/honkaistarrail/characters/cerydra.json +0 -12
- package/data/honkaistarrail/characters/cipher.json +0 -12
- package/data/honkaistarrail/characters/clara.json +0 -12
- package/data/honkaistarrail/characters/constance.json +0 -12
- package/data/honkaistarrail/characters/cyrene.json +0 -12
- package/data/honkaistarrail/characters/danheng.json +0 -12
- package/data/honkaistarrail/characters/drratio.json +0 -12
- package/data/honkaistarrail/characters/feixiao.json +0 -12
- package/data/honkaistarrail/characters/firefly.json +0 -12
- package/data/honkaistarrail/characters/fuxuan.json +0 -12
- package/data/honkaistarrail/characters/gallagher.json +0 -12
- package/data/honkaistarrail/characters/gepard.json +0 -12
- package/data/honkaistarrail/characters/guinaifen.json +0 -12
- package/data/honkaistarrail/characters/hanya.json +0 -12
- package/data/honkaistarrail/characters/herta.json +0 -12
- package/data/honkaistarrail/characters/himeko.json +0 -13
- package/data/honkaistarrail/characters/hook.json +0 -12
- package/data/honkaistarrail/characters/huohuo.json +0 -12
- package/data/honkaistarrail/characters/hyacine.json +0 -12
- package/data/honkaistarrail/characters/hysilens.json +0 -12
- package/data/honkaistarrail/characters/jade.json +0 -12
- package/data/honkaistarrail/characters/jiaoqiu.json +0 -12
- package/data/honkaistarrail/characters/jingliu.json +0 -12
- package/data/honkaistarrail/characters/jingyuan.json +0 -12
- package/data/honkaistarrail/characters/kafka.json +0 -12
- package/data/honkaistarrail/characters/lingsha.json +0 -12
- package/data/honkaistarrail/characters/lisushang.json +0 -12
- package/data/honkaistarrail/characters/luka.json +0 -12
- package/data/honkaistarrail/characters/luocha.json +0 -12
- package/data/honkaistarrail/characters/lynx.json +0 -12
- package/data/honkaistarrail/characters/march7th.json +0 -12
- package/data/honkaistarrail/characters/misha.json +0 -12
- package/data/honkaistarrail/characters/moze.json +0 -12
- package/data/honkaistarrail/characters/mydei.json +0 -12
- package/data/honkaistarrail/characters/natasha.json +0 -12
- package/data/honkaistarrail/characters/pela.json +0 -12
- package/data/honkaistarrail/characters/phainon.json +0 -12
- package/data/honkaistarrail/characters/qingque.json +0 -12
- package/data/honkaistarrail/characters/rappa.json +0 -12
- package/data/honkaistarrail/characters/robin.json +0 -12
- package/data/honkaistarrail/characters/ruanmei.json +0 -12
- package/data/honkaistarrail/characters/sampo.json +0 -12
- package/data/honkaistarrail/characters/seele.json +0 -12
- package/data/honkaistarrail/characters/serval.json +0 -12
- package/data/honkaistarrail/characters/silverwolf.json +0 -12
- package/data/honkaistarrail/characters/sparkle.json +0 -12
- package/data/honkaistarrail/characters/tingyun.json +0 -12
- package/data/honkaistarrail/characters/topaz.json +0 -12
- package/data/honkaistarrail/characters/tribbie.json +0 -12
- package/data/honkaistarrail/characters/weltyang.json +0 -12
- package/data/honkaistarrail/characters/xueyi.json +0 -12
- package/data/honkaistarrail/characters/yanqing.json +0 -12
- package/data/honkaistarrail/characters/yaoguang.json +0 -12
- package/data/honkaistarrail/characters/yukong.json +0 -12
- package/data/honkaistarrail/characters/yunli.json +0 -12
- package/data/honkaistarrail/meta.json +0 -14
- package/data/nte/characters/adler.json +0 -14
- package/data/nte/characters/aurelia.json +0 -14
- package/data/nte/characters/baicang.json +0 -14
- package/data/nte/characters/chaos.json +0 -14
- package/data/nte/characters/chiz.json +0 -14
- package/data/nte/characters/daffodill.json +0 -14
- package/data/nte/characters/edgar.json +0 -14
- package/data/nte/characters/esperzero.json +0 -14
- package/data/nte/characters/fadia.json +0 -14
- package/data/nte/characters/haniel.json +0 -14
- package/data/nte/characters/hathor.json +0 -14
- package/data/nte/characters/hotori.json +0 -14
- package/data/nte/characters/iroi.json +0 -14
- package/data/nte/characters/jiuyuan.json +0 -14
- package/data/nte/characters/lacrimosa.json +0 -14
- package/data/nte/characters/mint.json +0 -14
- package/data/nte/characters/nanally.json +0 -14
- package/data/nte/characters/sakiri.json +0 -14
- package/data/nte/characters/shinku.json +0 -14
- package/data/nte/characters/skia.json +0 -14
- package/data/nte/meta.json +0 -14
- package/data/toweroffantasy/meta.json +0 -14
- package/data/vrchat/avatars/.gitkeep +0 -0
- package/data/vrchat/groups/clubzodiac.json +0 -7
- package/data/vrchat/groups/dirtydancers.json +0 -7
- package/data/vrchat/groups/drumsynews.json +0 -7
- package/data/vrchat/groups/ebola20.json +0 -7
- package/data/vrchat/groups/internationaldancersassociation.json +0 -7
- package/data/vrchat/groups/kannagang.json +0 -6
- package/data/vrchat/groups/lolisquad.json +0 -7
- package/data/vrchat/groups/nekonightsnightclub.json +0 -7
- package/data/vrchat/groups/nekostudios.json +0 -7
- package/data/vrchat/groups/nekosunecommunity.json +0 -7
- package/data/vrchat/groups/voidclub.json +0 -7
- package/data/vrchat/groups/voltdanceclub.json +0 -7
- package/data/vrchat/groups/vrceventsgroup.json +0 -7
- package/data/vrchat/groups/vrchatcommunitydiscord.json +0 -6
- package/data/vrchat/meta.json +0 -13
- package/data/vrchat/players/alto.json +0 -9
- package/data/vrchat/players/arcadum.json +0 -10
- package/data/vrchat/players/chairhandler.json +0 -9
- package/data/vrchat/players/chipz.json +0 -9
- package/data/vrchat/players/hikarivr.json +0 -10
- package/data/vrchat/players/kimple.json +0 -10
- package/data/vrchat/players/linkzr.json +0 -8
- package/data/vrchat/players/nagzz21.json +0 -9
- package/data/vrchat/players/nebride.json +0 -10
- package/data/vrchat/players/nekosunevr.json +0 -10
- package/data/vrchat/players/pokelawls.json +0 -9
- package/data/vrchat/players/qwonk.json +0 -8
- package/data/vrchat/players/roflgator.json +0 -10
- package/data/vrchat/players/scifri.json +0 -7
- package/data/vrchat/players/sodapoppin.json +0 -9
- package/data/vrchat/players/woops.json +0 -9
- package/data/vrchat/players/zentreya.json +0 -10
- package/data/vrchat/worlds/ascensionacademy.json +0 -7
- package/data/vrchat/worlds/clubvgl.json +0 -8
- package/data/vrchat/worlds/fightfinder.json +0 -8
- package/data/vrchat/worlds/hangouthouse.json +0 -7
- package/data/vrchat/worlds/nekostudioshalloween.json +0 -9
- package/data/vrchat/worlds/nekostudiosnewyear.json +0 -9
- package/data/vrchat/worlds/nekostudiosxmas.json +0 -9
- package/data/vrchat/worlds/nekosunevranimations2023.json +0 -9
- package/data/vrchat/worlds/nekosunevranimations2024.json +0 -9
- package/data/vrchat/worlds/nekosunevranimations2025.json +0 -9
- package/data/vrchat/worlds/rumhamsavatarworld.json +0 -7
- package/data/vrchat/worlds/smewsranch.json +0 -7
- package/data/vrchat/worlds/smewsroom.json +0 -7
- package/data/vrchat/worlds/sunsetbar.json +0 -7
- package/data/vrchat/worlds/theblackcat.json +0 -9
- package/data/vrchat/worlds/thegreatpug.json +0 -8
- package/data/vrchat/worlds/themidnightbar.json +0 -7
- package/data/vrchat/worlds/voidclub.json +0 -7
- package/data/vrchat/worlds/voltdanceclub.json +0 -7
- package/data/vrchat/worlds/vrchathome.json +0 -8
- package/data/vrchat/worlds/whatalegend.json +0 -6
- package/data/warframe/meta.json +0 -14
- package/data/wutheringwaves/meta.json +0 -14
- package/data/zenlesszonezero/meta.json +0 -14
- package/gamecodes.js +0 -178
- package/scripts/cache-sync-smoke.js +0 -92
- package/scripts/game-code-cache-smoke.js +0 -85
- package/scripts/sync-all-cache.js +0 -18
- package/scripts/sync-api-cache.js +0 -319
- package/scripts/sync-game-code-cache.js +0 -244
- package/scripts/sync-gaming-info-cache.js +0 -46
package/index.js
CHANGED
|
@@ -1,224 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const gameCodes = require('./gamecodes');
|
|
1
|
+
'use strict';
|
|
2
|
+
const { createClient, createWarframe, createVRChat, listProviders, providers } = require('./src/registry');
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
const DEFAULT_API_BASE = 'https://api.nekosunevr.co.uk';
|
|
7
|
-
const DEFAULT_API_PATH = '/v5/games/api/gaming-infos';
|
|
8
|
-
const DEFAULT_TIMEOUT_MS = 4000;
|
|
9
|
-
|
|
10
|
-
function apiBase() {
|
|
11
|
-
return (process.env.GAMING_INFOS_API_BASE || DEFAULT_API_BASE).replace(/\/+$/, '');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function apiPath() {
|
|
15
|
-
return process.env.GAMING_INFOS_API_PATH || DEFAULT_API_PATH;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function apiDisabled() {
|
|
19
|
-
return process.env.GAMING_INFOS_DISABLE_API === '1' || process.env.GAMING_INFOS_DISABLE_API === 'true';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function timeoutMs() {
|
|
23
|
-
const raw = Number(process.env.GAMING_INFOS_TIMEOUT_MS);
|
|
24
|
-
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_TIMEOUT_MS;
|
|
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
|
-
|
|
32
|
-
// Live lookups go through the NekoSuneVR V5 API first so data can be updated
|
|
33
|
-
// in real time without republishing this package. The bundled JSON under
|
|
34
|
-
// data/ is the offline fallback and is mirrored from APINODE by the scheduled
|
|
35
|
-
// sync workflow.
|
|
36
|
-
async function fetchFromApi(game, type, name) {
|
|
37
|
-
if (apiDisabled() || typeof fetch !== 'function') return null;
|
|
38
|
-
|
|
39
|
-
const url = `${apiBase()}${apiPath()}/${encodeURIComponent(game.toLowerCase())}/${encodeURIComponent(type)}/${encodeURIComponent(name.toLowerCase())}`;
|
|
40
|
-
const controller = new AbortController();
|
|
41
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs());
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
45
|
-
if (!res.ok) return null;
|
|
46
|
-
const body = await res.json();
|
|
47
|
-
if (!body || body.success === false) return null;
|
|
48
|
-
return body.data !== undefined ? body.data : body;
|
|
49
|
-
} catch (err) {
|
|
50
|
-
return null;
|
|
51
|
-
} finally {
|
|
52
|
-
clearTimeout(timer);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function loadLocal(game, type, name) {
|
|
57
|
-
const filePath = path.join(DATA_DIR, game.toLowerCase(), type, `${name.toLowerCase()}.json`);
|
|
58
|
-
if (!fs.existsSync(filePath)) {
|
|
59
|
-
throw new Error(`${type} "${name}" not found for game "${game}"`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const raw = await fs.promises.readFile(filePath, 'utf8');
|
|
63
|
-
return withLocalSlug(filePath, JSON.parse(raw));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function fetchListFromApi(game, type) {
|
|
67
|
-
if (apiDisabled() || typeof fetch !== 'function') return null;
|
|
68
|
-
|
|
69
|
-
const url = `${apiBase()}${apiPath()}/${encodeURIComponent(game.toLowerCase())}/${encodeURIComponent(type)}`;
|
|
70
|
-
const controller = new AbortController();
|
|
71
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs());
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
75
|
-
if (!res.ok) return null;
|
|
76
|
-
const body = await res.json();
|
|
77
|
-
if (!body || body.success === false) return null;
|
|
78
|
-
const data = body.data !== undefined ? body.data : body;
|
|
79
|
-
return Array.isArray(data) ? data : null;
|
|
80
|
-
} catch (err) {
|
|
81
|
-
return null;
|
|
82
|
-
} finally {
|
|
83
|
-
clearTimeout(timer);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function listLocal(game, type) {
|
|
88
|
-
const dir = path.join(DATA_DIR, game.toLowerCase(), type);
|
|
89
|
-
if (!fs.existsSync(dir)) return [];
|
|
90
|
-
|
|
91
|
-
const files = (await fs.promises.readdir(dir)).filter((file) => file.endsWith('.json')).sort((a, b) => a.localeCompare(b));
|
|
92
|
-
const items = [];
|
|
93
|
-
for (const file of files) {
|
|
94
|
-
const filePath = path.join(dir, file);
|
|
95
|
-
const raw = await fs.promises.readFile(filePath, 'utf8');
|
|
96
|
-
items.push(withLocalSlug(filePath, JSON.parse(raw)));
|
|
97
|
-
}
|
|
98
|
-
return items;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function list(game, type) {
|
|
102
|
-
if (!game || !type) {
|
|
103
|
-
return { error: 'game and type are both required' };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const fromApi = await fetchListFromApi(game, type);
|
|
107
|
-
if (fromApi) return fromApi;
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
return await listLocal(game, type);
|
|
111
|
-
} catch (err) {
|
|
112
|
-
return { error: err.message };
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function fetchGamesFromApi() {
|
|
117
|
-
if (apiDisabled() || typeof fetch !== 'function') return null;
|
|
118
|
-
|
|
119
|
-
const url = `${apiBase()}${apiPath()}`;
|
|
120
|
-
const controller = new AbortController();
|
|
121
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs());
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
125
|
-
if (!res.ok) return null;
|
|
126
|
-
const body = await res.json();
|
|
127
|
-
if (!body || body.success === false) return null;
|
|
128
|
-
const data = body.data !== undefined ? body.data : body;
|
|
129
|
-
return Array.isArray(data) ? data : null;
|
|
130
|
-
} catch (err) {
|
|
131
|
-
return null;
|
|
132
|
-
} finally {
|
|
133
|
-
clearTimeout(timer);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function listGamesLocal() {
|
|
138
|
-
const entries = await fs.promises.readdir(DATA_DIR, { withFileTypes: true });
|
|
139
|
-
const games = [];
|
|
140
|
-
for (const entry of entries) {
|
|
141
|
-
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'game-codes') continue;
|
|
142
|
-
const metaPath = path.join(DATA_DIR, entry.name, 'meta.json');
|
|
143
|
-
if (!fs.existsSync(metaPath)) continue;
|
|
144
|
-
const raw = await fs.promises.readFile(metaPath, 'utf8');
|
|
145
|
-
games.push(JSON.parse(raw));
|
|
146
|
-
}
|
|
147
|
-
return games;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function games() {
|
|
151
|
-
const fromApi = await fetchGamesFromApi();
|
|
152
|
-
if (fromApi) return fromApi;
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
return await listGamesLocal();
|
|
156
|
-
} catch (err) {
|
|
157
|
-
return { error: err.message };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async function load(game, type, name) {
|
|
162
|
-
if (!game || !type || !name) {
|
|
163
|
-
return { error: 'game, type, and name are all required' };
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const fromApi = await fetchFromApi(game, type, name);
|
|
167
|
-
if (fromApi) return fromApi;
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
return await loadLocal(game, type, name);
|
|
171
|
-
} catch (err) {
|
|
172
|
-
return { error: err.message };
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async function loadGameMeta(game) {
|
|
177
|
-
if (!game) return { error: 'game is required' };
|
|
178
|
-
|
|
179
|
-
const fromApi = await fetchFromApi(game, 'meta', game);
|
|
180
|
-
if (fromApi) return fromApi;
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const filePath = path.join(DATA_DIR, game.toLowerCase(), 'meta.json');
|
|
184
|
-
if (!fs.existsSync(filePath)) {
|
|
185
|
-
throw new Error(`meta info for game "${game}" not found`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const raw = await fs.promises.readFile(filePath, 'utf8');
|
|
189
|
-
return JSON.parse(raw);
|
|
190
|
-
} catch (err) {
|
|
191
|
-
return { error: err.message };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
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
|
-
|
|
205
|
-
module.exports = {
|
|
206
|
-
Games: () => games(),
|
|
207
|
-
Game: (game) => loadGameMeta(game),
|
|
208
|
-
Worlds: (game, name) => load(game, 'worlds', name),
|
|
209
|
-
Groups: (game, name) => load(game, 'groups', name),
|
|
210
|
-
Players: (game, name) => load(game, 'players', name),
|
|
211
|
-
Avatars: (game, name) => load(game, 'avatars', name),
|
|
212
|
-
Characters: (game, name) => load(game, 'characters', name),
|
|
213
|
-
Get: (game, type, name) => load(game, type, name),
|
|
214
|
-
List: (game, type) => list(game, type),
|
|
215
|
-
CacheInfo: () => cacheInfo(),
|
|
216
|
-
|
|
217
|
-
// Game redeem/creator code API helpers. These also use live APINODE first and
|
|
218
|
-
// automatically fall back to the daily mirrored data/game-codes snapshot.
|
|
219
|
-
Codes: gameCodes.Codes,
|
|
220
|
-
CodesStatus: gameCodes.CodesStatus,
|
|
221
|
-
GameCodes: gameCodes.GameCodes,
|
|
222
|
-
GameCode: gameCodes.GameCode,
|
|
223
|
-
CodeCacheInfo: gameCodes.CodeCacheInfo
|
|
224
|
-
};
|
|
4
|
+
module.exports = Object.freeze({ createClient, createWarframe, createVRChat, listProviders, providers });
|
package/package.json
CHANGED
|
@@ -1,48 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
2
|
+
"name": "@nekosuneprojects/gaming-infos",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Modular multi-game information SDK using independently versioned Warframe and VRChat providers.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test",
|
|
12
|
+
"check": "node --check index.js && node --check src/registry.js && node --check src/providers/warframe.js && node --check src/providers/vrchat.js",
|
|
13
|
+
"prepack": "npm run check && npm test"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/NekoSuneProjectsNPM/gaming-infos.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/NekoSuneProjectsNPM/gaming-infos/issues"
|
|
21
|
+
},
|
|
22
|
+
"funding": "https://github.com/NekoSuneProjectsNPM/gaming-infos?sponsor",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"gaming",
|
|
25
|
+
"game-data",
|
|
26
|
+
"modular",
|
|
27
|
+
"warframe",
|
|
28
|
+
"warframe-api",
|
|
29
|
+
"worldstate",
|
|
30
|
+
"redeem-codes",
|
|
31
|
+
"vrchat",
|
|
32
|
+
"vrchat-api",
|
|
33
|
+
"worlds",
|
|
34
|
+
"groups",
|
|
35
|
+
"avatars",
|
|
36
|
+
"avatar-search"
|
|
37
|
+
],
|
|
38
|
+
"author": "NekoSuneVR",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@nekosuneprojects/warframe-api": "1.0.1",
|
|
42
|
+
"@nekosuneprojects/vrchat-api": "1.0.0"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"index.js",
|
|
46
|
+
"src/",
|
|
47
|
+
"README.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"registry": "https://registry.npmjs.org/"
|
|
54
|
+
}
|
|
48
55
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const VRChat = require('@nekosuneprojects/vrchat-api');
|
|
3
|
+
|
|
4
|
+
function create(options = {}) {
|
|
5
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
6
|
+
throw new TypeError('VRChat options must be an object');
|
|
7
|
+
}
|
|
8
|
+
return VRChat(options);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = Object.freeze({ id: 'vrchat', create });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Warframe = require('@nekosuneprojects/warframe-api');
|
|
3
|
+
function create(options = {}) {
|
|
4
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Warframe options must be an object');
|
|
5
|
+
return Warframe(options);
|
|
6
|
+
}
|
|
7
|
+
module.exports = Object.freeze({ id: 'warframe', create });
|
package/src/registry.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const warframe = require('./providers/warframe');
|
|
3
|
+
const vrchat = require('./providers/vrchat');
|
|
4
|
+
|
|
5
|
+
const providers = Object.freeze({ warframe, vrchat });
|
|
6
|
+
|
|
7
|
+
function listProviders() { return Object.keys(providers); }
|
|
8
|
+
|
|
9
|
+
function createClient(game, options = {}) {
|
|
10
|
+
if (typeof game !== 'string' || !game.trim()) {
|
|
11
|
+
throw new TypeError('Game must be a non-empty string');
|
|
12
|
+
}
|
|
13
|
+
const key = game.trim().toLowerCase();
|
|
14
|
+
if (!Object.hasOwn(providers, key)) {
|
|
15
|
+
throw new RangeError(`Game provider not installed: ${game}`);
|
|
16
|
+
}
|
|
17
|
+
return providers[key].create(options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createWarframe(options = {}) { return createClient('warframe', options); }
|
|
21
|
+
function createVRChat(options = {}) { return createClient('vrchat', options); }
|
|
22
|
+
|
|
23
|
+
module.exports = { createClient, createWarframe, createVRChat, listProviders, providers };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
name: Node.js Package GitHub Mode
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types:
|
|
6
|
-
- published
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: write
|
|
10
|
-
issues: write
|
|
11
|
-
pull-requests: write
|
|
12
|
-
packages: write
|
|
13
|
-
|
|
14
|
-
jobs:
|
|
15
|
-
build:
|
|
16
|
-
runs-on: ubuntu-latest
|
|
17
|
-
steps:
|
|
18
|
-
- name: Checkout repository
|
|
19
|
-
uses: actions/checkout@v4
|
|
20
|
-
|
|
21
|
-
- name: Setup Node.js
|
|
22
|
-
uses: actions/setup-node@v4
|
|
23
|
-
with:
|
|
24
|
-
node-version: 24
|
|
25
|
-
|
|
26
|
-
- name: Install dependencies
|
|
27
|
-
run: npm ci
|
|
28
|
-
|
|
29
|
-
publish-gpr:
|
|
30
|
-
needs: build
|
|
31
|
-
runs-on: ubuntu-latest
|
|
32
|
-
steps:
|
|
33
|
-
- name: Checkout repository
|
|
34
|
-
uses: actions/checkout@v4
|
|
35
|
-
|
|
36
|
-
- name: Setup Node.js for GitHub Packages
|
|
37
|
-
uses: actions/setup-node@v4
|
|
38
|
-
with:
|
|
39
|
-
node-version: 24
|
|
40
|
-
registry-url: https://npm.pkg.github.com/
|
|
41
|
-
|
|
42
|
-
- name: Install dependencies
|
|
43
|
-
run: npm ci
|
|
44
|
-
|
|
45
|
-
# Read version from package.json
|
|
46
|
-
- name: Read version from package.json
|
|
47
|
-
id: read_version
|
|
48
|
-
run: |
|
|
49
|
-
VERSION=$(node -p "require('./package.json').version")
|
|
50
|
-
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
|
51
|
-
|
|
52
|
-
# Publish package to GitHub Packages
|
|
53
|
-
- name: Publish to GitHub Packages
|
|
54
|
-
run: npm publish
|
|
55
|
-
env:
|
|
56
|
-
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
name: Node.js Package NPMJS
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types:
|
|
6
|
-
- published
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: write
|
|
10
|
-
issues: write
|
|
11
|
-
pull-requests: write
|
|
12
|
-
packages: write
|
|
13
|
-
|
|
14
|
-
jobs:
|
|
15
|
-
release:
|
|
16
|
-
runs-on: ubuntu-latest
|
|
17
|
-
steps:
|
|
18
|
-
# Checkout project repository
|
|
19
|
-
- name: Checkout
|
|
20
|
-
uses: actions/checkout@v4
|
|
21
|
-
|
|
22
|
-
# Setup Node.js environment
|
|
23
|
-
- name: Setup Node.js
|
|
24
|
-
uses: actions/setup-node@v4
|
|
25
|
-
with:
|
|
26
|
-
node-version: '24'
|
|
27
|
-
registry-url: 'https://registry.npmjs.org/'
|
|
28
|
-
|
|
29
|
-
# Read current package version
|
|
30
|
-
- name: Read version from package.json
|
|
31
|
-
id: read_version
|
|
32
|
-
run: |
|
|
33
|
-
VERSION=$(node -p "require('./package.json').version")
|
|
34
|
-
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
|
35
|
-
|
|
36
|
-
# Publish version to npm
|
|
37
|
-
- name: Publish to npm
|
|
38
|
-
run: npm publish --access public
|
|
39
|
-
env:
|
|
40
|
-
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
|
|
41
|
-
|
|
42
|
-
# Read the release notes from the already-versioned changelog.
|
|
43
|
-
- id: get_changelog
|
|
44
|
-
name: Get version changelog
|
|
45
|
-
shell: bash
|
|
46
|
-
run: |
|
|
47
|
-
CHANGELOG=$(awk -v version="${VERSION}" '
|
|
48
|
-
$0 ~ "^## \\[?" version "\\]?([[:space:]]+-|$)" { found=1; next }
|
|
49
|
-
found && /^## / { exit }
|
|
50
|
-
found { print }
|
|
51
|
-
' CHANGELOG.md)
|
|
52
|
-
|
|
53
|
-
if [[ -z "${CHANGELOG}" ]]; then
|
|
54
|
-
echo "No changelog section found for version ${VERSION}" >&2
|
|
55
|
-
exit 1
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
echo 'changelog<<CHANGELOG_EOF'
|
|
60
|
-
echo "${CHANGELOG}"
|
|
61
|
-
echo 'CHANGELOG_EOF'
|
|
62
|
-
} >> "${GITHUB_OUTPUT}"
|
|
63
|
-
|
|
64
|
-
# Update GitHub release
|
|
65
|
-
- name: Create or update GitHub Release
|
|
66
|
-
uses: softprops/action-gh-release@v2
|
|
67
|
-
with:
|
|
68
|
-
tag_name: v${{ env.VERSION }}
|
|
69
|
-
body: ${{ steps.get_changelog.outputs.changelog }}
|
|
70
|
-
env:
|
|
71
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
name: Sync APINODE fallback caches
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
schedule:
|
|
6
|
-
# Daily at 03:23 UTC. GitHub Actions cron uses UTC.
|
|
7
|
-
- cron: "23 3 * * *"
|
|
8
|
-
push:
|
|
9
|
-
branches:
|
|
10
|
-
- main
|
|
11
|
-
paths:
|
|
12
|
-
- "scripts/sync-api-cache.js"
|
|
13
|
-
- "scripts/sync-gaming-info-cache.js"
|
|
14
|
-
- "scripts/sync-game-code-cache.js"
|
|
15
|
-
- "scripts/sync-all-cache.js"
|
|
16
|
-
- "scripts/cache-sync-smoke.js"
|
|
17
|
-
- "scripts/game-code-cache-smoke.js"
|
|
18
|
-
- "gamecodes.js"
|
|
19
|
-
- "index.js"
|
|
20
|
-
- "package.json"
|
|
21
|
-
- "CACHE_SYNC.md"
|
|
22
|
-
- ".github/workflows/sync-api-cache.yml"
|
|
23
|
-
|
|
24
|
-
permissions:
|
|
25
|
-
contents: write
|
|
26
|
-
|
|
27
|
-
concurrency:
|
|
28
|
-
group: gaming-infos-api-cache
|
|
29
|
-
cancel-in-progress: false
|
|
30
|
-
|
|
31
|
-
jobs:
|
|
32
|
-
sync:
|
|
33
|
-
runs-on: ubuntu-latest
|
|
34
|
-
steps:
|
|
35
|
-
- name: Checkout
|
|
36
|
-
uses: actions/checkout@v4
|
|
37
|
-
with:
|
|
38
|
-
fetch-depth: 0
|
|
39
|
-
|
|
40
|
-
- name: Use Node.js 22
|
|
41
|
-
uses: actions/setup-node@v4
|
|
42
|
-
with:
|
|
43
|
-
node-version: 22
|
|
44
|
-
|
|
45
|
-
- name: Test atomic cache behavior
|
|
46
|
-
run: npm run test:all-cache
|
|
47
|
-
|
|
48
|
-
- name: Mirror APINODE gaming-infos and game codes
|
|
49
|
-
run: npm run sync:cache
|
|
50
|
-
env:
|
|
51
|
-
GAMING_INFOS_API_BASE: https://api.nekosunevr.co.uk
|
|
52
|
-
GAMING_INFOS_API_PATH: /v5/games/api/gaming-infos
|
|
53
|
-
GAME_CODES_API_PATH: /v5/games/api/codes
|
|
54
|
-
GAMING_INFOS_CACHE_TIMEOUT_MS: 20000
|
|
55
|
-
GAMING_INFOS_CACHE_RETRIES: 2
|
|
56
|
-
GAME_CODES_CACHE_TIMEOUT_MS: 20000
|
|
57
|
-
GAME_CODES_CACHE_RETRIES: 2
|
|
58
|
-
GAME_CODES_CACHE_GAME_DELAY_MS: 150
|
|
59
|
-
|
|
60
|
-
- name: Commit fallback changes
|
|
61
|
-
shell: bash
|
|
62
|
-
run: |
|
|
63
|
-
git add -A data
|
|
64
|
-
if git diff --cached --quiet; then
|
|
65
|
-
echo "Fallback caches already match APINODE."
|
|
66
|
-
exit 0
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
git config user.name "github-actions[bot]"
|
|
70
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
71
|
-
git commit -m "chore(cache): mirror APINODE fallback data [skip ci]"
|
|
72
|
-
git push
|