@async/witness

中文文档
0.9.0发布于 2026/08/22TypeScriptES Module构建来源已验证近 30 天下载 1.8万

See what your Vite pipeline actually did — with receipts.

原始使用文档
文档目录8 节
  1. A box in 30 seconds
  2. What a box can prove
  3. Every receipt has witnesses
  4. Why not Vitest / Playwright / Storybook?
  5. Bring your own browser
  6. Docs
  7. Status
  8. Contributing

Async Witness

See what your Vite pipeline actually did — with receipts.

Restart, hard-refresh, console.log, pray 🙏 — Vite knew what happened the whole time, it just never told you. Async Witness runs your real pipeline and writes a receipt for every run: proof you, CI, or an AI agent can act on.

⚠️ Early release — the specs are the product truth. Designed by Jack, implemented by Mythos, reviewed by Codex.

A box in 30 seconds

A box is a small file that runs inside your real Vite pipeline and asserts what the pipeline did, in the pipeline's own vocabulary:

import { box } from '@async/witness';

export default box('message updates without reload', async ({ browser, project, expect }) => {
    // Visit a real route — this auto-starts your real Vite dev server.
    const page = await browser.visit('/demo');

    // Edit a real source file, like a developer saving in their editor.
    const change = await project.edit('src/message.ts', {
        replace: ['before', 'after'],
    });

    // Declare what Vite should have done about it.
    await expect.edit(change, {
        client: { hmr: 'accepted' }, // hot update applied, no full reload
    });

    // Confirm the browser actually shows the new text.
    await expect.page.text(page, '#message', 'after');
});
witness hmr

Async Witness runs the box, restores the edited file, and writes a receipt to .witness/receipts/ — pass or fail, human- and machine-readable. If the box fails, the receipt explains why in Vite's own terms: what payload Vite sent, whether the server restarted, what the console said.

you edit src/message.ts → your real Vite pipeline (dev server, HMR, SSR, build) → receipt.json: hmr accepted, 0 reloads

What a box can prove

  • A route renders with every asset loaded and a clean console
  • A user-made UI state — clicked, typed, hovered, key-pressed, dragged, or scrolled into — survives an edit, with every interaction recorded as receipt evidence
  • An edit hot-updated, full-reloaded, or silently did nothing — per environment
  • A server-only edit left the browser alone
  • A config edit restarted the server with the new plugin active
  • SSR renders and hydrates without console errors
  • The built app behaves like dev (build + preview parity)
  • Artifacts are right — manifest entries, no stale placeholders, no node:fs in worker bundles
  • A workflow stayed inside a performance budget

Every receipt has witnesses

Assertions only answer the questions you thought to ask. The interesting failures live in what you didn't ask about — the console error that didn't break your selector, the request that died while the page still rendered, the full reload that happened to land on the right text anyway. Async Witness treats a box run like a case: the receipt is the case file, and everyone who observed the run testifies in it.

Three witnesses watch every browser box from different vantage points. The pipeline witness is Vite's server side — it knows what hot payloads were actually sent, whether the server restarted, what each environment did with your edit. The client witness is the page itself — its console, its uncaught errors, the custom events your framework fired. The driver witness watches from outside the page over CDP — failed requests with their real net::ERR_* reasons, navigations the page can't hide, the screenshots. None of them can see what the others see, which is the point: they corroborate or contradict each other independently.

Each witness gets a verdict on every box. When a box passes but a witness holds evidence against the run, the pass is marked contested:

pass noisy page records console and network evidence  [pipeline+ client! driver!]  contested
     client reported: 1 console error   driver reported: 1 failed request

Every assertion passed here. The page also logged an error and a request died — facts no assertion asked about, preserved instead of swallowed. That's the gap between "tests pass" and "the app is fine", made visible on every run.

Cross-examine any box straight from a stored receipt:

witness evidence 'noisy page'
crimes reported (2):
  ! console error   Failed to load resource: net::ERR_ABORTED   — reported by client
  ! request failed  GET http://127.0.0.1:5173/missing.js        — reported by driver

Witness identity and verdicts are plain data in the receipt JSON — stable, greppable, safe for CI and agents to consume. Color is just how your terminal paints them.

Why not Vitest / Playwright / Storybook?

They're great at what they own — but they see the page, not the pipeline that produced it. That gap is how "all tests pass" and "the app is broken" happen at the same time. Async Witness owns the pipeline: the chain from an edit to a Vite environment event to what you see, with a receipt preserving the whole story.

There's no automation framework underneath, either. Async Witness drives the browser itself over the Chrome DevTools Protocol — a couple thousand lines of WebSocket and JSON it owns outright, zero dependencies. One pooled browser serves the whole run and every box gets its own isolated browser context, the same architecture playwright uses internally, which is how witness ended up faster per test than playwright-core on every platform CI measures.

Bring your own browser

Async Witness drives a Chromium-family browser already on your machine over the Chrome DevTools Protocol — no playwright dependency, no download at install time. Discovery order:

  1. WITNESS_BROWSER_PATH — explicit override. If it's set but doesn't point at an executable, discovery fails with an error instead of silently falling through to another browser.
  2. System installs of Chrome, Edge, or Chromium, in the usual per-OS locations.
  3. Playwright's browser cache as a courtesy fallback (~/Library/Caches/ms-playwright on macOS, $XDG_CACHE_HOME or ~/.cache/ms-playwright on Linux, %LOCALAPPDATA%\ms-playwright on Windows) — full chromium-<revision> downloads only, newest first, the headless shell is skipped.

macOS, Linux, and Windows are all exercised in CI. When nothing is found, witness fails with the exact paths it checked.

Docs

  • Specs — product direction and the source of truth

Status

Built in slices. Box authoring, dev/build/preview runs, browser evidence, witness verdicts with the witness evidence drill-down, the CLI, and JSON receipts work today. The state-gallery UI, generated types, and receipt replay are coming.

Contributing

The workspace runs on pnpm (the library itself is runtime-agnostic — it runs wherever Vite runs):

pnpm install        # install dependencies
pnpm run test       # run the test suite (drives real Vite pipelines)
pnpm run check      # format check + lint + typecheck

See CONTRIBUTING.md for the full setup. Start with specs/ for intent, and .ruler/ for the working agreements.

版本与发布记录

@async/witness 共发布 5 个版本,首次发布于 2026/06/17;过去 12 个月发布了 5 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.0latest2026/08/222 个月前
0.8.02026/07/034 个月前
0.7.02026/06/224 个月前
0.6.02026/06/184 个月前
0.5.02026/06/174 个月前

关于 @async/witness 的常见问题

@async/witness 是什么?

See what your Vite pipeline actually did — with receipts.

如何安装 @async/witness?

在项目目录运行 npm install @async/witness,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:vite(^8.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 witness,可以用 npx witness 直接运行。

@async/witness 支持 TypeScript 吗?

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

@async/witness 支持 ESM 还是 CommonJS?

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

@async/witness 需要什么版本的 Node.js?

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

@async/witness 可以商用吗?

@async/witness 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@async/witness 的下载量有多少?

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

@async/witness 还在维护吗?

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