@maixio/nintendo-eshop-tool
v0.1.3
Published
Nintendo eShop search and lookup tool with separated regional adapters
Readme
nintendo-eshop-tool
Nintendo eShop 查询工具,使用 Bun + TypeScript 编写。它把不同国家/地区的实现拆成独立 adapter,便于后续新增区域或修复单一区域的网站变化。
支持区域
hk: 香港us: 美国jp: 日本gb: 英国au: 澳大利亚
使用
cd nintendo-eshop-tool
bun install
bun run src/cli.ts search jp "ゼルダ" --limit 3
bun run src/cli.ts lookup hk 70010000119464
bun run src/cli.ts appid jp 70010000124937
bun run src/cli.ts resolve hk 0100616025B6C000
bun run src/cli.ts map jp 70010000027618作为 npm 包安装:
bun add @maixio/nintendo-eshop-tool作为 SDK 使用
这个项目的 SDK 入口是 src/index.ts。在 Bun / TypeScript 项目里,可以直接从包名或本地源码导入:
import * as NintendoEshop from "@maixio/nintendo-eshop-tool";
const lookupResult = await NintendoEshop.lookupItem("gb", "70010000122206", {
timeoutMs: 15_000,
});
const mapResult = await NintendoEshop.mapItemRegions("gb", "70070000036431", {
regions: ["hk", "us", "jp", "au"],
timeoutMs: 15_000,
debug: true,
});
const resolverResult = await NintendoEshop.resolveAppTitleIdToRegion(
"hk",
"0100616025B6C000",
);
const searchResult = await NintendoEshop.searchItems("jp", "KINGDOM HEARTS", {
limit: 10,
});如果不是通过 package name 引用,而是在仓库内直接调用:
import * as NintendoEshop from "./src/index.ts";推荐使用标准 ESM namespace import,把整个模块作为 NintendoEshop 使用:
searchItems(region, query, options?):搜索商品,返回SearchResponse。lookupItem(region, nsuid, options?):按 NSUID 查单个商品,返回LookupResponse。resolveAppTitleIdToRegion(region, appTitleId, options?):用 Nintendo resolver 解析 App Title ID。mapItemRegions(region, nsuid, options?):跨区映射 title / aoc / bundle。getAdapter(region)/adapters:需要直接调用单一区域 adapter 时使用。
为了兼容旧代码,入口仍保留 search、lookup、mapNintendoNsuidAcrossRegions、resolveNintendoAppTitleIdToRegionProduct 这些具名导出。也保留 nintendoEshop 对象供偏好对象式 API 的场景使用;新代码建议优先使用 import * as NintendoEshop from "@maixio/nintendo-eshop-tool"。常用类型也从入口导出,例如 NintendoItem、NintendoRegion、CrossRegionMapResponse、CrossRegionTarget、LookupOptions、SearchOptions。
region 支持 hk、us、jp、gb、au。options.timeoutMs 是单个 HTTP 请求包含重试在内的总预算;NintendoEshop.mapItemRegions 额外支持 regions 限定目标区域。Node.js 项目若不使用 Bun 或 TypeScript runtime,需要先增加编译产物再从 JavaScript 导入。
无参数运行会进入交互模式:
bun run src/cli.tsAgent Skill 安装
项目内置一个面向 agent 使用的 skill,源文件位于:
skills/nintendo-eshop-tool/源码仓库开发时,推荐用符号链接安装到目标 agent 的固定 skills 目录,而不是复制文件。这样 skill 会随本项目更新,agent 侧无需重复同步:
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
ln -s "$(pwd)/skills/nintendo-eshop-tool" \
"${CODEX_HOME:-$HOME/.codex}/skills/nintendo-eshop-tool"从 npm 包使用时,skill 会和 CLI 一起发布。把包内 skill 链接到目标项目的 agent skills 目录,并通过项目本地 bin 调用 CLI:
bun add -d @maixio/nintendo-eshop-tool
mkdir -p .agents/skills
ln -s "$(pwd)/node_modules/@maixio/nintendo-eshop-tool/skills/nintendo-eshop-tool" \
.agents/skills/nintendo-eshop-tool
./node_modules/.bin/nintendo-eshop --help安装后,支持 skills 的 agent 可以通过 $nintendo-eshop-tool 读取该工具的 CLI 使用方式,包括 search、lookup、appid、resolve、map、跨区 bundle 映射结果解读,以及常见网络不确定性的处理建议。
目录结构
src/
cli.ts # CLI 和交互入口
index.ts # SDK 风格导出
cross-region.ts # title/aoc/bundle 跨区映射
types.ts # 公共类型
regions/ # 各国家/地区独立实现
hk.ts
us.ts
jp.ts
gb.ts
au.ts
shared/ # HTTP、文本清洗、App API、页面解析等共用逻辑
nsuid.ts # NSUID 分类、路径和美区 productId 规则设计原则
- 区域代码分离:不同地区网站结构不同,避免把所有分支塞进一个大文件。
- 公共逻辑只放稳定能力:HTTP 超时/重试、文本清洗、ZNEJ App API 解析。
- CLI 保持薄层:命令行只负责参数、交互和输出 JSON。
- 默认快速失败:网络请求有超时和有限重试,避免批量任务长时间挂住。
--timeout 是单个 HTTP 请求包含全部重试在内的总预算,而不是每次重试各自拥有一份完整预算。遇到 429/5xx 时会有限重试,并优先遵守服务端的 Retry-After。
区域数据源
| 区域 | 主要数据源 | 用途 | | --- | --- | --- | | HK | 香港搜索 API、EC 商品页 | 搜索、详情、bundle 内容 | | US | ZNEJ App API、Algolia、EC/Next.js 页面 | title ID、详情、publisher | | JP | ZNEJ App API、日本公开搜索、store-jp Shopper API | title ID、详情、publisher | | GB | ZNEJ App API、Nintendo Europe Solr、UK Store Catalog(不稳定补充) | title ID、详情、真实商品 URL;实体商店目录仅用于补充部分商品的 appId/硬件信息 | | AU | GB locale 的 ZNEJ App API、AU EC 商品页 | 共享 NSUID 数据、AU 页面详情 |
页面解析的离线样例位于 tests/fixtures/。任天堂页面结构变化时,先更新 fixture 并运行 bun test,再运行联网回归确认真实端点行为。
UK Store Catalog 属于英国实体/零售商店目录,不是完整 eShop 数据源。大量数字版、第三方商品和 bundle 可能返回空结果,因此实现只把它作为 GB lookup 的可选补充源,任何失败或缺失都不会阻断主要查询路径。
Application Title ID 解析
resolve 命令使用 Nintendo eShop 的官方跳转入口解析 Application Title ID 到指定区域的商品页:
bun run src/cli.ts resolve <hk|us|jp|gb|au> <application-title-id>
bun run src/cli.ts resolve 0100616025B6C000 HK它请求的 resolver URL 形如:
https://ec.nintendo.com/apps/0100616025B6C000/HK返回内容包含 resolver URL、最终 URL、能从 URL 提取到的 NSUID、商品类型线索或美区 slug。这个入口不是公开文档化 JSON API,主要适合基础游戏 Application Title ID;DLC、bundle、未上架区域、独立区域 ID、Queue-it 拦截等情况都可能无法解析出商品。
跨区映射
map 命令输入一个区域的 NSUID,并尽量映射到其他区域的同款商品:
bun run src/cli.ts map <hk|us|jp|gb|au> <nsuid>title 和能拿到共享 title/right ID 的 aoc 会走 Application Title ID resolver。bundle 通常没有可直接解析的 Application Title ID,所以会解析包内 bundleItems。映射时优先只使用本体 title 的共享 app/title/right ID 找到目标区本体,并从目标区本体页的 includedBundleItems 或页面线索验证关联 bundle;只有直接路径未命中时,才继续解析 AOC 和执行搜索回退。最后按组件 appId 集合、目标组件 NSUID 和 bundle 结构确认同款。
候选数量较多时,映射内部会先使用 ZNEJ App API 批量获取匹配所需的 bundle identity,再对缺失或不完整的候选执行受限并发的完整 lookup,避免搜索回退瞬间产生大量页面请求。
输出中:
appId:只表示title/aoc自身的 Application Title ID。bundleComponentAppIds:bundle 内可跨区映射的组件 appId/titleId/rightId。matchedBy:app-title-resolver、related-version、app-id-search、bridge-title-search、title-identity-search、regional-title-search或bundle-contents。related-version表示 resolver 先落到同页旧版/升级包入口,再通过页面版本关系修正到目标商品;bridge-title-search表示目标区 resolver 未给出可靠 NSUID 时,先借助另一个已解析区域的标题做搜索,再用 appId 校验;title-identity-search表示区域 appId 分裂且目标商品缺少 appId 时,用商品类型、硬件与拉丁标题 token 做唯一确认;regional-title-search仅用于区域间 App ID 整组不同的 bundle 组件,并要求唯一标题、商品类型与硬件一致,最终仍需完整 bundle 内容验证。resolved:TypeScript 中可作为判别字段;未解析目标保证包含reason,bundle 命中保证包含nsuid、candidateCount和componentAppIds。
注意:部分 bundle 会包含 7009... 这类奖励/消耗型内容,它们通常没有共享 appId;当前实现会保留这些包内项目作为线索,但不把它们作为跨区硬匹配键。
验证
bun run typecheck
bun test
bun run test:network
bun run bench:network
bun run smoke:jpbun test 默认跳过真实 Nintendo 网络测试,避免普通本地测试受网络或商品上下架影响。bun run test:network 会运行固定 NSUID 的跨区映射和 lookup 回归用例。
bun run bench:network 会顺序运行核心性能样本,验证源商品和预期跨区 NSUID 后,再输出 lookup、普通 title map、bundle map 的平均值、P50、P95 和最大耗时。空结果或错误映射会标记为 ERROR,不会作为快速成功样本污染基准。使用 bun run bench:network --full 可运行所有网络回归样本。默认性能阈值为:
- lookup:5 秒
- title/aoc map:10 秒
- bundle map:20 秒
使用 bun run bench:network --fail-on-threshold 可在超过阈值时返回非零退出码。benchmark 参数同时支持 --kind=map 和 --kind map 两种风格。
排查单次慢查询时使用 --debug。map 会额外输出源 lookup、各目标区组件解析、直接候选发现、批量/完整 hydration、搜索回退等阶段耗时:
bun run src/cli.ts map us 70070000037477 --debug --compact