@app-studio/qa-core

中文文档
0.11.6发布于 2026/11/06UNLICENSEDTypeScriptES Module近 30 天下载 2350

The bindings contract every other @app-studio/qa package reads: schema, loader, cross-process lock, command runner and the default layer stack.

原始使用文档
文档目录5 节
  1. What is here
  2. The bindings file
  3. The lock, in three decisions
  4. Design acceptance for an explicitly limited viewport
  5. Requirements

@app-studio/qa-core

The contract every other @app-studio/qa package reads.

A host repository declares its QA facts once, in qa.bindings.json at its root: which commands exist, which layers the test suite is made of, where the tests and seeds live, which ports the apps bind, which accounts the seeds create. The runner, the hooks and the doctor read that file at execution time. Nothing is copied into the host and left to go stale.

That rule is not stylistic. A QA system copied from one repository into another without its bindings rewritten produces a guard whose patterns match no file — it denies nothing while looking exactly like a guard with nothing to complain about. @app-studio/qa-doctor exists to make that state impossible; this package is the contract it checks against.

What is here

ExportPurpose
bindingsSchema, QaBindingsThe zod schema and its types. strict() throughout: an unknown key is an error, because a typo that silently does nothing is how a binding becomes a lie.
loadBindings, findBindingsFileWalk up from any directory to the repository root and validate. Returns failure as data so the doctor can report it instead of crashing.
acquireLock, withLockA cross-process lock keyed by checkout. Atomic creation; a stale lock is reported, never auto-deleted.
runCommand, startBackgroundCommand execution with signal forwarding and SIGTERM→SIGKILL shutdown.
waitForUrl, ensurePortFree, isPortFreeReadiness and port hygiene for orchestrating servers.
DEFAULT_LAYERS, selectLayers, runLayers, describeLayersThe layered walk: bottom-up, stop at the first break, say what was not run and why.
Rule, Finding, DoctorContextThe doctor's plugin contract, here so packages can contribute rules without depending on the doctor.
appStudioFeatureSpecSchema, appStudioFormSpecSchemaThe original App Studio feature/form contracts and their cross-reference validation.
compileAppStudioFeatureSpecCompiles feature.userExperience into live nominal and conditional browser paths without a duplicate scenario file.

The bindings file

{
  "$schema": "./node_modules/@app-studio/qa-core/schema/qa.bindings.schema.json",
  "version": "2",
  "repo": { "name": "my-app" },

  // The canonical registry. Layers, gates and docs reference the KEYS, never
  // the shell strings, so "what this repository runs" is written down once.
  "commands": {
    "typecheck": "pnpm typecheck",
    "test.unit": "pnpm vitest run --project unit",
    "test.integration": "pnpm vitest run --project integration",
    "test.e2e": "pnpm playwright test",
    "seed.apply": "pnpm seed",
    "seed.check": "pnpm seed:check"
  },

  // Bottom-up. `qa run` stops at the first break.
  "layers": [
    {
      "key": "logic",
      "title": "Pure logic",
      "restsOn": "nothing — no database, no application",
      "means": "a sanitiser or a validator is wrong. Nothing above can be trusted.",
      "commands": ["test.unit"]
    }
  ],

  "paths": { "seeds": "src/features/*/seed/index.ts", "tests.e2e": "tests/e2e" },
  "ports": { "api": { "port": 3000, "boundBy": "playwright.config.ts" } },
  "seedAccounts": [
    { "role": "ADMIN", "email": "[email protected]", "password": { "kind": "seeded-constant", "value": "password123456" } }
  ]
}

restsOn and means are not decoration. qa run --list prints them, and a broken layer prints its own means next to the list of layers it did not run — so a red run says which floor is broken and why the floors above it were skipped, instead of five hundred failures that are one bug wearing different names.

The lock, in three decisions

  1. Keyed by checkout, hashing the repository root into the lock filename, so two worktrees of the same project seed concurrently and two shells in the same checkout do not.
  2. Atomic creation (open(path, 'wx')). Check-then-create leaves a window in which both contenders believe they won.
  3. A stale lock is reported, never removed automatically. Two waiters can both observe a dead holder and both delete the file — and the second delete evicts a lock the first has just legitimately taken. The error names the holder and prints the rm command; a human decides.

Design acceptance for an explicitly limited viewport

Ordinary design-acceptance records require at least two distinct viewport labels. A signed contract that authorizes exactly one viewport can be represented by the optional scope field:

{
  "scope": {
    "kind": "single-viewport",
    "viewport": { "name": "desktop", "width": 1440, "height": 900 },
    "contract": {
      "file": "knowledge/design/feature-ui.md",
      "sha256": "<SHA-256 of the signed contract>"
    },
    "reason": "The signed contract limits this review to the desktop viewport."
  }
}

This is a scope fragment, not an acceptance verdict. The existing record still needs its reviewer, time, states, source digests and evidence for all three checks. checks.responsive.viewports must contain exactly the scope's label; width and height are positive integers describing the authorized viewport. The contract file must also appear in sources with the identical digest. The CLI checks that digest against the file on disk, as it does for every source. Recording refreshes source hashes but does not silently update the scope's contract hash: a changed contract requires an explicit scope review.

The schema does not interpret the contract's prose or inspect screenshots. The Designer verifies that the scope and dimensions match the signed contract and the observed evidence. Restricting the responsive scope does not waive accessibility, design-system, verdict, fixture-id or source-freshness checks. Duplicate labels are rejected for both ordinary and scoped records.

Requirements

Node.js 20 or newer.

版本与发布记录

@app-studio/qa-core 共发布 34 个版本,首次发布于 2026/08/09;过去 12 个月发布了 34 个版本。

版本发布日期距今
0.11.6latest2026/11/061 天前
0.11.32026/11/017 天前
0.11.22026/10/2217 天前
0.11.12026/10/1622 天前
0.10.22026/10/0830 天前
0.10.3next2026/10/0830 天前

关于 @app-studio/qa-core 的常见问题

@app-studio/qa-core 是什么?

The bindings contract every other @app-studio/qa package reads: schema, loader, cross-process lock, command runner and the default layer stack.

如何安装 @app-studio/qa-core?

在项目目录运行 npm install @app-studio/qa-core,或使用 pnpm add / yarn add / bun add。

@app-studio/qa-core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@app-studio/qa-core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@app-studio/qa-core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@app-studio/qa-core 可以商用吗?

@app-studio/qa-core 使用 UNLICENSED 许可。作者声明为 UNLICENSED,即不授予公开使用许可(与公共领域的 Unlicense 不同)。使用前需获得作者授权。以上为概括,不构成法律意见,请以许可原文为准。

@app-studio/qa-core 的下载量有多少?

根据 npm 官方下载统计,@app-studio/qa-core 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,350 次,日均约 78 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@app-studio/qa-core 还在维护吗?

最新版本 0.11.6 发布于 2026/11/06(1 天前);至今共发布 34 个版本,其中过去 12 个月发布了 34 个;首次发布于 2026/08/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。