npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

miaoda-game-devkit

v0.10.0

Published

Shared React and Phaser game lint plus deterministic testing tools for Miaoda games

Readme

miaoda-game-devkit

妙搭 Phaser 4 与 React 游戏模板的共享开发基础设施。

该开发工具包提供:

  • React 游戏的生产/手动时钟、Strict Mode 安全 Controller ownership、JSDOM Vitest 配置和自动收集运行错误的 Testing Library setup,分别从 miaoda-game-devkit/reactmiaoda-game-devkit/react/testingmiaoda-game-devkit/react/vitest-config 导入;
  • React 的 playthroughTest 与项目级 reporter 自动验证连续的 enter/milestone/finish 玩法阶段、真实 DOM 输入或确定性推进、有界结果、结果断言和阶段状态变化;聚焦运行只提示未检查, 完整运行缺少有效流程时失败;

playthroughTest 的主流程回调只需要编排 enter、至少三个唯一命名的 milestonefinishenter 必须声明真实输入 act;后续阶段声明 act 或确定性 step。每个阶段还要 提供初始为 false 的有界 until,以及使用 test-bound expectassert。DevKit 在验证阶段 前后 DOM 或权威 observe 状态确实变化后自动提交阶段证据。每个 milestone 使用非空且 唯一的领域名称,表示一段独立的玩法进展。finish.kind 对有限结果使用 terminal,对无尽或 沙盒游戏的有意义进展使用 progress。大型游戏保留一条最短关键流程;分支、关卡规则和恢复 清理由专门测试覆盖。不要再次调用 userEvent.setup()。 Playthrough 测试始终以非并发任务运行,且只统计当前阶段 act 执行窗口内的输入事件,避免 同一 JSDOM 中其他测试或后台事件污染玩法证据。 时间、帧或自定义 scheduler 流程必须显式传入确定性 step;精确物理时间、暂停恢复 或调度清理测试可使用 ManualGameClock。Canvas/Controller 流程在 playthroughTest 上声明一次 observe,返回 JSON 可序列化的权威 Telemetry 状态;框架自动比较流程状态并复用为超时诊断。

失败的 production playthrough 会额外输出一条有界 TRACE::记录已完成 milestone、当前阶段、 阶段前后可观测状态与推进步数。通过流程不输出 TRACE,避免增加 常规 CI 噪声。显式启用 Vitest retry 时,每次尝试会重置证据,最终报告只展示最后一次尝试的 CAUSE 与 TRACE;repeat 同样为每轮建立新证据。测试超时或取消会中止后续有界推进,并通过 Vitest 的 test-local failure hook 在 worker 最终同步前保留活动阶段;若测试主体已完成、随后由 afterEach 或运行时错误判失败,reporter 仍会从 task metadata 恢复这条流程证据。状态优先来自 observe,没有 Telemetry 时才使用有界 DOM 摘要;不要把 TRACE 当作新的断言或第二份业务状态。

Vitest 的 fake timers(包括 vi.advanceTimersToNextFrame())适合在 devkit 自身的 专门 scheduler 合约中使用,但不在所有 production playthrough 中全局启用;React Testing Library 的 asyncWrapper 和 user-event 内部 timer 会因此互相影响。使用自定义 scheduler、Worker 或未通过浏览器 timer 接入的引擎时,应传显式 step 并通过 diagnostics 暴露权威状态;已声明 observe 的流程无需重复提供 diagnostics

  • React Vitest 在项目声明 Phaser 3 时使用其 browser bundle,并提供仅供模块能力探测的 最小 Canvas 2D 边界,使 Phaser 3 代码不会阻断普通 JSDOM 测试;Storage 使用 JSDOM 按 origin 提供的原生实现,Node 25+ worker 会关闭冲突的 Node Web Storage, 并在每个用例后清理;该 Canvas 适配不执行真实绘制, 也不提供 Canvas/WebGL 像素证明;

  • React Vitest 统一统计 src/game/core/src/game/runtime/ 的逐文件覆盖率;Core 使用 50/40 分支基线,Runtime 使用 40/30 分支基线。模板通过 pnpm test 启用 V8 coverage,项目不能覆盖范围或降低阈值;

  • 支持 DOM 鼠标、键盘和触摸输入的确定性 Phaser 无头测试宿主;

  • 不依赖渲染器的运行时、交互区域和文字健康检查;

  • 只在 devkit 自身 CI 中运行、不会发布到业务项目的通用 Vitest 契约测试;

  • 显式隔离的 miaoda-react-game-lintmiaoda-phaser-game-lint 命令, 共享 TypeScript、Biome、Tailwind 和 Oxlint 检查,同时分别校验 React 与 Phaser 4 配置边界。

  • 统一的 defineGameVitestConfig 配置工厂,固定 Phaser 无头测试依赖的基础配置, 同时允许每个游戏声明 Scene 审计范围,并配置额外 alias、setup 文件和超时。

  • 与 Vite defineConfig 同签名的 defineGameViteConfig 配置工厂,统一维护生产 Scene 的运行时基础能力,同时完整保留游戏自己的 Vite 配置。

互动游戏的长流程优先使用游戏自己暴露的 globalThis.gameTelemetry。稳定 façade 通常调用 installGameTelemetry(this.game, telemetry) 绑定 Game 生命周期;只有刻意局部的 façade 才绑定 Scene 生命周期。开发工具包只规定最小 外壳:version: 1read.session() 和游戏自有的 controlsfixtures 可选。快照字段、 控制方法名和 Core 适配由游戏负责,controls 必须调用与生产输入共享的权威命令。Telemetry 不会复制 host.stepFrames()、DOM 输入或 Scene 生命周期 API。

createHeadlessGame 分别记录测试请求和 Phaser 公开事件确认的执行结果。输入必须被 InputPlugin 消费,完整帧必须由活动 Scene 收到 UPDATE,Arcade 单步必须产生 WORLD_STEP,Scene 转场和 restart 必须进入目标生命周期,才能满足对应的 host.assertGameplayEvidence() 门禁。业务 checkpoint 和 authoritative state 仍必须由 测试显式断言,devkit 不从引擎事件推断玩法语义。

HEADLESS host 会把 Phaser 自身的 warning/error、Loader 文件失败、漏掉 load.start() 的队列、 未注册 Scene 命令和可见对象的 __MISSING 纹理升级为测试失败。只有经过确认的引擎 warning 才能在创建 host 时通过 ignoreEngineWarnings: [/pattern/] 显式豁免;资源 URL 仍须通过浏览器验证,因为 JSDOM 图片加载只支持测试用的 data:image/ URL。刻意暂存 资源供稍后加载的 Scene 可以通过 allowIdleLoaderQueues: ["SceneKey"] 精确声明。

玩法测试应优先使用 gameplayTest。它是玩法 contract 的唯一权威声明,在执行阶段绑定 HEADLESS host,并在 onTestFinished 校验 Ledger。vitest.config.ts 默认只列生产 Scene, 不再复制 contract id、测试文件和证据要求。Reporter 从测试 metadata 汇总交互项目的最低 基线、失败、证据不足、Scene 未访问和 registry 不一致。旧项目仍可提供显式 gameplayAudit.contracts 清单,但新游戏通常不需要。

两套模板直接调用各自的命令行程序:

{
  "scripts": {
    "lint": "miaoda-react-game-lint"
  }
}

Phaser 4 模板对应使用 miaoda-phaser-game-lint。命令类型不通过依赖猜测,因此 React 模板即使预装 Phaser 3 及相关插件,也不会解析 Phaser 或运行 Phaser 4 检查。旧的 miaoda-game-lint 不再发布,避免在同时包含 React 与 Phaser 依赖的项目中产生歧义。

可编辑的游戏机制源码

Devkit 发布统一的 miaoda 命令。裸 miaoda-game-* 包名默认从公开源码索引解析为 当前稳定版本,Devkit 直接下载并校验该包及其传递机制依赖的源码归档;消费项目自己的 pnpm 10 或 11 只负责最终 workspace 联动和普通第三方依赖安装。生产 TypeScript 源码写入 src/game-mechanics/<包名>/.miaoda/mechanics-source.json 只保存版本、索引地址与安装摘要, 不保存第二份源码。

pnpm exec miaoda mechanics --help
pnpm exec miaoda mechanics list
pnpm exec miaoda mechanics list --domain=grid --engine=react
pnpm exec miaoda mechanics add miaoda-game-beam-core
pnpm exec miaoda mechanics add [email protected] \
  --source-index=https://public.example.com/game-mechanics/stable.json
pnpm exec miaoda mechanics status

list 只列出稳定索引中实际可安装的包,并从 Devkit 自带的 capabilities JSON 动态汇总 domain 的 owns 能力;带筛选时会显示包负责和不负责的边界、使用指引及可测试性。它不会安装 源码,也不会维护另一份容易过时的文字目录。先选择覆盖需求的最小包集合,再单独运行 add, 成功后阅读生成的 src/game-mechanics/README.md

默认索引是 https://resource-static.bj.bcebos.com/miaoda-game/stable.json。只有调试、测试或 私有镜像场景才需要通过 --source-indexMIAODA_MECHANICS_INDEX_URL 覆盖。

重复添加同版本不会覆盖源码;status 报告 cleanmodifiedmissing。更新到不同版本时, 若本地源码已修改,命令会在写入前失败。包内的单元测试、Vitest 配置、构建输出和 node_modules 不会复制进游戏项目,避免它们进入游戏自身的 TypeScript 编译范围。索引中的相对 TGZ URL 以 stable.json 为基准解析;每个版本必须提供精确的机制依赖和 SHA-256。机制源码只通过该索引 解析,不回退 npm registry,也不接受绕过索引的直接 TGZ 或本地包路径。

开发工具包自行维护并精确固定 Vitest 等工具版本,从自身依赖中解析可执行文件,使用方不需要重复声明 Biome、Oxlint 或 tsgo。仓库中保留稳定的命令入口,因此 dist/ 尚未生成时,pnpm 也能正确建立命令链接;工作区开发状态下由 Nx 在模板检查前构建开发工具包,发布包则 直接携带运行时 dist/。打包消费者契约会验证固定 Vitest 版本以及 reporter 的通过、聚焦、 缺失流程、retry 失败诊断和 hook 失败诊断行为。开发工具包内部的 *.test.* 与构建后的 *.contract.mjs 都不会 进入 npm 包;这些契约只由 devkit 自己的测试目标执行。

monorepo 中的模板可以放置只包含 extends: ["miaoda-game-devkit/biome"] 的薄配置, 用于让 Biome 从模板 package.json 判断依赖声明。规则本身仍由本开发工具包维护;生成后的 独立模板位于仓库根目录,不需要这个 workspace 定位配置。

模板的 tsconfig.json 应继承 miaoda-game-devkit/tsconfig-base,只保留源码范围和路径 别名等项目差异。TypeScript 严格模式、bundler 模块解析和无输出检查等平台基线由开发 工具包集中维护,两套模板专用 lint CLI 会直接检查模板的这个配置。

Phaser4 模板的 vite.config.ts 应从 miaoda-game-devkit/vite 导入 defineGameViteConfig,调用方式 与 Vite 的 defineConfig 相同,支持配置对象、Promise、同步配置函数和异步配置函数:

export default defineGameViteConfig({
  plugins: [miaodaDevPlugin()],
});

该入口只增强 src/scenes 中对 Phaser 的导入。游戏 Scene 仍按标准方式继承 Phaser.Scene;devkit 在 Scene 创建前提供 RexUI,并在创建后验证业务确实完成了 RexUI 根布局。RexUI 的加载、Phaser 4.0 兼容和 Scene Plugin 生命周期属于 devkit 责任,游戏 配置不重复注册。

具体游戏的玩法测试仍由模板使用方放在 tests/。运行时测试从本开发工具包 导入 createHeadlessGame;有限目标游戏通过合法 Telemetry controls 走到正常成功终局, 无尽或沙盒游戏证明有意义的持续进展。短输入测试从入口操作走到一个主要操作,用真实 DOM 事件证明生产输入接线,并通过 Telemetry 断言业务状态变化。 同步帧逻辑使用 stepFrames()update() 返回 Promise 时使用 await stepFramesAsync(),其他同步操作启动生命周期 Promise 后使用 await settle()

输入驱动会把 DOM 兼容事件发送到 Phaser 实际配置的输入目标,并在每次事件后检查异步 错误和运行时健康状态。坐标参数使用 Phaser 游戏坐标:

host.stepFrames();
await host.input.mouse.click(100, 80);

await host.input.keyboard.down(Phaser.Input.Keyboard.KeyCodes.RIGHT);
host.stepFrames(10);
await host.input.keyboard.up(Phaser.Input.Keyboard.KeyCodes.RIGHT);

await host.input.touch.start(40, 40);
await host.input.touch.move(100, 80);
await host.input.touch.end(100, 80);

clickpresstap 适合离散命令。需要验证按住、拖动或滑动时,应分别调用开始和 结束方法,并在两者之间推进完整 Phaser 帧。JSDOM 没有布局和真实渲染器;这些驱动验证 DOM 到 Phaser 的输入、命中测试和游戏状态变化,不验证 Canvas/WebGL 像素、CSS 布局、 浏览器焦点或真实触摸策略。

模板的 vitest.config.ts 应从 miaoda-game-devkit/vitest-config 导入 defineGameVitestConfig。JSDOM 环境、Phaser 浏览器构建别名、通用 setup 和 mock 隔离 以及 Scene coverage 均由开发工具包统一维护。模板必须传入 projectRootgameplayAudit;默认只列生产 Scene,关键流程及证据要求由 gameplayTest 自己声明一次。 确有需要时,可以通过 aliasesadditionalSetupFilestestTimeouthookTimeout 增加安全的项目配置,不能覆盖测试范围、运行环境、审计 reporter 或 coverage 基线。

gameplayTest 会自动要求访问其声明的 Scene 并完成 host 销毁。玩法进展、恢复和终局应在 断言 authoritative state 后调用 host.checkpoint("progress")host.checkpoint("recovery") 等稳定检查点,并在对应 gameplayTest 中通过 requireCheckpoint 声明。 运行结束会输出稳定的 GAMEPLAY_AUDIT: ALL CONTRACTS PASSEDGAMEPLAY_AUDIT: ... FAILED 标识。该标识只表示玩法证据审计;pnpm test 的整体结果 必须以最后的 TEST_RESULT: PASSTEST_RESULT: FAIL 为准。失败报告按根因合并问题并 给出直接修复步骤;测试文件无法加载或配置要求的测试尚未建立时,不再继续报告 Scene 注册等派生问题。先处理第一类错误,再重新运行 pnpm test

Phaser Vitest 配置使用 minimal reporter;React Vitest 配置使用无 ANSI 的精简 reporter。 两者都保留失败定位、coverage 摘要和最终机器标识,同时避免输出完整 DOM 快照、测试列表和逐文件噪声。

发布顺序

  1. 在开发工具包中运行类型检查和测试。pnpm test 会额外生成经过 clean-publish 清理的 tarball,在隔离项目中按发布目录结构解包,并以 NODE_ENV=production 运行真实 React DOM 输入测试。
  2. 使用 pnpm run release:dry-run 检查将要发布的文件和清理后的 package.json
  3. 根据版本类型运行 pnpm run release:patchrelease:minorrelease:major。 发布脚本会先测试和构建,再通过 clean-publish 从临时目录发布;发布包会移除 devDependencies 和开发脚本,不会修改工作区中的原始 package.json
  4. 也可以使用 pnpm run release 发布已经手动设置好版本的开发工具包。
  5. 最后运行模板的 pnpm run build:template,让 output/ 使用已发布版本重新生成锁文件。

pnpm-workspace.yaml 已将 miaoda-game-devkit 加入 minimumReleaseAgeExclude, 因此新版本发布后无需等待 24 小时即可生成模板锁文件。--skip-install 仅用于发布前 检查 output/ 的产物边界,不能用于正式 CI 构建。