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

ushman-spector

v0.3.0

Published

Bounded WebGL buffer-prefix capture for ushman parity/handoff. Opt-in samples with attestation log.

Readme

ushman-spector

Bounded WebGL capture for the ushman parity/handoff workflow. Browser-side instrumentation hookup, opt-in bounded GPU buffer-prefix sampling, and a v4 writer/CLI flow that archives each run under .lab/spector/<run-id>/.

Opt-in by default. Production captures with full pixel dumps require explicit operator opt-in plus an attestation log entry. Per the IMPLEMENTATION-PLAN: "Tier S2 buffer-content recording (production). Opt-in + policy/attestation remains v3.1 for shipping mainline. It is an explicit POC track (bounded samples / raw prefix in JSON) so handoff data matches proposal depth; legal review before prod defaults."

Graphics-only. This package is for WebGL donors. Non-graphical adapters (browser extensions, plain web apps) do not consume it.

Status

Greenfield POC — Some Spector-shaped artifact code exists today fused into ushman/src/core/parity/index.ts and adapters/scene-heavy/gpu-diagnostics.ts; this package extracts and consolidates that surface. Runtime: browser (preboot WebGL hook) + Node/Bun (writer / orchestrator / CLI).

What this package is NOT

  • Not a parity verdict producer. The orchestrator's parity/ consumes our captures.
  • Not a full pixel-dump tool. We capture bounded prefixes of GPU buffers (configurable, default off).
  • Not a Three.js library.

Install

npm i ushman-spector
bun add ushman-spector

puppeteer-core is an optional peer dependency for the bundled CLI. The library surface only needs a page-like object that supports evaluate(...) and, for pre-navigation install, evaluateOnNewDocument(...) or addInitScript(...).

Test

bun test
bun run test:e2e

bun test includes the browser E2E file as well. The browser-backed tests require a real local Chrome-family browser through puppeteer-core, auto-detect /Applications/Google Chrome.app on macOS, and fall back to PUPPETEER_EXECUTABLE_PATH=/path/to/chrome when Chrome lives elsewhere.

Public API

// Browser-side (page-script source)
export const SPECTOR_HOOK_SOURCE: string;
export const installSpectorHook: (page: PageLike) => Promise<void>;
export type PageLike = {
    evaluate: <TResult>(pageFunction: ((...args: unknown[]) => TResult | Promise<TResult>) | string, ...args: unknown[]) => Promise<TResult>;
};

// Node-side capture orchestration
export type SpectorCaptureOptions = {
    readonly captureBuffers?: boolean;        // default false; opt-in
    readonly bufferPrefixBytes?: number;      // default 256, hard max 4096
    readonly captureShaders?: boolean;        // default true
    readonly attestationLogPath?: string;     // required if captureBuffers === true
    readonly bundleHash?: string;             // required if captureBuffers === true, format: sha256:<64-lowercase-hex>
    readonly operatorName?: string;
    readonly maxBuffers?: number;             // default 64
    readonly maxDrawCalls?: number;           // default 256, hard max 1024
    readonly maxTotalJsonBytes?: number;      // default 262144
};

export type SpectorCapture = {
    readonly createdAt: string;
    readonly schemaName: 'ushman.spector-capture';
    readonly schemaVersion: '1.0.0';
    readonly ushmanVersion: string;
    readonly state: string;
    readonly durationMs: number;
    readonly frameCount: number;
    readonly glState: unknown;
    readonly commands: readonly { readonly name: string; readonly argsPreview?: readonly string[] }[];
    readonly shaders: readonly { readonly id: string; readonly type: 'vertex' | 'fragment'; readonly source: string }[];
    readonly buffers: readonly unknown[];
    readonly textures: readonly unknown[];
};

export type BufferSamplesDocument = {
    readonly createdAt: string;
    readonly schemaName: 'ushman.buffer-samples';
    readonly schemaVersion: '1.0.0';
    readonly ushmanVersion: string;
    readonly state: string;
    readonly captureBuffers: true;
    readonly bufferPrefixBytes: number;
    readonly buffers: readonly {
        readonly id: string;
        readonly target: string;
        readonly usage?: string;
        readonly byteLength: number;
        readonly samplePrefix?: string;
        readonly samplePrefixEncoding?: 'base64';
        readonly sampleByteCount: number;
        readonly sha256Prefix?: string;
        readonly sha256Full?: string;
        readonly notes?: readonly string[];
    }[];
    readonly truncation: {
        readonly truncated: boolean;
        readonly reasons: readonly string[];
        readonly reasonDetails: readonly string[];
        readonly droppedBuffers: number;
    };
};

// Buffer sample semantics:
// - `sampleByteCount` is the actual captured prefix length (bounded by `bufferPrefixBytes`)
// - `byteLength` is the full GPU buffer size observed on the page

export type SpectorCaptureArtifacts = {
    readonly capture: SpectorCapture;
    readonly bufferSamples: BufferSamplesDocument | null;
};

export const runSpectorCapture: (opts: {
    readonly createdAt?: string;
    readonly page: PageLike;
    readonly state: string;
    readonly options?: SpectorCaptureOptions;
}) => Promise<SpectorCaptureArtifacts>;

export type LedgerPhase =
    | 'capture'
    | 'intake'
    | 'seed'
    | 'vendor-extract'
    | 'cleanup'
    | 'parity'
    | 'characterize'
    | 'equiv'
    | 'analyze'
    | 'recover'
    | 'ship'
    | 'migration';

export type LedgerValidator =
    | 'spector'
    | 'parity'
    | 'characterize'
    | 'equiv'
    | 'verify'
    | 'doctor';

export const writeSpectorCapture: (opts: {
    readonly artifacts: SpectorCaptureArtifacts;
    readonly outputPath: string;
}) => Promise<void>;

export const assertV4Workspace: (workspaceRoot: string) => Promise<void>;
export const resolveSpectorRunPaths: (opts: {
    readonly workspaceRoot: string;
    readonly state: string;
    readonly timestamp: string;
    readonly runId?: string;
}) => Promise<{
    readonly runId: string;
    readonly runDir: string;
    readonly capturePath: string;
    readonly bufferSamplesPath: string;
    readonly manifestPath: string;
    readonly ledgerPhase: string;
}>;

export const writeWorkspaceSpectorCapture: (opts: {
    readonly artifacts: SpectorCaptureArtifacts;
    readonly workspaceRoot: string;
    readonly state: string;
    readonly timestamp?: string;
    readonly runId?: string;
    readonly phase?: LedgerPhase;
    readonly validator?: LedgerValidator;
    readonly toolInvocation?:
        | false
        | {
              readonly summary?: string;
              readonly cwd?: string;
              readonly args?: readonly string[];
              readonly exitCode?: number;
          };
}) => Promise<{
    readonly runId: string;
    readonly runDir: string;
    readonly capturePath: string;
    readonly manifestPath: string;
    readonly ledgerEntryIds: readonly string[];
    readonly phase: LedgerPhase;
}>;

CLI

ushman-spector capture <workspace> \
  --state=<state-id> \
  --url=<page-url> \
  [--capture-buffers] \
  [--buffer-prefix-bytes=256] \
  [--no-shaders] \
  [--attestation-log=<path>] \
  [--bundle-hash=<sha256:...>] \
  [--max-buffers=<count>] \
  [--max-draw-calls=<count>] \
  [--max-total-json-bytes=<bytes>] \
  [--operator-name=<name>] \
  [--browser-ws-endpoint=<ws-url> | --executable-path=/path/to/chrome] \
  [--wait-until=load|domcontentloaded|networkidle0|networkidle2] \
  [--timeout-ms=<milliseconds>]

If --capture-buffers is enabled, the CLI refuses to run unless both --attestation-log and --bundle-hash are supplied. USHMAN_BUFFER_CAPTURE=1 enables --capture-buffers by default for the CLI. If you pass an out-of-range integer such as --buffer-prefix-bytes=8192, the CLI fails fast instead of silently clamping it.

The CLI is v4-only. It requires <workspace>/.lab/lab.json with schemaVersion ushman-lab/v4.0 and refuses legacy v3 workspaces with a migration hint.

When the CLI launches its own browser, it currently uses the fixed Chromium flags --enable-webgl, --ignore-gpu-blocklist, and --use-angle=swiftshader. Those flags are not configurable yet.

Artifact flow

  1. Install the hook before navigation with installSpectorHook(page) so buffer uploads are observed from the start of app boot.
  2. Run runSpectorCapture(...) after the page settles.
  3. Persist the results with writeSpectorCapture(...) or archive a full v4 run with writeWorkspaceSpectorCapture(...).

runSpectorCapture(...) also attempts a best-effort install on the current page, but that fallback cannot reconstruct WebGL calls that already happened before the hook was present. For reliable buffer evidence, preinstall the hook before the app requests a context.

For v4 workspaces, the CLI and writeWorkspaceSpectorCapture(...) write:

  • .lab/spector/<run-id>/spector-capture.json — legacy spector-shaped telemetry (ushman.spector-capture)
  • .lab/spector/<run-id>/buffer-samples.json — bounded GPU buffer evidence with explicit truncation metadata
  • .lab/spector/<run-id>/manifest.json — run metadata plus the emitted ledger entry ids
  • .lab/ledger/<current-phase>/tool-invocation and validator-result entries for the capture

writeWorkspaceSpectorCapture(...) emits:

  • a default archive-scoped tool-invocation entry for library callers
  • a validator-result entry whose validator defaults to the active phase when it maps cleanly (parity, characterize, equiv) and otherwise falls back to spector

The CLI emits its own start/finish tool-invocation entries and disables the library default so the ledger does not duplicate those boundaries.

Pass toolInvocation: false when the caller already records its own tool-invocation boundaries around the archive step. The bundled CLI does this so the ledger has one start/finish pair instead of a nested extra archive entry.

writeSpectorCapture(...) treats the run directory as the atomic unit. Fresh runs are written into a sibling staging directory and promoted with a single directory rename. Overwrites rename the previous run directory into a backup, promote the staged directory, and restore the backup if promotion fails. If interrupted temp directories remain on disk, the next write recovers the newest backup or cleans stale staging directories before committing.

writeSpectorCapture(...) assumes path.dirname(outputPath) is a dedicated run directory. Existing directories may only contain capture artifacts managed by this package (spector-capture.json, buffer-samples.json, and an optional manifest.json). Shared directories with unrelated files are rejected instead of being renamed as a unit.

Ledger Shape

tool-invocation entries include:

  • kind, phase, summary, ts, id, prevEntryId
  • optional cwd, args, exitCode

validator-result entries include:

  • kind, phase, summary, ts, id, prevEntryId
  • validator
  • optional metrics, resultPath
  • verdict

Verdict semantics:

  • green means the capture archive was written with complete bounded evidence for the configured caps.
  • yellow means the archive succeeded but buffer evidence was truncated by maxBuffers or maxTotalJsonBytes.
  • red is reserved for failed validator-style writes and is not emitted by the happy-path archive flow.

The ledger manifest at .lab/ledger/manifest.json is a bounded hot index with schemaVersion ushman-ledger-manifest/v2. It stores only entryCount, perPhaseCounts, and perPhaseLatest, so common read/write cost stays constant as historical entry files accumulate. Older v1 manifests that still contain entryLocations are migrated in place on the next ledger write. Readers that depended on entryLocations should enumerate .lab/ledger/<phase>/ directly instead of expecting a full global path map in the hot manifest.

The hot manifest is a cache, not the source of truth. The append-only entry files under .lab/ledger/<phase>/ remain authoritative. If a process crashes after writing an entry file but before updating the hot manifest, consumers should rescan the phase directory rather than assuming the manifest is complete.

Capture Notes

  • samplePrefixEncoding is currently always Base64.
  • frameCount is snapshot-oriented. The package captures one evidence point, not a full frame timeline.
  • glState is a hook summary object, not a full Spector.js state export.
  • If you need operator-facing guidance on what is recorded, limits, and privacy implications, see docs/operator-guide.md.

V4 Usage

Boot the workspace from the Vite root, not a legacy stages/03-clean/ subtree:

cd /path/to/workspace
bunx vite build
bunx vite preview --host 127.0.0.1 --port 4173

Then capture against the preview URL:

ushman-spector capture /path/to/workspace \
  --state=0-lobby \
  --url=http://127.0.0.1:4173 \
  --bundle-hash=sha256:<64-hex>

Cleanup Policy

Run archives under .lab/spector/ are retained. This package does not prune old runs automatically. Cleanup is expected to be orchestrator-managed or operator-managed so audit trails are not removed implicitly.

Why this exists separate from ushman

  1. Different release cadence. POC-only, opt-in, with explicit legal review notes.
  2. Different audience. Operators evaluating shader-level parity vs operators running normal characterization.
  3. Different runtime. Needs a browser context with WebGL + the preboot hook attached.
  4. Different threat model. Buffer captures may contain user-facing pixels — that's why we ship attestation hooks.

Where this fits in the family

| | | |---|---| | Depends on | Browser page adapter; puppeteer-core only if you use the bundled CLI | | Consumed by | ushman operator/parity flows, which read archived runs under .lab/spector/<run-id>/ | | Default safety | captureBuffers: false. Production captures require operator opt-in + attestation log |